import asyncio

from aiohttp import web
from smb.common.multiruntime.lib.io import setup_filesystem
from smb.common.pgswim import SwimEngine
from smb.common.sensors import IGaugeBuilder, MetricsHub

from maps_adv.common.geoproduct import GeoproductClient
from maps_adv.common.lasagna import Lasagna
from maps_adv.common.yasms.client import YasmsClient
from maps_adv.geosmb.booking_yang.server.lib.clients import YangClient
from maps_adv.geosmb.clients.geosearch import GeoSearchClient
from maps_adv.geosmb.doorman.client import DoormanClient

from . import tasks
from .api import create as create_api
from .data_managers import OrdersDataManager
from .db.engine import DB
from .domains import OrdersDomain

setup_filesystem("maps_adv/geosmb/booking_yang/server/")


class Application(Lasagna):
    __slots__ = ("domain", "dm", "product_sensors")

    domain: OrdersDomain
    dm: OrdersDataManager
    product_sensors: MetricsHub
    SWIM_ENGINE_CLS = DB

    TASKS = {
        "booking_yang__upload_orders": tasks.upload_orders,
        "booking_yang__create_missed_clients": tasks.create_missed_clients,
        "booking_yang__send_missed_result_events": tasks.send_missed_result_events,
        "booking_yang__import_processed_tasks": tasks.import_processed_tasks,
        "booking_yang__notify_about_processed_tasks": tasks.notify_about_processed_tasks,  # noqa
        "booking_yang__export_created_orders": tasks.CreatedOrdersYtExportTask,
        "booking_yang__export_orders_processed_by_yang": tasks.OrdersProcessedByYangYtExportTask,  # noqa
        "booking_yang__export_orders_notified_to_user": tasks.OrdersNotifiedToUsersYtExportTask,  # noqa
    }
    TASKS_KWARGS_KEYS = ["domain", "dm", "config"]

    async def _setup_layers(self, db: SwimEngine) -> web.Application:
        self.product_sensors = MetricsHub(pending_yang_tasks=IGaugeBuilder())

        clients = await self._init_clients()

        self.dm = OrdersDataManager(db)
        self.domain = OrdersDomain(
            dm=self.dm,
            **clients,
            new_yang_format=self.config["NEW_YANG_FORMAT"],
            disconnect_orgs=self.config["DISCONNECT_ORGS"],
        )

        api = create_api(self.domain)
        api.on_shutdown.append(
            lambda _: asyncio.wait(
                [c.close() for c in clients.values() if c is not None]
            )
        )

        return api

    async def _init_clients(self) -> dict:
        return {
            "geoproduct": await GeoproductClient(
                url=self.config["GEO_PRODUCT_URL"],
                default_uid=self.config["GEO_PRODUCT_BIZ_OWNER_UID"],
                tvm_client=self.tvm,
                tvm_destination="geo_product",
            ),
            "geosearch": await GeoSearchClient(
                url=self.config["GEO_SEARCH_URL"],
                tvm=self.tvm,
                tvm_destination="geo_search",
            ),
            "yang": await YangClient(
                url=self.config["YANG_URL"],
                token=self.config["YANG_TOKEN"],
                pool_id=self.config["YANG_POOL_ID"],
            ),
            "yasms": (
                await YasmsClient(
                    url=self.config["YASMS_URL"],
                    sender=self.config["YASMS_SENDER"],
                    tvm=self.tvm,
                    tvm_destination="yasms",
                )
                if self.config["YASMS_URL"] is not None
                else None
            ),
            "doorman": await DoormanClient(
                self.config["DOORMAN_URL"],
                tvm=self.tvm,
                tvm_destination=self.config["DOORMAN_TVM"],
            )
            if self.config.get("DOORMAN_URL") is not None
            else None,
        }
