import logging
from typing import Iterable, List, Tuple

from maps_adv.export.lib.config import config
from maps_adv.adv_store.api.schemas.enums import PlatformEnum, PublicationEnvEnum

from maps_adv.export.lib.core.enum import (
    ActionType,
    CampaignType,
    CreativeType,
    ImageType,
)
from maps_adv.export.lib.core.exception import UnsupportedCampaignPlatform
from maps_adv.export.lib.core.utils import generate_image_filename_from_avatar

from .images import TemplateImageFields
from .phone import phone_transform

logger = logging.getLogger(__name__)


class AdvertDataTransform:
    def __init__(
        self,
        campaign: dict,
        experiment_maps_highlighted: List[PublicationEnvEnum],
        experiment_navi_highlighted: List[PublicationEnvEnum],
    ):
        self._campaign = campaign
        self._experiment_maps_highlighted = experiment_maps_highlighted
        self._experiment_navi_highlighted = experiment_navi_highlighted

    @classmethod
    def _get_creative_images(cls, creative: dict) -> dict:
        return {image["type"]: image for image in creative["images"]}

    @classmethod
    def _split_pages_by_groups(cls, pages: List[str]) -> Tuple[List[str], List[str]]:
        pages_production = []
        pages_datatesting = []
        for page in pages:
            if page.endswith("/datatesting"):
                pages_datatesting.append(page)
            else:
                pages_production.append(page)
        return pages_production, pages_datatesting

    async def _generate_maps_fields(self, search_pin: dict) -> dict:
        generate_filename = generate_image_filename_from_avatar
        images = self._get_creative_images(search_pin)

        optional_fields = {}
        if ImageType.BANNER in images:
            optional_fields["styleBalloonBanner"] = generate_filename(
                images[ImageType.BANNER]
            )

        return dict(
            advert_type="menu_icon",
            anchorDust="0.5 0.5",
            anchorDustHover="0.5 0.5",
            anchorDustVisited="0.5 0.5",
            anchorIcon="0.5 0.89",
            anchorIconHover="0.5 0.89",
            anchorIconVisited="0.5 0.89",
            anchorSelected="0.5 0.94",
            sizeDust="18 18",
            sizeDustHover="18 18",
            sizeDustVisited="18 18",
            sizeIcon="32 38",
            sizeIconHover="32 38",
            sizeIconVisited="32 38",
            sizeSelected="60 68",
            styleDust=generate_filename(images[ImageType.DUST]),
            styleDustHover=generate_filename(images[ImageType.DUST_HOVER]),
            styleDustVisited=generate_filename(images[ImageType.DUST_VISITED]),
            styleIcon=generate_filename(images[ImageType.PIN]),
            styleIconHover=generate_filename(images[ImageType.PIN_HOVER]),
            styleIconVisited=generate_filename(images[ImageType.PIN_VISITED]),
            styleLogo=generate_filename(images[ImageType.LOGO]),
            styleSelected=generate_filename(images[ImageType.PIN_SELECTED]),
            **optional_fields,
        )

    async def _generate_navi_fields(self, search_pin: dict) -> dict:
        generate_filename = generate_image_filename_from_avatar
        images = self._get_creative_images(search_pin)

        optional_images = {
            "dust": TemplateImageFields(images, anchor="0.5 0.5", size="18 18")
        }

        return dict(
            advert_type="menu_icon",
            anchorIcon="0.5 0.5",
            anchorIconHover="0.5 0.5",
            anchorIconVisited="0.5 0.5",
            anchorSelected="0.5 0.94",
            sizeIcon="32 32",
            sizeIconHover="32 32",
            sizeIconVisited="32 32",
            sizeSelected="60 68",
            styleIcon=generate_filename(images[ImageType.PIN_ROUND]),
            styleIconHover=generate_filename(images[ImageType.PIN_ROUND]),
            styleIconVisited=generate_filename(images[ImageType.PIN_ROUND]),
            styleSelected=generate_filename(images[ImageType.PIN_SELECTED]),
            **optional_images["dust"].generate("Dust", ImageType.DUST),
            **optional_images["dust"].generate("DustHover", ImageType.DUST_HOVER),
            **optional_images["dust"].generate("DustVisited", ImageType.DUST_VISITED),
        )

    async def _transform_pin_search_creative(
        self, data: dict, pin_search: dict
    ) -> Iterable[dict]:
        platforms = self._campaign["platforms"]
        data = data.copy()
        data_list = []

        if platforms == [PlatformEnum.NAVI]:
            data["fields"] = await self._generate_navi_fields(pin_search)

        elif platforms == [PlatformEnum.MAPS]:
            data["fields"] = await self._generate_maps_fields(pin_search)

        else:
            logger.debug("Platforms %s", platforms)
            raise UnsupportedCampaignPlatform()

        pin_title = pin_search.get("title")
        if pin_title:
            data["title"] = pin_title
            data["companies"] = sorted(pin_search["organizations"].keys())
            data_list.append(data)

        else:
            # Необходимо размножать и подменивать title, для
            # компаний. Так как навигатор не умеет сам резолвить
            # title, если он не указан. И мы его вынуждены
            # прописывать самостоятельно. [GEOADVDEV-426]
            pin_companies = pin_search["organizations"]
            for permalink, company in sorted(pin_companies.items()):
                data_company = data.copy()
                data_company.update(title=company.title, companies=[permalink])

                data_list.append(data_company)

        return data_list

    def _apply_experiment(
        self, data: Iterable[dict], pin_search: dict
    ) -> Iterable[dict]:
        platforms = self._campaign["platforms"]
        publication_envs = self._campaign["publication_envs"]
        if (
            platforms == [PlatformEnum.MAPS]
            and bool(set(publication_envs) & set(self._experiment_maps_highlighted))
        ) or (
            platforms == [PlatformEnum.NAVI]
            and bool(set(publication_envs) & set(self._experiment_navi_highlighted))
        ):
            try:
                result = []
                for item in data:
                    pages_production, pages_testing = self._split_pages_by_groups(
                        item["pages"]
                    )

                    for pub_env, pages in (
                        (PublicationEnvEnum.PRODUCTION, pages_production),
                        (PublicationEnvEnum.DATA_TESTING, pages_testing),
                    ):
                        if pub_env in publication_envs:
                            new_item = item.copy()
                            new_item["pages"] = pages

                            if (
                                platforms == [PlatformEnum.MAPS]
                                and pub_env in self._experiment_maps_highlighted
                            ) or (
                                platforms == [PlatformEnum.NAVI]
                                and pub_env in self._experiment_navi_highlighted
                            ):
                                new_item["highlighted"] = "false"

                            result.append(new_item)

                return result
            except Exception:
                logger.exception(f"Can't apply experiment for {platforms} platform")

        return data

    async def transform(self) -> List[dict]:
        data_list = []

        campaign = self._campaign
        if campaign["campaign_type"] == CampaignType.PIN_SEARCH:
            text = campaign["creatives"][CreativeType.TEXT]
            data = dict(
                pages=campaign["pages"],
                log_id="ac_auto_log_id_campaign_{}".format(campaign["id"]),
                text=text["text"],
                disclaimer=text["disclaimer"],
            )

            for action in campaign["actions"]:
                if action["type"] == ActionType.PHONE_CALL:
                    data["phone"] = phone_transform(action["phone"])

            for pin_search in campaign["creatives"][CreativeType.PIN_SEARCH]:
                data_list.extend(
                    self._apply_experiment(
                        await self._transform_pin_search_creative(data, pin_search),
                        pin_search,
                    )
                )

        return data_list


async def advert_data_transform(campaign: dict) -> List[dict]:
    return await AdvertDataTransform(
        campaign,
        experiment_maps_highlighted=config.EXPERIMENT_USE_HIGHLIGHTED_ATTR_FOR_PIN_SEARCH,  # noqa: E501
        experiment_navi_highlighted=config.EXPERIMENT_USE_HIGHLIGHTED_ATTR_FOR_PIN_SEARCH_NAVI,  # noqa: E501
    ).transform()
