"""配额管理器（月度 token 预算校验）.

在 LLM 调用前检查用户/学校的月度 token 预算：
  - 用户级：default_teacher_monthly_budget（默认 100,000 tokens/月）
  - 学校级：default_school_monthly_budget（默认 1,000,000 tokens/月）

超限时抛 AIQuotaExceededError，由 GlobalErrorHandler 转换为 ActionState 错误响应。

全并行模式：Redis 不可用时降级放行（记录警告）。
"""

from dataclasses import dataclass

import structlog

from ..errors import AIQuotaExceededError
from .usage_recorder import UsageRecorder

logger = structlog.get_logger()


@dataclass
class QuotaStatus:
    """配额状态."""

    allowed: bool
    scope: str  # user / school
    used: int
    budget: int
    remaining: int

    @property
    def usage_percentage(self) -> float:
        """使用百分比."""
        if self.budget == 0:
            return 0.0
        return round(self.used / self.budget * 100, 2)


class QuotaEnforcer:
    """配额管理器.

    检查用户和学校的月度 token 预算，超限拒绝请求。
    全并行模式：Redis 不可用时降级放行。
    """

    def __init__(
        self,
        usage_recorder: UsageRecorder,
        user_monthly_budget: int = 100_000,
        school_monthly_budget: int = 1_000_000,
    ) -> None:
        self._recorder = usage_recorder
        self._user_budget = user_monthly_budget
        self._school_budget = school_monthly_budget

    async def check(
        self,
        user_id: str,
        school_id: str = "",
    ) -> list[QuotaStatus]:
        """检查配额.

        Args:
            user_id: 用户 ID
            school_id: 学校 ID（可选）

        Returns:
            各维度配额状态列表

        Raises:
            AIQuotaExceededError: 任一维度超限
        """
        statuses: list[QuotaStatus] = []

        if user_id:
            user_status = await self._check_user(user_id)
            statuses.append(user_status)

        if school_id:
            school_status = await self._check_school(school_id)
            statuses.append(school_status)

        for status in statuses:
            if not status.allowed:
                logger.warning(
                    "quota_exceeded",
                    scope=status.scope,
                    used=status.used,
                    budget=status.budget,
                    usage_pct=status.usage_percentage,
                )
                raise AIQuotaExceededError(
                    status.scope, status.used, status.budget,
                )

        return statuses

    async def _check_user(self, user_id: str) -> QuotaStatus:
        """检查用户级配额."""
        used = await self._recorder.get_user_usage(user_id)
        remaining = max(0, self._user_budget - used)
        allowed = used < self._user_budget
        return QuotaStatus(
            allowed=allowed,
            scope="user",
            used=used,
            budget=self._user_budget,
            remaining=remaining,
        )

    async def _check_school(self, school_id: str) -> QuotaStatus:
        """检查学校级配额."""
        used = await self._recorder.get_school_usage(school_id)
        remaining = max(0, self._school_budget - used)
        allowed = used < self._school_budget
        return QuotaStatus(
            allowed=allowed,
            scope="school",
            used=used,
            budget=self._school_budget,
            remaining=remaining,
        )

    @classmethod
    def from_settings(
        cls,
        usage_recorder: UsageRecorder,
        user_budget: int,
        school_budget: int,
    ) -> "QuotaEnforcer":
        """从配置创建配额管理器."""
        return cls(
            usage_recorder=usage_recorder,
            user_monthly_budget=user_budget,
            school_monthly_budget=school_budget,
        )
