#!/usr/bin/env python3

"""The core app entrypoint."""

from datetime import datetime
import logging
from pathlib import Path
import tomllib

import cherrypy
import click
from flask import Flask, request
from tentacles.blueprints import (
    admin_ui,
    api,
    file_ui,
    job_ui,
    user_ui,
)
from tentacles.db import Db
from tentacles.globals import _ctx, Ctx, ctx
from tentacles.workers import *
from tentacles.workers import assign_jobs, Worker


def db_factory(app):
    store = Db(app.config.get("db", {}).get("uri"))
    store.connect()
    return store


def custom_ctx(app, wsgi_app):
    def helper(environ, start_response):
        store = db_factory(app)
        token = _ctx.set(Ctx(store))
        try:
            return wsgi_app(environ, start_response)
        finally:
            store.close()
            _ctx.reset(token)

    return helper


def create_j2_request_global(app):
    app.jinja_env.globals["ctx"] = ctx
    app.jinja_env.globals["request"] = request
    app.jinja_env.globals["datetime"] = datetime


def user_session():
    if (
        (
            (session_id := request.cookies.get("sid", ""))
            and (row := ctx.db.try_key(kid=session_id))
        )
        or (
            request.authorization
            and request.authorization.token
            and (row := ctx.db.try_key(kid=request.authorization.token))
        )
        or (
            (api_key := request.headers.get("x-api-key"))
            and (row := ctx.db.try_key(kid=api_key))
        )
    ):
        ctx.sid = row.id
        ctx.uid = row.user_id
        user = ctx.db.fetch_user(row.user_id)
        ctx.gid = user.group_id
        ctx.username = user.name
        ctx.is_admin = user.group_id == 0


def make_app():
    app = Flask(__name__)

    # Before first request
    create_j2_request_global(app)

    # Before request
    app.before_request(user_session)

    # Blueprints
    app.register_blueprint(admin_ui.BLUEPRINT)
    app.register_blueprint(api.BLUEPRINT)
    app.register_blueprint(file_ui.BLUEPRINT)
    app.register_blueprint(job_ui.BLUEPRINT)
    app.register_blueprint(user_ui.BLUEPRINT)

    # Shove our middleware in there
    app.wsgi_app = custom_ctx(app, app.wsgi_app)

    return app


@click.command()
@click.option("--hostname", "hostname", type=str, default="0.0.0.0")
@click.option("--port", "port", type=int, default=8080)
@click.option("--trace/--no-trace", "trace", default=False)
@click.option("--config", type=Path)
def serve(hostname: str, port: int, config: Path, trace: bool):
    logging.addLevelName(logging.DEBUG - 5, "TRACE")
    logging.TRACE = logging.DEBUG - 5
    logging.basicConfig(
        format="%(asctime)s %(threadName)s - %(name)s - %(levelname)s -  %(message)s",
        level=logging.INFO,
    )

    logging.getLogger("tentacles").setLevel(logging.DEBUG)
    if trace:
        logging.getLogger("tentacles.db").setLevel(logging.TRACE)

    app = make_app()

    if config:
        with open(config, "rb") as fp:
            app.config.update(tomllib.load(fp))

    print(app.config)

    # Run migrations once at startup rather than when connecting
    with closing(db_factory(app)) as db:
        db.migrate()

    # Configuring cherrypy is kinda awful
    cherrypy.server.unsubscribe()
    server = cherrypy._cpserver.Server()
    cherrypy.config.update(
        {
            "environment": "production",
            "engine.autoreload.on": False,
            "log.screen.on": True,
        }
    )
    cherrypy.tree.graft(app, "/")

    server.socket_host = hostname
    server.socket_port = port
    server.thread_pool = 16
    server.shutdown_timeout = 1
    server.subscribe()

    # Spawn the worker thread(s)
    Worker(cherrypy.engine, app, db_factory, poll_printers, frequency=5).start()
    Worker(cherrypy.engine, app, db_factory, analyze_files, frequency=5).start()
    Worker(cherrypy.engine, app, db_factory, assign_jobs, frequency=5).start()
    Worker(cherrypy.engine, app, db_factory, push_jobs, frequency=5).start()
    Worker(cherrypy.engine, app, db_factory, revoke_jobs, frequency=5).start()
    Worker(cherrypy.engine, app, db_factory, pull_jobs, frequency=5).start()
    Worker(cherrypy.engine, app, db_factory, send_emails, frequency=5).start()
    # Worker(cherrypy.engine, app, db_factory, debug_queue, frequency=5).start()

    # Run the server
    cherrypy.engine.start()
    cherrypy.engine.block()


if __name__ == "__main__":
    serve()
