import logging
from decimal import Decimal
from typing import Optional

from maps_adv.statistics.beekeeper.lib.steps.base import BaseStep
from maps_adv.warden.client.lib import TaskContext

from . import schemas


class ChargesCalculator(BaseStep):

    input_schema = schemas.ContextState
    output_schema = schemas.ChargesCalculatorState

    async def run(self, data: Optional[dict] = None, _: Optional[TaskContext] = None):
        for order in data["orders"]:
            amount_to_bill = Decimal("0.000")
            for campaign in order["campaigns"]:
                event_cost = campaign["paid_event_cost"]

                # Find "closest" campaign limit
                campaign_limit = min(
                    campaign["budget"] - campaign["charged"],
                    campaign["daily_budget"] - campaign["daily_charged"],
                    order["balance"] - amount_to_bill,
                )
                if campaign_limit <= 0:
                    campaign["paid_events_to_charge"] = 0
                    campaign["last_paid_event_cost"] = Decimal("0")
                    if campaign_limit < 0:
                        self._warn_about_overbudget(campaign)
                    continue

                campaign_charge = min(
                    campaign_limit, campaign["paid_events_count"] * event_cost
                )
                paid_events_to_charge, remainder = divmod(campaign_charge, event_cost)
                if remainder == 0:
                    last_paid_event_cost = event_cost
                else:
                    paid_events_to_charge += 1
                    last_paid_event_cost = remainder

                campaign["paid_events_to_charge"] = int(paid_events_to_charge)
                campaign["last_paid_event_cost"] = last_paid_event_cost
                amount_to_bill += campaign_charge

            order["amount_to_bill"] = amount_to_bill

        return data

    @staticmethod
    def _warn_about_overbudget(campaign: dict) -> None:
        logger = logging.getLogger("beekeeper.charges_calculator")
        if campaign["daily_charged"] > campaign["daily_budget"]:
            logger.error(
                f"Negative charge limit detected for campaign {campaign['campaign_id']} (daily_budget): "  # noqa: E501
                "daily_budget=%f, daily_charged=%f (%s)",
                campaign["daily_budget"],
                campaign["daily_charged"],
                campaign,
            )
        if campaign["charged"] > campaign["budget"]:
            logger.error(
                f"Negative charge limit detected for campaign {campaign['campaign_id']} (budget): "  # noqa: E501
                "budget=%f, charged=%f (%s)",
                campaign["budget"],
                campaign["charged"],
                campaign,
            )
