from __future__ import annotations

from datetime import UTC, datetime
from decimal import Decimal
from typing import Any


class FinanceDashboardBudgetMixin:
    """构建预算摘要、预算卡片和预算瓶颈展示数据。"""

    def _budget_summary(self, fiscal_year: int) -> dict[str, Any]:
        allocations = self._fetch_budget_allocations(fiscal_year)
        total = Decimal("0.00")
        used = Decimal("0.00")
        available = Decimal("0.00")

        for allocation in allocations:
            balance = self.get_balance(allocation)
            total += balance.total_amount
            used += balance.reserved_amount + balance.consumed_amount
            available += balance.available_amount

        ratio = Decimal("0.00")
        if total > Decimal("0.00"):
            ratio = (used / total) * Decimal("100")

        payload = {
            "ratio": self._decimal_number(ratio),
            "total": self._currency(total),
            "used": self._currency(used),
            "left": self._currency(available),
            "included": self.include_legacy_budget,
            "scope": "legacy_default_budget" if self.include_legacy_budget else "unavailable",
        }
        if not self.include_legacy_budget:
            payload["reason"] = "当前租户尚未接入独立预算池，预算指标未纳入统计。"
        return payload

    def _budget_metrics(self, fiscal_year: int) -> list[dict[str, Any]]:
        allocations = self._fetch_budget_allocations(fiscal_year)
        total = Decimal("0.00")
        consumed = Decimal("0.00")
        reserved = Decimal("0.00")
        available = Decimal("0.00")
        over_count = 0
        warning_count = 0

        for allocation in allocations:
            balance = self.get_balance(allocation)
            total += balance.total_amount
            consumed += balance.consumed_amount
            reserved += balance.reserved_amount
            available += balance.available_amount
            if balance.available_amount < Decimal("0.00"):
                over_count += 1
                continue
            if balance.usage_rate >= Decimal(str(allocation.warning_threshold or 80)):
                warning_count += 1

        used = consumed + reserved
        usage_rate = Decimal("0.00")
        if total > Decimal("0.00"):
            usage_rate = (used / total) * Decimal("100")

        return [
            self._budget_metric(
                label="预算池数量",
                value=f"{len(allocations)} 个",
                detail="年度有效预算池",
                tone="neutral",
                icon="mdi mdi-database-outline",
            ),
            self._budget_metric(
                label="总预算",
                value=self._currency(total),
                detail="原始预算 + 调整",
                tone="neutral",
                icon="mdi mdi-cash-register",
            ),
            self._budget_metric(
                label="已用预算",
                value=self._currency(used),
                detail=f"使用率 {self._decimal_number(usage_rate):.1f}%",
                tone="warning" if usage_rate >= Decimal("80") else "success",
                icon="mdi mdi-chart-arc",
            ),
            self._budget_metric(
                label="预占预算",
                value=self._currency(reserved),
                detail="待流转单据占用",
                tone="warning" if reserved > Decimal("0.00") else "success",
                icon="mdi mdi-lock-outline",
            ),
            self._budget_metric(
                label="可用预算",
                value=self._currency(available),
                detail="可继续使用额度",
                tone="danger" if available < Decimal("0.00") else "success",
                icon="mdi mdi-wallet-outline",
            ),
            self._budget_metric(
                label="预警预算池",
                value=f"{warning_count} 个",
                detail=f"超支 {over_count} 个",
                tone="danger" if over_count else "warning" if warning_count else "success",
                icon="mdi mdi-alert-outline",
            ),
        ]

    def _budget_metric(
        self,
        *,
        label: str,
        value: str,
        detail: str,
        tone: str,
        icon: str,
    ) -> dict[str, Any]:
        if not self.include_legacy_budget:
            detail = "当前租户未接入独立预算池"
            tone = "neutral"
        return {
            "label": label,
            "value": value,
            "detail": detail,
            "tone": tone,
            "icon": icon,
        }

    def _budget_focus_rows(self) -> list[dict[str, Any]]:
        if not self.include_legacy_budget:
            return []
        allocations = self._fetch_budget_allocations(datetime.now(UTC).year)
        over_count = 0
        warning_count = 0
        over_amount = Decimal("0.00")
        warning_used = Decimal("0.00")

        for allocation in allocations:
            balance = self.get_balance(allocation)
            if balance.available_amount < Decimal("0.00"):
                over_count += 1
                over_amount += abs(balance.available_amount)
                continue
            if balance.usage_rate >= Decimal(str(allocation.warning_threshold or 80)):
                warning_count += 1
                warning_used += balance.reserved_amount + balance.consumed_amount

        return [
            self._focus_item(
                name="预算超支",
                role="预算控制",
                duration=f"{over_count} 个池",
                status=self._currency(over_amount),
                tone="danger" if over_count else "success",
                avatar="超",
            ),
            self._focus_item(
                name="预算预警",
                role="预算控制",
                duration=f"{warning_count} 个池",
                status=self._currency(warning_used),
                tone="warning" if warning_count else "success",
                avatar="预",
            ),
        ]

    @staticmethod
    def _focus_item(
        *,
        name: str,
        role: str,
        duration: str,
        status: str,
        tone: str,
        avatar: str,
    ) -> dict[str, Any]:
        return {
            "name": name,
            "role": role,
            "duration": duration,
            "status": status,
            "tone": tone,
            "avatar": avatar,
        }
