from asyncio import Future
from collections.abc import Mapping
from datetime import datetime, timezone
from multiprocessing import Queue
from typing import List

from maps_adv.common.yt_utils import BaseYtExportTask

from .data_managers import OrdersDataManager
from .domains import OrdersDomain


async def upload_orders(*args, domain: OrdersDomain, **kwargs):
    await domain.upload_orders()


async def create_missed_clients(*args, domain: OrdersDomain, **kwargs):
    await domain.create_missed_clients()


async def send_missed_result_events(*args, domain: OrdersDomain, **kwargs):
    await domain.send_missed_result_events()


async def import_processed_tasks(*args, domain: OrdersDomain, **kwargs):
    await domain.import_processed_tasks()


async def notify_about_processed_tasks(*args, domain: OrdersDomain, **kwargs):
    await domain.notify_about_processed_tasks()


class BaseOrdersYtExportTask(BaseYtExportTask):
    _dm: OrdersDataManager
    _order_exported_dt_field: str
    _exported_order_ids: List[int]

    def __init__(
        self,
        *,
        cluster: str,
        token: str,
        table: str,
        dm: OrdersDataManager,
        order_exported_dt_field: str,
    ):
        super().__init__(cluster=cluster, token=token, table=table, data_producer=dm)
        self._dm = dm
        self._order_exported_dt_field = order_exported_dt_field
        self._exported_order_ids = []

    def __reduce__(self):
        reduced = super().__reduce__()

        state_dict = reduced[2].copy()
        # This property is not picklable, but not needed in subprocess
        del state_dict["_dm"]

        return reduced[0], reduced[1], state_dict

    async def __call__(self, *args, **kwargs):
        await super().__call__(*args, **kwargs)
        await self._dm.update_orders(
            **{
                "order_ids": self._exported_order_ids,
                self._order_exported_dt_field: datetime.now(timezone.utc),
            }
        )

    async def _put_in_queue(self, queue: Queue, data: List[dict], fut: Future):
        await super()._put_in_queue(queue, data, fut)
        if data:
            self._exported_order_ids.extend([order["order_id"] for order in data])


class CreatedOrdersYtExportTask(BaseOrdersYtExportTask):
    TABLE_SCHEMA = [
        dict(name="order_id", type="uint64", required=True),
        dict(name="created_at", type="timestamp", required=True),
        dict(name="processing_time", type="timestamp", required=False),
    ]
    ITER_SIZE: int = 1000
    CHUNKED_ITERATOR_METHOD_NAME: str = "iter_created_orders"
    TIMEOUT: int = 60
    RECREATE_TABLE_EVERY_RUN: bool = False

    def __init__(self, *args, config: Mapping, dm: OrdersDataManager, **kwargs):
        super().__init__(
            cluster=config["YT_CLUSTER"],
            token=config["YT_TOKEN"],
            table=config["YT_EXPORT_CREATED_ORDERS"],
            dm=dm,
            order_exported_dt_field="exported_as_created_at",
        )


class OrdersProcessedByYangYtExportTask(BaseOrdersYtExportTask):
    TABLE_SCHEMA = [
        dict(name="order_id", type="uint64", required=True),
        dict(name="yang_suite_id", type="string", required=True),
        dict(name="yang_task_created_at", type="timestamp", required=True),
        dict(name="task_created_at", type="timestamp", required=True),
        dict(name="task_result_got_at", type="timestamp", required=True),
    ]
    ITER_SIZE: int = 1000
    CHUNKED_ITERATOR_METHOD_NAME: str = "iter_orders_processed_by_yang"
    TIMEOUT: int = 60
    RECREATE_TABLE_EVERY_RUN: bool = False

    def __init__(self, *args, config: Mapping, dm: OrdersDataManager, **kwargs):
        super().__init__(
            cluster=config["YT_CLUSTER"],
            token=config["YT_TOKEN"],
            table=config["YT_EXPORT_ORDERS_PROCESSED_BY_YANG_TABLE"],
            dm=dm,
            order_exported_dt_field="exported_as_processed_at",
        )


class OrdersNotifiedToUsersYtExportTask(BaseOrdersYtExportTask):
    TABLE_SCHEMA = [
        dict(name="order_id", type="uint64", required=True),
        dict(name="yang_suite_id", type="string", required=True),
        dict(name="booking_verdict", type="string", required=False),
        dict(name="sms_sent_at", type="timestamp", required=True),
    ]
    ITER_SIZE: int = 1000
    CHUNKED_ITERATOR_METHOD_NAME: str = "iter_orders_notified_to_users"
    TIMEOUT: int = 60
    RECREATE_TABLE_EVERY_RUN: bool = False

    def __init__(self, *args, config: Mapping, dm: OrdersDataManager, **kwargs):
        super().__init__(
            cluster=config["YT_CLUSTER"],
            token=config["YT_TOKEN"],
            table=config["YT_EXPORT_ORDERS_NOTIFIED_TO_USERS_TABLE"],
            dm=dm,
            order_exported_dt_field="exported_as_notified_at",
        )
