import asyncio
from logging import Logger
import logging.config
import os
from typing import Any, cast

import yaml

from phederation.actions.worker import main_worker
from phederation.integration.base import BaseIntegration
from phederation.integration.integration import FastAPIIntegration
from phederation.utils.exceptions import SettingsError
from phederation.utils.logging import configure_logger
from phederation.utils.settings import PhedSettings


class Instance:
    integration: BaseIntegration | None = None
    main_instance_settings: PhedSettings | None = None

    def __init__(self, settings: PhedSettings):
        self.verify_settings(settings)
        self.settings: PhedSettings = settings.model_copy(deep=True)
        self.ssl_context: tuple[str, str] = (
            "PEM pub path not set",
            "PEM private path not set",
        )
        if self.settings.federation.admin_mode:
            self.configure_admin_settings()

        self.logger: Logger = configure_logger(__name__, prefix=self.settings.federation.logging_prefix)

        if self.settings.federation.admin_mode:
            self.logger.warning("Instance running in admin mode.")

    def verify_settings(self, settings: PhedSettings):
        if settings.domain.port == settings.domain.admin_port:
            raise SettingsError("The port of the main instance is set to be the same as the port in admin mode. This is now allowed")

    def configure_admin_settings(self):
        """Reconfigures the settings object to be used in admin mode.

        New configuration
         - Add an "ADMIN" tag ad the end of the logger prefix to log into a different file
         - Do not clear the database on start
         - Do not log any statistics / middleware
         - Do not use temporal workers for delivery

        The domain.url and domain.port are left the same as the main instance, to simulate
        that the admin instance is "the same" as the main one. This allows to easily resolve
        internal collections from the main instance.
        To be able to check the settings of the main instance, they are saved in "main_instance_settings"
        before changing them to the admin settings.
        """
        self.main_instance_settings = self.settings.model_copy(deep=True)
        self.settings.storage.database.clear_on_initialize = False
        self.settings.statistics.enabled = False
        self.settings.worker.enabled = False
        self.settings.federation.logging_prefix = f"{self.settings.federation.logging_prefix}-ADMIN"

    async def initialize(self):
        self.logger.debug(f"Initalizing instance.")

        self.integration = FastAPIIntegration(self.settings)
        await self.integration.initialize()

        self.logger.debug(f"Integration created.")

    async def run(self):
        await self.initialize()
        self.logger.debug(f"Instance initialized. Starting run.")
        if not self.integration:
            return None
        return self.integration.app

    def close(self):
        pass


async def main(env_file: str | None = None, settings_file: str | None = None, logging_file: str | None = None, admin_mode: str = "False"):

    if not env_file:
        env_file = ".env"
    if not settings_file:
        settings_file = "phederation.yaml"

    settings = PhedSettings(yaml_file=settings_file, env_file=env_file)

    if logging_file is None:
        with open(settings_file) as f:
            settings_config = cast(dict[str, Any], yaml.safe_load(f))
            logging_config = cast(dict[str, Any], settings_config["logging"])
    else:
        with open(logging_file) as f:
            logging_config = cast(dict[str, Any], yaml.safe_load(f))
    logging.config.dictConfig(config=logging_config)

    if admin_mode.lower() == "true":
        settings.federation.admin_mode = True

    instance = Instance(settings=settings)
    instance.logger.info(f"Using settings file '{os.path.basename(settings_file)}'")

    # for a WSGI application like gunicorn, we need to return the app, not run it
    await instance.initialize()
    if not instance.integration:
        return None
    return instance.integration.app


# Gunicorn entry point generator for phederation instance
def app(*args_app, **kwargs_app):  # pyright: ignore[reportUnknownParameterType, reportMissingParameterType, reportUnusedParameter]
    # Gunicorn CLI args are useless.
    # https://stackoverflow.com/questions/8495367/
    #
    # Start the application in modified environment.
    # https://stackoverflow.com/questions/18668947/
    #

    return asyncio.run(main(**kwargs_app))  # pyright: ignore[reportUnknownArgumentType]


# Gunicorn entry point generator for temporalio worker
def app_worker(*args_worker, **kwargs_worker):  # pyright: ignore[reportUnknownParameterType, reportMissingParameterType, reportUnusedParameter]

    return asyncio.run(main_worker(**kwargs_worker))  # pyright: ignore[reportUnknownArgumentType]


if __name__ == "__main__":
    _ = asyncio.run(main())
