#!/usr/bin/env python3

"""Main Haldis script"""

import logging
import sentry_sdk
import typing
from datetime import datetime
from logging.handlers import TimedRotatingFileHandler

from admin import init_admin
from config import Configuration
from flask import Flask, render_template, Response
from flask_bootstrap import Bootstrap, StaticCDN
from flask_debugtoolbar import DebugToolbarExtension
from flask_login import LoginManager
from flask_migrate import Migrate, MigrateCommand
from flask_script import Manager, Server
from markupsafe import Markup

from admin import init_admin
from auth.login import init_login
from auth.zeus import init_oauth
from config import Configuration
from models import db
from models.anonymous_user import AnonymouseUser
from sentry_sdk.integrations.flask import FlaskIntegration
from utils import euro_string, price_range_string, ignore_none


def register_plugins(app: Flask) -> Manager:
    """Register the plugins to the app"""
    # pylint: disable=W0612
    if not app.debug:
        timedFileHandler = TimedRotatingFileHandler(
            app.config["LOGFILE"], when="midnight", backupCount=100
        )
        timedFileHandler.setLevel(logging.DEBUG)

        loglogger = logging.getLogger("werkzeug")
        loglogger.setLevel(logging.DEBUG)
        loglogger.addHandler(timedFileHandler)
        app.logger.addHandler(timedFileHandler)

    # Initialize SQLAlchemy
    db.init_app(app)

    # Initialize Flask-Migrate
    migrate = Migrate(app, db)
    app_manager = Manager(app)
    app_manager.add_command("db", MigrateCommand)
    app_manager.add_command("runserver", Server(port=8000))
    init_admin(app, db)

    # Init login manager
    login_manager = LoginManager()
    login_manager.init_app(app)
    login_manager.anonymous_user = AnonymouseUser
    init_login(app)

    # Add oauth
    zeus = init_oauth(app)
    app.zeus = zeus

    # Load the bootstrap local cdn
    Bootstrap(app)
    app.config["BOOTSTRAP_SERVE_LOCAL"] = True

    # use our own bootstrap theme
    app.extensions["bootstrap"]["cdns"]["bootstrap"] = StaticCDN()

    # Load the flask debug toolbar
    toolbar = DebugToolbarExtension(app)

    # Make cookies more secure
    app.config.update(
        SESSION_COOKIE_HTTPONLY=True,
        SESSION_COOKIE_SAMESITE="Lax",
    )

    if not app.debug:
        app.config.update(SESSION_COOKIE_SECURE=True)

    return app_manager


def add_handlers(app: Flask) -> None:
    """Add handlers for 4xx error codes"""

    # pylint: disable=W0612,W0613
    @app.errorhandler(404)
    def handle404(e) -> typing.Tuple[str, int]:
        return render_template("errors/404.html"), 404

    @app.errorhandler(401)
    def handle401(e) -> typing.Tuple[str, int]:
        return render_template("errors/401.html"), 401


def add_routes(application: Flask) -> None:
    """Add all routes to Haldis"""
    # import views  # TODO convert to blueprint
    # import views.stats  # TODO convert to blueprint

    from auth.login import auth_bp
    from auth.microsoft import auth_microsoft_bp
    from auth.zeus import auth_zeus_bp
    from views.debug import debug_bp
    from views.general import general_bp
    from views.order import order_bp
    from views.stats import stats_blueprint

    application.register_blueprint(general_bp, url_prefix="/")
    application.register_blueprint(order_bp, url_prefix="/order")
    application.register_blueprint(stats_blueprint, url_prefix="/stats")
    application.register_blueprint(auth_bp, url_prefix="/")
    if Configuration.ENABLE_MICROSOFT_AUTH:
        application.register_blueprint(auth_microsoft_bp,
                                       url_prefix="/users/auth/microsoft_graph_auth")  # "/auth/microsoft")
    application.register_blueprint(auth_zeus_bp, url_prefix="/auth/zeus")

    if application.debug:
        application.register_blueprint(debug_bp, url_prefix="/debug")


def add_template_filters(app: Flask) -> None:
    """Add functions which can be used in the templates"""

    # pylint: disable=W0612
    @app.template_filter("countdown")
    def countdown(
        value, only_positive: bool = True, show_text: bool = True, reload: bool = True
    ) -> str:
        delta = int(value.timestamp() - datetime.now().timestamp())
        if delta < 0 and only_positive:
            text = "closed"
        else:
            carry, seconds = divmod(delta, 60)
            carry, minutes = divmod(carry, 60)
            days, hours = divmod(carry, 24)

            days_text = f"{days} days, " if days else ""

            appendix = " left" if show_text else ""
            text = f"{days_text}{hours:02d}:{minutes:02d}:{seconds:02d}{appendix}"

        reload_str = "yes" if reload else "no"

        return Markup(
            f"<span class='time' data-seconds='{delta}' data-reload='{reload_str}'>"
            + text
            + "</span>"
        )

    @app.template_filter("year")
    def current_year(_value: typing.Any) -> str:
        return str(datetime.now().year)

    app.template_filter("euro")(euro_string)
    app.template_filter("price_range")(price_range_string)
    app.template_filter("any")(any)
    app.template_filter("all")(all)
    app.template_filter("ignore_none")(ignore_none)


def create_app():
    """Initializer for the Flask app object"""
    app = Flask(__name__)

    @app.route('/robots.txt')
    def noindex():
        r = Response(response="User-Agent: *\nDisallow: /\n", status=200, mimetype="text/plain")
        r.headers["Content-Type"] = "text/plain; charset=utf-8"
        return r
    
    # Load the config file
    app.config.from_object("config.Configuration")

    app_manager = register_plugins(app)
    add_handlers(app)
    add_routes(app)
    add_template_filters(app)

    @app.context_processor
    def inject_config():
        return dict(configuration=Configuration)

    return app, app_manager


# For usage when you directly call the script with python
if __name__ == "__main__":
    if Configuration.SENTRY_DSN:
        sentry_sdk.init(
            dsn=Configuration.SENTRY_DSN,
            integrations=[FlaskIntegration()]
        )

    app, app_mgr = create_app()
    app_mgr.run()
