import asyncio
import urllib.parse

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

from smb.common.aiotvm import TvmClient
from maps_adv.common.lasagna import Lasagna
from maps_adv.geosmb.booking_yang.client import BookingYangClient
from maps_adv.geosmb.clients.bvm import BvmClient
from maps_adv.geosmb.clients.facade import FacadeIntClient
from maps_adv.geosmb.clients.geosearch import GeoSearchClient
from maps_adv.geosmb.clients.logbroker.logbroker import LogbrokerClient
from maps_adv.geosmb.clients.loyalty import LoyaltyIntClient
from maps_adv.geosmb.clients.market import MarketIntClient
from maps_adv.geosmb.doorman.client import DoormanClient

from . import tasks
from .api import create as create_api
from .domains import CouponsDomain, PoiDomain

setup_filesystem("maps_adv/geosmb/crane_operator")


class Application(Lasagna):
    __slots__ = ("coupons_domain", "poi_domain")

    SWIM_ENGINE_CLS = None

    TASKS = {
        "geosmb_crane_operator__export_orgs_with_coupons_to_yt": tasks.OrgsWithCouponsYtExportTask,  # noqa
        "geosmb_crane_operator__export_orgs_with_booking_to_yt": tasks.OrgsWithBookingYtExportTask,  # noqa
        "geosmb_crane_operator__export_loyalty_items_to_yt": tasks.LoyaltyItemsYtExportTask,  # noqa
        "geosmb_crane_operator__export_business_coupons_to_yt": tasks.BusinessCouponsYtExportTask,  # noqa
        "geosmb_crane_operator__export_coupons_to_review": tasks.export_coupons_to_review,  # noqa
        "geosmb_crane_operator__export_reviewed_to_loyalty": tasks.export_reviewed_to_loyalty,  # noqa
        "geosmb_crane_operator__generate_data_for_snippet": tasks.GenerateDataForSnippet,  # noqa
        "geosmb_crane_operator__export_orders_poi_data": tasks.OrdersPoiYtExportTask,
        "geosmb_crane_operator__export_coupons_with_segments": tasks.CouponsWithSegmentsYtExportTask,  # noqa
        "geosmb_crane_operator__generate_coupons_poi_data": tasks.GenerateCouponsPoiDataTask,  # noqa
        "geosmb_crane_operator__export_coupon_promotions": tasks.export_coupon_promotions,  # noqa
    }
    TASKS_KWARGS_KEYS = ["coupons_domain", "poi_domain", "config"]

    coupons_domain: CouponsDomain
    poi_domain: PoiDomain

    async def _setup_layers(self, db: None) -> web.Application:
        tvm_client = await TvmClient(
            self.config["TVM_DAEMON_URL"], self.config["TVM_TOKEN"]
        )

        clients = {
            "tvm_client": tvm_client,
            "facade_client": await FacadeIntClient(
                url=self.config["FACADE_URL"],
                tvm=tvm_client,
                tvm_source="self",
                tvm_destination="facade",
            ),
            "loyalty_client": await LoyaltyIntClient(
                url=self.config["LOYALTY_URL"],
                tvm=tvm_client,
                tvm_source="self",
                tvm_destination="loyalty",
            ),
            "booking_yang": await BookingYangClient(
                url=self.config["BOOKING_YANG_URL"],
                tvm=tvm_client,
                tvm_source="self",
                tvm_destination="booking_yang",
            ),
            "doorman_client": await DoormanClient(
                self.config["DOORMAN_URL"],
                tvm=self.tvm,
                tvm_source="self",
                tvm_destination="doorman",
            ),
            "market_client": await MarketIntClient(self.config["MARKET_INT_URL"]),
            "bvm_client": await BvmClient(self.config["BVM_URL"]),
            "geosearch_client": await GeoSearchClient(
                self.config["GEOSEARCH_URL"],
                tvm=self.tvm,
                tvm_source="self",
                tvm_destination="geosearch",
            ),
        }

        for _client in clients.values():
            self.register_client(_client)

        parsed_tvm_daemon_url = urllib.parse.urlparse(self.config["TVM_DAEMON_URL"])

        logbroker_reader_clients = [
            LogbrokerClient(
                host=lb_host,
                port=self.config["LOGBROKER_PORT"],
                default_source_id=self.config["LOGBROKER_DEFAULT_SOURCE_ID"],
                tvm_self_alias=self.config["TVM_SELF_ALIAS"],
                tvm_port=parsed_tvm_daemon_url.port,
                tvm_destination="logbroker",
            )
            for lb_host in self.config["LOGBROKER_READER_HOSTS"]
        ]

        logbroker_writer_client = LogbrokerClient(
            host=self.config["LOGBROKER_WRITER_HOST"],
            port=self.config["LOGBROKER_PORT"],
            default_source_id=self.config["LOGBROKER_DEFAULT_SOURCE_ID"],
            tvm_self_alias=self.config["TVM_SELF_ALIAS"],
            tvm_port=parsed_tvm_daemon_url.port,
            tvm_destination="logbroker",
        )

        self.coupons_domain = CouponsDomain(
            facade_client=clients["facade_client"],
            loyalty_client=clients["loyalty_client"],
            logbroker_writer_client=logbroker_writer_client,
            logbroker_reader_clients=logbroker_reader_clients,
            logbroker_consumer_id=self.config["LOGBROKER_CONSUMER_ID"],
            logbroker_message_count_threshold=self.config[
                "LOGBROKER_MESSAGE_COUNT_THRESHOLD"
            ],
            coupons_to_review_topic_out=self.config[
                "LOGBROKER_COUPONS_TO_REVIEW_TOPIC_OUT"
            ],
            coupons_reviewed_topic_in=self.config[
                "LOGBROKER_COUPONS_REVIEWED_TOPIC_IN"
            ],
        )

        self.poi_domain = PoiDomain(
            booking_yang=clients["booking_yang"],
            doorman_client=clients["doorman_client"],
            market_client=clients["market_client"],
            bvm_client=clients["bvm_client"],
            geosearch_client=clients["geosearch_client"],
            booking_yang_poi_experiment=self.config[
                "BOOKING_YANG_ORDERS_POI_EXPERIMENT"
            ],
            market_poi_experiment=self.config["MARKET_INT_ORDERS_POI_EXPERIMENT"],
        )

        _api = create_api()
        _api.on_startup.append(
            lambda _: asyncio.wait(
                [
                    logbroker_writer_client.start(),
                    *[client.start() for client in logbroker_reader_clients],
                ]
            )
        )
        _api.on_shutdown.append(
            lambda _: asyncio.gather(
                logbroker_writer_client.close(),
                *[client.close() for client in logbroker_reader_clients],
            )
        )

        return _api
