import sys
import os

from flask_migrate import Migrate
from flask import Flask, send_from_directory, request, redirect
from flask_cors import CORS
from flask_compress import Compress
from utils import DebugUtils

from database.db import lookup_db_for_binding, init_all_dbs_with_app, init_binding_to_db_for_bindings
from config.config import DEBUG_MODE, get_app_config, get_static_url

# The environment variable whose value is the PORT this application will listen on
PORT_ENV_VAR_KEY = "PORT"

# Create and configure our Flask app
app = Flask(__name__, static_url_path=get_static_url())
app.url_map.strict_slashes = False

# Enable Gzip Compression on all responses
Compress(app)

# Enable CORS for all routes
CORS(app)
# NOTE: if there is an error in your route, e.g.: printing a variable that doesn't
#       exist, then you will get a CORS related error which has nothing to do with CORS
# Source: https://stackoverflow.com/questions/25594893/how-to-enable-cors-in-flask

# Load config
flask_app_config = get_app_config()
app.config.from_object(flask_app_config)
init_binding_to_db_for_bindings(flask_app_config.SQLALCHEMY_BINDS.keys())

# Initialise all databases
init_all_dbs_with_app(app)

# The import below registers all models to their respective database
from models import *

# Register all blueprints/routes
from blueprints import home_blueprint, persons_blueprint
# from blueprints.auth import auth_blueprint
app.register_blueprint(home_blueprint)
app.register_blueprint(persons_blueprint)
# app.register_blueprint(auth_blueprint)

# ===================== MIGRATION SETUP =====================
APP_CONFIG_BINDINGS_DICT_KEY = "SQLALCHEMY_BINDS"
# The below environment variables should be set by the script that initializes the migration
# This environment variable is used by "Flask_Migrate" and should not be changed
FLASK_MIGRATE_FLASK_APP_ENV_VAR_KEY = "FLASK_APP"
# This environment variable is a hack to specify the DB we want to migrate, since we have multiple dbs
#       and only require/want to migrate one at a time.
FLASK_DB_BINDING_ENV_VAR_KEY = "FLASK_DB_BINDING_TO_MIGRATE"
MIGRATION_MODE = os.environ.get(FLASK_MIGRATE_FLASK_APP_ENV_VAR_KEY, None) is not None
if MIGRATION_MODE:
    binding_name = os.environ.get(FLASK_DB_BINDING_ENV_VAR_KEY, None)
    if not binding_name:
        print("Invalid binding name specified:", binding_name)
        exit(-1)
    with app.app_context():
        print("Initializing migration")
        # Source: https://flask-migrate.readthedocs.io/en/latest/
        # After the extension is initialized, a "db" group will be added to the command-line options with several
        # sub-commands
        bindings_dict = app.config[APP_CONFIG_BINDINGS_DICT_KEY]
        # Set the main db
        app.config["SQLALCHEMY_DATABASE_URI"] = bindings_dict[binding_name]
        # engine = db.get_engine(flask_app, bind_name)
        migrate = Migrate()
        # "db" arg below needs to be the database you want the above main db to become/migrate to
        db_to_migrate = lookup_db_for_binding(binding_name)
        migrate.init_app(app, db_to_migrate)
# ===================== End MIGRATION SETUP. =====================
# Get arguments to this program call
if not MIGRATION_MODE:
    # The Flask Migrate CLI passes arguments from its own CLI, and we don't want to handle those.
    from cli_handler import handle_cli
    cli_args = sys.argv[1:]
    if len(cli_args) > 0:
        if DEBUG_MODE:
            with app.app_context():
                handle_cli(app, lookup_db_for_binding, cli_args)
            exit(1)
        else:
            DebugUtils.print_warning("Production application should not be run with CLI arguments, "
                                     "if you intended to run the CLI please run in development mode.")
            exit(-1)


# Serve all static assets for the frontend
@app.route('/static/<path:path>')
def serve_static_files(path):
    return send_from_directory('static', path)


# Force secure connection
@app.before_request
def before_request():
    if not DEBUG_MODE:
        scheme = request.headers.get('X-Forwarded-Proto')
        if scheme and scheme == 'http' and request.url.startswith('http://'):
            url = request.url.replace('http://', 'https://', 1)
            code = 301
            return redirect(url, code=code)

# Hook any custom Jinja templating functions
from config import CUSTOM_TEMPLATE_FUNCTIONS
app.jinja_env.globals.update(CUSTOM_TEMPLATE_FUNCTIONS)

if __name__ == '__main__':
    host = '127.0.0.1'
    port = int(os.environ.get(PORT_ENV_VAR_KEY, 5000))
    if DEBUG_MODE:
        mode_desc = "debug"
    else:
        mode_desc = "production"
    # NOTE: it will say "Environment: production", but if DEBUG_MODE=True, then ignore that.
    DebugUtils.print_warning(f"Running in {mode_desc} mode")
    app.run(debug=DEBUG_MODE, host=host, port=port)