from decimal import Decimal
from typing import Dict, List

from maps_adv.stat_tasks_starter.lib.charger.clients.adv_store.enums import (
    ReasonsToStop,
)


def filter_to_stop(orders: List[dict]) -> List[dict]:
    campaigns_to_close = []
    for order in orders:
        campaigns_filter = (
            filter_campaigns_without_order
            if order["order_id"] is None
            else filter_campaigns_with_order
        )
        campaigns_to_close.extend(campaigns_filter(order))

    return campaigns_to_close


def filter_campaigns_without_order(order):
    return filter_campaigns_in_order(order["campaigns"])


def filter_campaigns_with_order(order):
    if order["billing_success"] is False:
        return []
    elif order["amount_to_bill"] >= order["budget_balance"]:
        return close_all_campaigns_in_order(order)
    else:
        return filter_campaigns_in_order(order["campaigns"])


def check_if_campaign_out_of_budget(
    campaign: Dict, charge_for_campaign: Decimal
) -> bool:
    return campaign["charged"] + charge_for_campaign >= campaign["budget"]


def check_if_campaign_out_of_daily_budget(
    campaign: Dict, charge_for_campaign: Decimal
) -> bool:
    return campaign["charged_daily"] + charge_for_campaign >= campaign["daily_budget"]


def calculate_charge_for_campaign(campaign: Dict) -> Decimal:
    if not (campaign["cost_per_last_event"] or campaign["cost_per_event"]):
        return Decimal("0")

    return (
        campaign["cost_per_last_event"]
        + (campaign["events_to_charge"] - 1) * campaign["cost_per_event"]
    )


def filter_campaigns_in_order(campaigns: List[dict]) -> List[dict]:
    campaigns_to_close = []

    for campaign in campaigns:
        charge_for_campaign = calculate_charge_for_campaign(campaign)

        if check_if_campaign_out_of_budget(campaign, charge_for_campaign):
            campaigns_to_close.append(
                {
                    "campaign_id": campaign["campaign_id"],
                    "reason_stopped": ReasonsToStop.budget_limit,
                }
            )
        elif check_if_campaign_out_of_daily_budget(campaign, charge_for_campaign):
            campaigns_to_close.append(
                {
                    "campaign_id": campaign["campaign_id"],
                    "reason_stopped": ReasonsToStop.daily_budget_limit,
                }
            )

    return campaigns_to_close


def close_all_campaigns_in_order(order: Dict) -> List[dict]:
    campaigns_to_close = []
    for campaign in order["campaigns"]:
        campaigns_to_close.append(
            {
                "campaign_id": campaign["campaign_id"],
                "reason_stopped": ReasonsToStop.order_limit,
            }
        )

    return campaigns_to_close
