from itertools import filterfalse
from operator import attrgetter
from typing import Iterable, List

from maps_adv.adv_store.api.schemas.enums import PublicationEnvEnum
from maps_adv.common.helpers import validate_custom_page_id
from maps_adv.export.lib.pipeline.exceptions import StepException

from .feature import Terminator
from .page import (
    Page,
    NAVIGATOR_PAGES,
    METRO_PAGES,
    MOBILE_MAPS_PAGES,
)


class ResolvePagesStep:
    ENV_SUFFIX = {
        PublicationEnvEnum.PRODUCTION: "",
        PublicationEnvEnum.DATA_TESTING: "/datatesting",
    }

    def __init__(self, config, pages=NAVIGATOR_PAGES + MOBILE_MAPS_PAGES + METRO_PAGES):
        self._pages = pages
        self._ctype = config.INSTANCE_TAG_CTYPE

    async def __call__(self, campaigns):
        troublesome_ids = []
        processed_ids = []
        for campaign in campaigns:
            pages = set()

            if not validate_custom_page_id(
                campaign.get("settings", {}).get("custom_page_id")
            ):
                troublesome_ids.append(campaign["id"])
                continue

            for page in self._pages:
                pages.update(await self._pages_for_campaign(page, campaign))

            campaign["pages"] = sorted(pages)
            processed_ids.append(campaign["id"])

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

    async def _pages_for_campaign(self, page: Page, campaign: dict) -> List[str]:
        if not ResolvePagesStep._check_filters(page, campaign):
            return []

        pages = []
        versions = self._versions(page, campaign)

        for env in campaign["publication_envs"]:
            if (
                env == PublicationEnvEnum.DATA_TESTING
                and "custom_page_id" in campaign["settings"]
            ):
                suffix = f'/testing_{campaign["settings"]["custom_page_id"]}'
            else:
                suffix = ResolvePagesStep.ENV_SUFFIX[env]

            for version in versions:
                pages.append(
                    "{name}{version_delimiter}{version}{suffix}".format(
                        name=page.name,
                        version=version or "",
                        version_delimiter="_" if version else "",
                        suffix=suffix,
                    )
                )

        return pages

    def _versions(self, page: Page, campaign: dict) -> Iterable[int]:
        max_version = max(map(attrgetter("min_version"), page.features))

        enabled_features = page.features + [Terminator(max_version + 1)]
        if self._ctype == "production":
            enabled_features = list(
                filterfalse(attrgetter("for_testing_only"), enabled_features)
            )

        validated_features = filter(lambda f: f.validate(campaign), enabled_features)
        invalidated_features = filter(
            lambda f: f.invalidate(campaign), enabled_features
        )

        return range(
            max(map(attrgetter("min_version"), validated_features)),
            min(map(attrgetter("min_version"), invalidated_features)),
        )

    @staticmethod
    def _check_filters(page: Page, campaign: dict):
        return all(map(lambda func: func(campaign), page.filters))
