from decimal import Decimal

from .base import BaseCoefCpmFilter, CpmUndefined, CpmCoef
from maps_adv.billing_proxy.lib.domain.enums import (
    TargetingCriterion,
    CpmCoefFieldType,
)


class TargetingTypeCpmFilter(BaseCoefCpmFilter):
    def __init__(self, *, cascade, **kwargs):
        super().__init__(**kwargs)
        self._cascade = cascade

        self._targeting_criterion_to_group = {
            targeting_criterion: num
            for num, group in enumerate(self.settings["criterion_groups"])
            for targeting_criterion in group["criteria"]
        }
        self._group_to_rate = {
            num: group["rate"]
            for num, group in enumerate(self.settings["criterion_groups"])
        }
        self._cascade_rubric_exceptions = self.settings.get(
            "cascade_rubric_exceptions", set()
        )

    def get_coefs(
        self, *, targeting_query, rubric_name=None, **kwargs
    ) -> list[CpmCoef]:
        items = []
        targeting_groups = {}

        if targeting_query is not None:
            items.append(targeting_query)

        while items:
            item = items.pop()
            tag = item["tag"]
            if tag in ("or", "and"):
                items.extend(item["items"])
            else:
                try:
                    criterion = TargetingCriterion(tag.lower())
                except ValueError:
                    continue
                if criterion in self._targeting_criterion_to_group:
                    group = self._targeting_criterion_to_group[criterion]
                    if group not in targeting_groups:
                        targeting_groups[group] = criterion

        if targeting_groups:
            criteria_rates = [
                (criterion, self._group_to_rate[group])
                for group, criterion in targeting_groups.items()
            ]

            if self._cascade and rubric_name not in self._cascade_rubric_exceptions:
                coefs = []
                for criterion, rate in criteria_rates:
                    coefs.append(CpmCoef(CpmCoefFieldType.TARGETING, criterion, rate))
                return coefs
            else:
                max_rate = max(criteria_rates, key=lambda rate: rate[1])
                return [CpmCoef(CpmCoefFieldType.TARGETING, max_rate[0], max_rate[1])]
        elif "default_rate" in self.settings:
            return [
                CpmCoef(CpmCoefFieldType.TARGETING, None, self.settings["default_rate"])
            ]
        else:
            raise CpmUndefined(f"Unsupported targeting: {targeting_query}")


cpm_filters = {
    "targeting_type_common": TargetingTypeCpmFilter(
        cascade=False,
        default_rate=1,
        criterion_groups=[
            {
                "criteria": {
                    TargetingCriterion.AGE,
                    TargetingCriterion.GENDER,
                    TargetingCriterion.INCOME,
                },
                "rate": Decimal("1.1"),
            },
            {"criteria": {TargetingCriterion.SEGMENT}, "rate": Decimal("1.25")},
        ],
    ),
    "targeting_type_cascade_except_realty": TargetingTypeCpmFilter(
        cascade=True,
        default_rate=1,
        criterion_groups=[
            {
                "criteria": {
                    TargetingCriterion.AGE,
                    TargetingCriterion.GENDER,
                    TargetingCriterion.INCOME,
                },
                "rate": Decimal("1.1"),
            },
            {"criteria": {TargetingCriterion.SEGMENT}, "rate": Decimal("1.25")},
        ],
        cascade_rubric_exceptions={"realty"},
    ),
    "targeting_type_common_with_audience": TargetingTypeCpmFilter(
        cascade=False,
        default_rate=1,
        criterion_groups=[
            {
                "criteria": {
                    TargetingCriterion.AGE,
                    TargetingCriterion.GENDER,
                    TargetingCriterion.INCOME,
                },
                "rate": Decimal("1.1"),
            },
            {"criteria": {TargetingCriterion.SEGMENT}, "rate": Decimal("1.25")},
            {"criteria": {TargetingCriterion.AUDIENCE}, "rate": Decimal("1.25")},
        ],
    ),
    "targeting_type_cascade_with_audience": TargetingTypeCpmFilter(
        cascade=True,
        default_rate=1,
        criterion_groups=[
            {
                "criteria": {
                    TargetingCriterion.AGE,
                    TargetingCriterion.GENDER,
                    TargetingCriterion.INCOME,
                },
                "rate": Decimal("1.1"),
            },
            {"criteria": {TargetingCriterion.SEGMENT}, "rate": Decimal("1.25")},
            {"criteria": {TargetingCriterion.AUDIENCE}, "rate": Decimal("1.25")},
        ],
    ),
    "targeting_type_cascade_except_realty_with_audience": TargetingTypeCpmFilter(
        cascade=True,
        default_rate=1,
        criterion_groups=[
            {
                "criteria": {
                    TargetingCriterion.AGE,
                    TargetingCriterion.GENDER,
                    TargetingCriterion.INCOME,
                },
                "rate": Decimal("1.1"),
            },
            {"criteria": {TargetingCriterion.SEGMENT}, "rate": Decimal("1.25")},
            {"criteria": {TargetingCriterion.AUDIENCE}, "rate": Decimal("1.25")},
        ],
        cascade_rubric_exceptions={"realty"},
    ),
}
