import asyncio

from aiohttp import web

from smb.common.multiruntime.lib.io import setup_filesystem

from . import api, api_providers, data_managers, domains, tasks
from .data_managers.system_op import SystemOpManager
from .db import DB

__all__ = ["Application"]

setup_filesystem("maps_adv/stat_controller/server/")


class Application:
    __slots__ = "_config", "_periodic_tasks", "_system_dm"

    _config: dict
    _periodic_tasks: list

    def __init__(self, config: dict):
        self._config = config

        self._periodic_tasks = []

    def setup(self, db: DB) -> web.Application:
        normalizer = self._setup_normalizer(db)
        charger = self._setup_charger(db)
        collector = self._setup_collector(db)

        self._system_dm = system_op = self._setup_system_op(db)

        _api = api.create(normalizer, charger, collector, system_op)

        _api.on_startup.append(self._start_tasks)
        _api.on_shutdown.append(self._stop_tasks)

        return _api

    def _setup_normalizer(self, db: DB) -> api_providers.Normalizer:
        dm = data_managers.normalizer.TaskManager(db)
        domain = domains.Normalizer(
            dm,
            self._config["TIME_LAG"],
            self._config["MIN_TIME_RANGE"],
            self._config["MAX_TIME_RANGE"],
            self._config["MAX_TIME_RANGE_TO_SKIP"],
        )
        return api_providers.Normalizer(domain)

    def _setup_charger(self, db: DB) -> api_providers.Charger:
        dm = data_managers.Charger(db)
        domain = domains.Charger(dm)
        return api_providers.Charger(domain)

    def _setup_collector(self, db: DB) -> api_providers.Collector:
        dm = data_managers.Collector(db)
        domain = domains.Collector(dm)
        return api_providers.Collector(domain)

    def _setup_system_op(self, db: DB) -> SystemOpManager:
        return SystemOpManager(db)

    async def _start_tasks(self, *args, **kwargs):
        self._periodic_tasks = []

        if self._config["TIME_PERIOD_AUTO_MARK_EXPIRED_TASKS"]:
            loop = asyncio.get_event_loop()

            system_op = domains.system_op.Domain(
                self._system_dm, self._config["TASK_LIFETIME"]
            )

            task = loop.create_task(
                tasks.auto_mark_expired_tasks(
                    system_op, self._config["TIME_PERIOD_AUTO_MARK_EXPIRED_TASKS"], loop
                )
            )
            self._periodic_tasks.append(task)

    async def _stop_tasks(self, *args, **kwargs):
        for task in self._periodic_tasks:
            task.cancel()
            try:
                await task
            except asyncio.CancelledError:
                pass

    async def _run(self) -> web.Application:
        db = await DB.create(
            self._config["DATABASE_URL"], self._config["DATABASE_URL_RO"]
        )

        return self.setup(db)

    def run(self):
        web.run_app(self._run())
