"Main Haldis script"
import logging
from logging.handlers import TimedRotatingFileHandler
import typing
from datetime import datetime

from airbrake import Airbrake, AirbrakeHandler
from flask import Flask, render_template
from flask_bootstrap import Bootstrap, StaticCDN
from flask_debugtoolbar import DebugToolbarExtension
from flask_login import LoginManager
from flask_migrate import Migrate, MigrateCommand
from flask_oauthlib.client import OAuth, OAuthException
from flask_script import Manager, Server

from admin import init_admin
from login import init_login
from models import db
from models.anonymous_user import AnonymouseUser
from utils import euro_string
from zeus import init_oauth


def create_app() -> Manager:
    "Create the Haldis application"
    app = Flask(__name__)

    # 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)

    # TODO do we need to return and then run the manager?
    return app_manager


def register_plugins(app: Flask) -> Manager:
    "Register all the plugins to Haldis"
    # pylint: disable=W0612
    # Register Airbrake and enable the logrotation
    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)

        airbrakelogger = logging.getLogger("airbrake")

        # Airbrake
        airbrake = Airbrake(project_id=app.config["AIRBRAKE_ID"],
                            api_key=app.config["AIRBRAKE_KEY"])
        # ugly hack to make this work for out errbit
        airbrake._api_url = "http://errbit.awesomepeople.tv/api/v3/projects/{}/notices".format(  # pylint: disable=W0212
            airbrake.project_id
        )

        airbrakelogger.addHandler(AirbrakeHandler(airbrake=airbrake))
        app.logger.addHandler(AirbrakeHandler(airbrake=airbrake))

    # 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))
    # Add admin interface
    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 views.order import order_bp
    from views.general import general_bp
    from views.stats import stats_blueprint
    from views.debug import debug_bp
    from login import auth_bp
    from zeus import oauth_bp

    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="/")
    application.register_blueprint(oauth_bp, url_prefix="/")

    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) -> str:
        "A function which returns time until the order is done"
        delta = value - datetime.now()
        if delta.total_seconds() < 0 and only_positive:
            return "closed"
        hours, remainder = divmod(delta.seconds, 3600)
        minutes, seconds = divmod(remainder, 60)
        time = "%02d:%02d:%02d" % (hours, minutes, seconds)
        if show_text:
            return f"{time} left"
        return time

    @app.template_filter("year")
    def current_year(value: typing.Any) -> str:  # pylint: disable=W0613
        "A function which returns the current year"
        return str(datetime.now().year)

    @app.template_filter("euro")
    def euro(value: int) -> str:
        "A function which converts a value to its euro_string"
        return euro_string(value)


# For usage when you directly call the script with python
if __name__ == "__main__":
    manager = create_app()
    manager.run()
