import asyncio

from maps_adv.stat_controller.client.lib.charger import TaskStatus
from maps_adv.stat_tasks_starter.lib.base.pipeline import BasePipeline
from maps_adv.stat_tasks_starter.lib.charger.billing_charger import BillingCharger
from maps_adv.stat_tasks_starter.lib.charger.calculator import calculate_charges
from maps_adv.stat_tasks_starter.lib.charger.campaigns_stopper import CampaignStopper
from maps_adv.stat_tasks_starter.lib.charger.collector import Collector
from maps_adv.stat_tasks_starter.lib.charger.events_saver import EventsSaver
from maps_adv.stat_tasks_starter.lib.config import config


class Pipeline(BasePipeline):
    __slots__ = (
        "collector",
        "billing_charger",
        "save_events",
        "stop_campaigns",
        "operations_map",
    )

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        db_config = {
            "database": config.CH_STORAGE_DB,
            "host": config.CH_STORAGE_HOST,
            "port": config.CH_STORAGE_PORT,
            "user": config.CH_STORAGE_USER,
            "password": config.CH_STORAGE_PASSWORD,
            "normalized_table": config.CH_STORAGE_NORMALIZED_TABLE,
            "charged_table": config.CH_STORAGE_ACCEPTED_TABLE,
        }
        if config.CH_STORAGE_USE_SSL:
            db_config["secure"] = True
            db_config["ca_certs"] = config.SSL_CERT_FILE
        self.collector = Collector(
            adv_store_url=config.ADV_STORE_URL,
            billing_url=config.BILLING_URL,
            stat_db_config=db_config,
            ignore_campaign_ids=config["MONKEY_PATCH_CAMPAIGNS_FOR_BEEKEEPER"],
        )
        self.billing_charger = BillingCharger(config.BILLING_URL)
        self.save_events = EventsSaver(db_config)
        self.stop_campaigns = CampaignStopper(config.ADV_STORE_URL)
        self.operations_map = {
            TaskStatus.context_received: self.collect_data,
            TaskStatus.calculation_completed: self.calculate_charges,
            TaskStatus.billing_notified: self.charge_funds,
            TaskStatus.charged_data_sent: self.save_charged_events,
            TaskStatus.completed: self.notify_adv_store,
        }

    async def __call__(self):
        task_data = await self.find_new_task()
        current_status = task_data["status"]
        for status in current_status.slice():
            operation = self.operations_map[status]
            await operation(task_data)
            await self._stat_client.update_task(
                task_id=task_data["id"],
                status=status,
                executor_id=self.executor_id,
                execution_state=task_data["execution_state"],
            )

    async def find_new_task(self) -> dict:
        return await self._stat_client.find_new_task(executor_id=self.executor_id)

    async def collect_data(self, task_data: dict):
        task_data["execution_state"] = await self.collector(
            task_data["timing_from"], task_data["timing_to"]
        )

    async def calculate_charges(self, task_data: dict):
        orders = task_data["execution_state"]
        task_data["execution_state"] = await asyncio.gather(
            *[self._calculate(order) for order in orders]
        )

    async def _calculate(self, order: dict):
        return calculate_charges(order)

    async def charge_funds(self, task_data: dict):
        orders = task_data["execution_state"]
        await self.billing_charger(orders, task_data["timing_to"])

    async def save_charged_events(self, task_data: dict):
        orders = task_data["execution_state"]
        await self.save_events(
            orders=orders,
            timing_from=task_data["timing_from"],
            timing_to=task_data["timing_to"],
        )

    async def notify_adv_store(self, task_data: dict):
        orders = task_data["execution_state"]
        await self.stop_campaigns(orders=orders, time_moment=task_data["timing_to"])
