import logging
import pytz

from collections import defaultdict
from datetime import datetime, timezone
from operator import itemgetter

from maps_adv.common.helpers.enums import BillingTypeEnum, CampaignTypeEnum
from maps_adv.common.helpers.mappers import BILLING_TYPES, EVENT_TYPES
from maps_adv.export.lib.core.enum import CampaignType
from maps_adv.export.lib.pipeline.exceptions import StepException
from maps_adv.statistics.dashboard.client import Client

logger = logging.getLogger(__name__)

_type_converter = {
    CampaignType.BILLBOARD: CampaignTypeEnum.BILLBOARD,
    CampaignType.CATEGORY: CampaignTypeEnum.CATEGORY_SEARCH,
    CampaignType.CATEGORY_SEARCH: CampaignTypeEnum.CATEGORY_SEARCH,
    CampaignType.OVERVIEW_BANNER: CampaignTypeEnum.OVERVIEW_BANNER,
    CampaignType.PIN_ON_ROUTE: CampaignTypeEnum.PIN_ON_ROUTE,
    CampaignType.PIN_SEARCH: CampaignTypeEnum.CATEGORY_SEARCH,
    CampaignType.PROMOCODE: CampaignTypeEnum.PROMOCODE,
    CampaignType.ROUTE_BANNER: CampaignTypeEnum.ROUTE_BANNER,
    CampaignType.ROUTE_VIA_POINT: CampaignTypeEnum.VIA_POINTS,
    CampaignType.ZERO_SPEED_BANNER: CampaignTypeEnum.ZERO_SPEED_BANNER,
}


class AddStatisticsStep:
    def __init__(self, config):
        self._client = Client(config["DASHBOARD_URL"])

    async def __call__(self, campaigns):
        async with self._client as stat_client:
            campaigns_per_tz = defaultdict(list)
            for campaign in campaigns:
                if not EVENT_TYPES[_type_converter[campaign["campaign_type"]]]:
                    continue
                campaigns_per_tz[campaign.get("timezone", "")].append(
                    {
                        "id": campaign["id"],
                        "type": _type_converter[campaign["campaign_type"]],
                    }
                )

            events = {}

            troublesome_ids = []
            processed_ids = []

            for tz_name, tz_campaigns in campaigns_per_tz.items():
                campaign_ids = list(map(itemgetter("id"), tz_campaigns))
                try:
                    tz_events = {}
                    if not tz_name:
                        tz_events = await stat_client.campaigns_events(tz_campaigns)
                    else:
                        tz = pytz.timezone(tz_name)
                        now_local = datetime.now(tz=tz)
                        day_start_local = datetime(
                            year=now_local.year,
                            month=now_local.month,
                            day=now_local.day,
                            tzinfo=now_local.tzinfo,
                        )
                        start = day_start_local.astimezone(timezone.utc)
                        end = datetime.now(tz=timezone.utc)
                        tz_events = await stat_client.campaigns_events(
                            tz_campaigns, period_from=start, period_to=end
                        )
                    events.update(tz_events)
                    processed_ids += campaign_ids
                except Exception:
                    logger.exception(
                        f"Failed to fetch statistics for campaigns {campaign_ids}"
                    )
                    troublesome_ids += campaign_ids

            for campaign in campaigns:
                if campaign["id"] not in events:
                    continue
                if (
                    BILLING_TYPES[_type_converter[campaign["campaign_type"]]]
                    == BillingTypeEnum.CPA
                ):
                    campaign["total_actions"] = events[campaign["id"]]
                else:
                    campaign["total_displays"] = events[campaign["id"]]

            if troublesome_ids:
                raise StepException(
                    troublesome_ids=troublesome_ids,
                    processed_ids=processed_ids,
                )
