import marshmallow_enum
from marshmallow import Schema, fields, post_dump, post_load, validate

from maps_adv.adv_store.api.schemas.enums import (
    ReasonCampaignStoppedEnum as ReasonStopped,
)
from maps_adv.statistics.beekeeper.lib.steps.base import FreeEventProcessingMode


class Campaign(Schema):
    class Meta:
        strict = True

    campaign_id = fields.Integer(required=True)
    billing_type = fields.String(required=True)
    tz_name = fields.String(required=True)

    paid_event_cost = fields.Decimal(
        required=True, as_string=True, validates=[validate.Range(0)]
    )
    paid_events_names = fields.List(fields.String(), required=True)
    paid_events_count = fields.Integer(required=True, validates=[validate.Range(0)])
    free_event_processing_mode = marshmallow_enum.EnumField(FreeEventProcessingMode)

    budget = fields.Decimal(
        required=True, allow_nan=True, as_string=True, validates=[validate.Range(0)]
    )
    daily_budget = fields.Decimal(
        required=True, allow_nan=True, as_string=True, validates=[validate.Range(0)]
    )

    charged = fields.Decimal(
        required=True, as_string=True, validates=[validate.Range(0)]
    )
    daily_charged = fields.Decimal(
        required=True, as_string=True, validates=[validate.Range(0)]
    )

    # ChargesCalculator
    last_paid_event_cost = fields.Decimal(
        required=True, as_string=True, validates=[validate.Range(0)]
    )
    paid_events_to_charge = fields.Integer(required=True, validates=[validate.Range(0)])

    def _load_free_event_processing_mode(self, data):
        return FreeEventProcessingMode.ONLY_IF_PAID_PRESENT


class Order(Schema):
    class Meta:
        strict = True

    order_id = fields.Integer(required=True, allow_none=True)
    balance = fields.Decimal(required=True, allow_nan=True, as_string=True)
    campaigns = fields.Nested(Campaign, many=True, required=True)

    # ChargesCalculator
    amount_to_bill = fields.Decimal(
        required=True, as_string=True, validates=[validate.Range(0)]
    )

    # BillingNotification
    billing_success = fields.Boolean(required=True, allow_none=True)


class EmptyState(Schema):
    class Meta:
        strict = True


class FinalState(Schema):
    class Meta:
        strict = True

    packet_start = fields.DateTime(required=True)
    packet_end = fields.DateTime(required=True)
    orders = fields.Nested(Order, many=True, required=True)

    # BillingNotification
    billing_applied = fields.Boolean(required=True)

    # AdvStoreNotifier
    stopped_campaigns = fields.Dict(required=True)

    @post_load
    def _load_stopped_campaign(self, data):
        if "stopped_campaigns" not in self.exclude:
            data["stopped_campaigns"] = {
                int(key): ReasonStopped[reason]
                for key, reason in data["stopped_campaigns"].items()
            }

    @post_dump
    def _dump_stopped_campaign(self, data):
        if "stopped_campaigns" not in self.exclude:
            data["stopped_campaigns"] = {
                int(key): reason.name
                for key, reason in data["stopped_campaigns"].items()
            }


class ContextState(FinalState):
    class Meta:
        strict = True
        exclude = (
            "billing_applied",
            "stopped_campaigns",
            "orders.amount_to_bill",
            "orders.billing_success",
            "orders.campaigns.last_paid_event_cost",
            "orders.campaigns.paid_events_to_charge",
        )


class ChargesCalculatorState(FinalState):
    class Meta:
        strict = True
        exclude = ("billing_applied", "stopped_campaigns", "orders.billing_success")


class BillingNotificationState(FinalState):
    class Meta:
        strict = True
        exclude = ("stopped_campaigns",)


class PaidEventsPorterState(FinalState):
    class Meta:
        strict = True
        exclude = ("stopped_campaigns",)


class AdvStoreNotificationState(FinalState):
    class Meta:
        strict = True


class FreeEventsPorterState(FinalState):
    class Meta:
        strict = True
        exclude = ("orders.campaigns.free_event_processing_mode",)


class MarkProcessedPacketState(FinalState):
    class Meta:
        strict = True
