# SPDX-License-Identifier: LGPL-3.0-or-later

"""
Blueprint definitions for the Maeser application.

This module sets up the **Flask blueprint** and associated routes for the Maeser
application. It includes route handlers for chat interfaces, user management,
feedback, and training functionalities.
"""

from flask import Blueprint, Flask
from flask_login import login_required, current_user, LoginManager
import os
from datetime import datetime
import threading
import time

from maeser.chat.chat_session_manager import ChatSessionManager
from maeser.controllers.common.decorators import admin_required, rate_limited
from maeser.user_manager import UserManager
from maeser.controllers import (
    chat_api,
    chat_interface,
    chat_logs_overview,
    display_chat_log,
    feedback_api,
    feedback_form_get,
    feedback_form_post,
    login_api,
    logout,
    new_session_api,
    training,
    training_post,
    conversation_history_api,
    remaining_requests_api,
    manage_users_view,
    user_management_api,
)


class AppManager:
    """
    Manages the Maeser App and its configurations.

    Takes in configuration for the Maeser application and applies the configuration when
    adding the Flask blueprint.

    Running **add_flask_blueprint()** after initializing an AppManager object will return
    a Flask object with the blueprint added.

    Args:
        app (Flask): The Flask application instance.
        app_name (str | None, optional): The name of the application.
        flask_secret_key (str): The secret key for the Flask application.
        chat_session_manager (ChatSessionManager): The chat session manager instance.
        user_manager (UserManager | None, optional): The user manager instance. Defaults to None.
        main_logo_login (str | None, optional): URL or path to the login logo. Defaults to None.
        main_logo_chat (str | None, optional): URL or path to the chat logo. Defaults to None.
        chat_head (str | None, optional): URL or path to the chat header image. Defaults to None.
        favicon (str | None, optional): URL or path to the favicon. Defaults to None.
    """

    def __init__(
        self,
        # app and app name
        app: Flask,
        app_name: str,
        # flask secret
        flask_secret_key: str,
        # managers
        chat_session_manager: ChatSessionManager,
        user_manager: UserManager | None = None,
        # images
        main_logo_login: str | None = None,
        main_logo_chat: str | None = None,
        chat_head: str | None = None,
        favicon: str | None = None,
    ):
        self.app = app
        self.app_name = app_name

        self.flask_secret_key = flask_secret_key

        self.chat_session_manager = chat_session_manager
        self.user_manager = user_manager

        self.main_logo_login = main_logo_login
        self.main_logo_chat = main_logo_chat
        self.chat_head = chat_head
        self.favicon = favicon

        # NOTE: The commented-out variables below are unimplemented.
        # self.login_text = login_text # TODO: Not implemented
        # self.changelog = changelog # TODO: Not implemented
        # self.chat_greeting = chat_greeting # TODO: Not implemented
        # self.branch_response = branch_response # TODO: Not implemented

        # self.animation = animation # TODO: Not implemented

        # self.primary_color = primary_color # TODO: Not implemented
        # self.secondary_color = secondary_color # TODO: Not implemented
        # self.button_color = button_color # TODO: Not implemented
        # self.button_color_active = button_color_active # TODO: Not implemented
        # self.button_color_inactive = button_color_inactive # TODO: Not implemented
        # self.fafa_button = fafa_button # TODO: Not implemented
        # self.logout_button = logout_button # TODO: Not implemented
        # self.new_chat_button = new_chat_button # TODO: Not implemented
        # self.help_train_button = help_train_button # TODO: Not implemented

        self.current_dir = os.path.dirname(os.path.abspath(__file__ + "/."))

    # The following functions with no code are work in progress and will be added soon

    # def template_styles_css(self):
    #     """
    #     Template the styles.css file using Jinja2 templating
    #     """

    # def template_chat_interface(self):
    #     """
    #     Template the chat_interface.html file using Jinja2 templating
    #     """

    # def template_login(self):
    #     """
    #     Template the login.html file using Jinja2 templating
    #     """

    # def template_training(self):
    #     """
    #     Template the training.html file using Jinja2 templating
    #     """

    # def template_feedback(self):
    #     """
    #     Template the feedback_form.html file using Jinja2 templating
    #     """

    def add_flask_blueprint(self) -> Flask:
        """
        Adds the Maeser blueprint to the Flask application.

        This function does not mutate the AppManager instance itself but rather returns
        a new Flask instance with the blueprint added.

        Returns:
            Flask: The Flask application instance with the blueprint registered.
        """
        maeser_blueprint = Blueprint(
            "maeser",
            __name__,
            template_folder=os.path.join(self.current_dir, "data/templates"),
            static_folder=os.path.join(self.current_dir, "data/static"),
            static_url_path="/maeser/static",
        )

        @maeser_blueprint.app_template_filter("datetimeformat")
        def datetimeformat(value, format="%Y-%m-%d %H:%M:%S"):
            """Format a timestamp as a datetime string."""
            return datetime.fromtimestamp(value).strftime(format)

        if self.user_manager:

            def refresh_requests():
                """Refresh user requests at regular intervals."""
                while True:
                    time.sleep(self.user_manager.rate_limit_interval)
                    self.user_manager.refresh_requests()

            threading.Thread(target=refresh_requests, daemon=True).start()

            self.app.secret_key = self.flask_secret_key
            login_manager = LoginManager(self.app)
            login_manager.init_app(self.app)
            login_manager.login_view = "maeser.login"  # type: ignore
            login_manager.session_protection = "strong"

            @login_manager.user_loader
            def load_user(user_full_id: str):
                """Load a user by their ID."""
                auth_method, user_id = user_full_id.split(".", 1)
                return self.user_manager.get_user(auth_method, user_id)

            @maeser_blueprint.route("/")
            @login_required
            def chat_interface_route():
                """Route for the chat interface."""
                return chat_interface.controller(
                    self.chat_session_manager,
                    self.user_manager.max_requests,
                    self.user_manager.rate_limit_interval,
                    current_user,
                    main_logo_login=self.main_logo_login,
                    main_logo_chat=self.main_logo_chat,
                    favicon=self.favicon,
                    chat_head=self.chat_head,
                    app_name=self.app_name,
                )

            @maeser_blueprint.route("/login", methods=["GET", "POST"])
            def login():
                """Route for the login interface."""
                return login_api.login_controller(
                    self.user_manager,
                    main_logo_login=self.main_logo_login,
                    main_logo_chat=self.main_logo_chat,
                    favicon=self.favicon,
                    app_name=self.app_name,
                )

            @maeser_blueprint.route("/login/github", methods=["GET"])
            def github_authorize():
                """Route for GitHub authorization."""
                return login_api.github_authorize_controller(
                    current_user, self.user_manager.authenticators["github"]
                )

            @maeser_blueprint.route("/login/github_callback")
            def github_auth_callback():
                """Route for GitHub authorization callback."""
                return login_api.github_auth_callback_controller(
                    current_user,
                    self.user_manager,
                )

            @maeser_blueprint.route("/logout")
            @login_required
            def logout_route():
                """Route for logging out."""
                return logout.controller()

            @maeser_blueprint.route("/users")
            @login_required
            @admin_required(current_user)
            def manage_users():
                """Route for managing users."""
                return manage_users_view.controller(
                    self.user_manager,
                    main_logo_chat=self.main_logo_chat,
                    favicon=self.favicon,
                    app_name=self.app_name,
                )

            @maeser_blueprint.route("/users/api", methods=["POST"])
            @login_required
            @admin_required(current_user)
            def manage_users_api():
                """API route for managing users."""
                return user_management_api.controller(self.user_manager)

            @maeser_blueprint.route("/req_session", methods=["POST"])
            @login_required
            def sess_handler():
                """Route for handling session requests."""
                return new_session_api.controller(self.chat_session_manager, True)

            @maeser_blueprint.route("/msg/<chat_session>", methods=["POST"])
            @login_required
            @rate_limited(self.user_manager, current_user)
            def msg_api(chat_session):
                """API route for handling chat messages."""
                return chat_api.controller(self.chat_session_manager, chat_session)

            @maeser_blueprint.route("/feedback", methods=["POST"])
            @login_required
            def feedback():
                """Route for submitting feedback."""
                return feedback_api.controller(self.chat_session_manager)

            @self.app.route("/get_requests_remaining", methods=["GET"])
            @login_required
            def get_requests_remaining():
                """Route for getting the remaining requests."""
                return remaining_requests_api.controller(
                    self.user_manager, current_user
                )

        else:

            @maeser_blueprint.route("/")
            def chat_interface_route():
                """Route for the chat interface."""
                return chat_interface.controller(
                    self.chat_session_manager,
                    main_logo_login=self.main_logo_login,
                    main_logo_chat=self.main_logo_chat,
                    favicon=self.favicon,
                    chat_head=self.chat_head,
                    app_name=self.app_name,
                )

            @maeser_blueprint.route("/req_session", methods=["POST"])
            def sess_handler():
                """Route for handling session requests."""
                return new_session_api.controller(self.chat_session_manager)

            @maeser_blueprint.route("/msg/<chat_session>", methods=["POST"])
            def msg_api(chat_session):
                """API route for handling chat messages."""
                return chat_api.controller(self.chat_session_manager, chat_session)

            @maeser_blueprint.route("/feedback", methods=["POST"])
            def feedback():
                """Route for submitting feedback."""
                return feedback_api.controller(self.chat_session_manager)

        if self.chat_session_manager.chat_logs_manager:

            @maeser_blueprint.route("/train")
            @login_required if self.user_manager else lambda x: x
            def train():
                """Route for training."""
                return training.controller(
                    main_logo_chat=self.main_logo_chat,
                    favicon=self.favicon,
                    app_name=self.app_name,
                )

            @maeser_blueprint.route("/submit_train", methods=["POST"])
            @login_required if self.user_manager else lambda x: x
            def submit_train():
                """Route for submitting training data."""
                return training_post.controller(self.chat_session_manager)

            @maeser_blueprint.route("/feedback_form")
            def feedback_form():
                """Route for getting the feedback form."""
                return feedback_form_get.controller(
                    main_logo_chat=self.main_logo_chat,
                    favicon=self.favicon,
                    app_name=self.app_name,
                )

            @maeser_blueprint.route("/submit_feedback", methods=["POST"])
            def submit_feedback():
                """Route for submitting feedback."""
                return feedback_form_post.controller(self.chat_session_manager)

            @maeser_blueprint.route("/logs", methods=["GET"])
            @login_required if self.user_manager else lambda x: x
            @admin_required(current_user) if self.user_manager else lambda x: x
            def logs():
                """Route for viewing chat logs."""
                return chat_logs_overview.controller(
                    self.chat_session_manager,
                    main_logo_chat=self.main_logo_chat,
                    favicon=self.favicon,
                    app_name=self.app_name,
                )

            @maeser_blueprint.route("/logs/<branch>/<filename>")
            @login_required if self.user_manager else lambda x: x
            @admin_required(current_user) if self.user_manager else lambda x: x
            def display_log(branch, filename):
                """Route for displaying a specific chat log."""
                return display_chat_log.controller(
                    self.chat_session_manager, branch, filename, app_name=self.app_name
                )

            @maeser_blueprint.route("/conversation_history", methods=["POST"])
            @login_required if self.user_manager else lambda x: x
            def conversation_history():
                """Route for getting conversation history."""
                return conversation_history_api.controller(self.chat_session_manager)

        self.app.register_blueprint(maeser_blueprint)
        return self.app
