"""Quota system with registry pattern.

Three subscription tiers with different limits:
- free:    2GB storage, 5 videos/month, 3 concurrent, 3 templates, 50 titles, 10 voiceovers, no AI voice
- basic:   20GB storage, 30 videos/month, 10 concurrent, 15 templates, 500 titles, 100 voiceovers, AI voice
- premium: 100GB storage, 100 videos/month, 20 concurrent, unlimited templates, 500 titles, 100 voiceovers, AI voice

Quota dimensions are registered by modules via the ModuleRegistry,
and checked against the user's subscription plan.
"""

from __future__ import annotations

from dataclasses import dataclass, field
from enum import Enum
from typing import Dict, List, Optional


class QuotaDimension(str, Enum):
    """配额维度 - 所有可量化的资源限制"""

    STORAGE_GB = "storage_gb"  # 存储空间 (GB)
    VIDEOS_PER_MONTH = "videos_per_month"  # 每月生成视频数
    MAX_CONCURRENT = "max_concurrent"  # 最大并发任务数
    MAX_TEMPLATES = "max_templates"  # 最大模板数
    MAX_TITLES = "max_titles"  # 最大标题库条目数
    MAX_VOICEOVERS = "max_voiceovers"  # 最大配音库条目数
    AI_VOICE_ENABLED = "ai_voice_enabled"  # AI 配音是否可用 (0/1)
    # 以下维度由扩展模块注册，初始配额为 0（由模块注册时填充）
    AI_VOICE_CREDITS = "ai_voice_credits"  # AI 配音积分（每月）
    BATCH_EXPORT_ENABLED = "batch_export_enabled"  # 批量导出
    MULTI_PLATFORM_ENABLED = "multi_platform_enabled"  # 多平台发布
    DEDUP_REPORT_ENABLED = "dedup_report_enabled"  # 去重检测报告


@dataclass
class QuotaTier:
    """一个套餐等级的配额定义"""

    name: str
    limits: Dict[str, float] = field(default_factory=dict)

    def get_limit(self, dimension: str) -> float:
        """获取指定维度的配额限制，未定义返回 0"""
        return self.limits.get(dimension, 0)

    def is_unlimited(self, dimension: str) -> bool:
        """指定维度是否不限量"""
        return self.limits.get(dimension, float("inf")) == float("inf")


# 三个套餐等级的配额定义
QUOTA_TIERS: Dict[str, QuotaTier] = {
    "free": QuotaTier(
        name="free",
        limits={
            QuotaDimension.STORAGE_GB: 2,
            QuotaDimension.VIDEOS_PER_MONTH: 5,
            QuotaDimension.MAX_CONCURRENT: 3,
            QuotaDimension.MAX_TEMPLATES: 3,
            QuotaDimension.MAX_TITLES: 50,
            QuotaDimension.MAX_VOICEOVERS: 10,
            QuotaDimension.AI_VOICE_ENABLED: 0,
            QuotaDimension.AI_VOICE_CREDITS: 0,
            QuotaDimension.BATCH_EXPORT_ENABLED: 0,
            QuotaDimension.MULTI_PLATFORM_ENABLED: 0,
            QuotaDimension.DEDUP_REPORT_ENABLED: 0,
        },
    ),
    "basic": QuotaTier(
        name="basic",
        limits={
            QuotaDimension.STORAGE_GB: 20,
            QuotaDimension.VIDEOS_PER_MONTH: 30,
            QuotaDimension.MAX_CONCURRENT: 10,
            QuotaDimension.MAX_TEMPLATES: 15,
            QuotaDimension.MAX_TITLES: 500,
            QuotaDimension.MAX_VOICEOVERS: 100,
            QuotaDimension.AI_VOICE_ENABLED: 1,
            QuotaDimension.AI_VOICE_CREDITS: 100,
            QuotaDimension.BATCH_EXPORT_ENABLED: 1,
            QuotaDimension.MULTI_PLATFORM_ENABLED: 0,
            QuotaDimension.DEDUP_REPORT_ENABLED: 0,
        },
    ),
    "premium": QuotaTier(
        name="premium",
        limits={
            QuotaDimension.STORAGE_GB: 100,
            QuotaDimension.VIDEOS_PER_MONTH: 100,
            QuotaDimension.MAX_CONCURRENT: 20,
            QuotaDimension.MAX_TEMPLATES: float("inf"),  # 不限量
            QuotaDimension.MAX_TITLES: 500,
            QuotaDimension.MAX_VOICEOVERS: 100,
            QuotaDimension.AI_VOICE_ENABLED: 1,
            QuotaDimension.AI_VOICE_CREDITS: 500,
            QuotaDimension.BATCH_EXPORT_ENABLED: 1,
            QuotaDimension.MULTI_PLATFORM_ENABLED: 1,
            QuotaDimension.DEDUP_REPORT_ENABLED: 1,
        },
    ),
}


class QuotaWarningLevel:
    """配额告警级别"""

    NORMAL = "normal"  # 使用量 < 80%
    WARNING = "warning"  # 80% <= 使用量 < 100%
    CRITICAL = "critical"  # 95% <= 使用量 < 100%
    EXCEEDED = "exceeded"  # 使用量 >= 100%


@dataclass
class QuotaCheckResult:
    """配额检查结果"""

    allowed: bool
    dimension: str
    limit: float
    used: float
    remaining: float
    warning_level: str

    @property
    def usage_percent(self) -> float:
        if self.limit <= 0:
            return 100.0 if self.used > 0 else 0.0
        if self.limit == float("inf"):
            return 0.0
        return min(100.0, (self.used / self.limit) * 100)


class QuotaRegistry:
    """配额注册表

    管理所有配额维度的定义和套餐限制。
    扩展模块可通过 register_dimension() 注册新的配额维度。
    """

    def __init__(self) -> None:
        self._dimensions: Dict[str, str] = {}  # dimension_name -> description
        self._tiers: Dict[str, QuotaTier] = dict(QUOTA_TIERS)

        # 初始化内置维度
        for dim in QuotaDimension:
            self._dimensions[dim.value] = dim.name

    def register_dimension(
        self,
        dimension: str,
        description: str,
        default_limits: Optional[Dict[str, float]] = None,
    ) -> None:
        """注册新的配额维度

        Args:
            dimension: 维度名称
            description: 人类可读描述
            default_limits: 各套餐的默认限制 {plan_name: limit}
        """
        if dimension in self._dimensions:
            return  # 幂等

        self._dimensions[dimension] = description

        # 为各套餐设置默认限制
        if default_limits:
            for plan_name, limit in default_limits.items():
                if plan_name in self._tiers:
                    self._tiers[plan_name].limits[dimension] = limit
        else:
            # 默认所有套餐该维度为 0
            for tier in self._tiers.values():
                tier.limits.setdefault(dimension, 0)

    def get_tier(self, plan_name: str) -> Optional[QuotaTier]:
        """获取套餐配额定义"""
        return self._tiers.get(plan_name)

    def get_limit(self, plan_name: str, dimension: str) -> float:
        """获取指定套餐在指定维度的限制"""
        tier = self._tiers.get(plan_name)
        if tier is None:
            return 0
        return tier.get_limit(dimension)

    def list_dimensions(self) -> Dict[str, str]:
        """列出所有已注册的配额维度"""
        return dict(self._dimensions)

    def list_tiers(self) -> List[str]:
        """列出所有套餐等级"""
        return list(self._tiers.keys())


class QuotaChecker:
    """配额检查器

    检查用户在指定维度的使用量是否超出限制。
    需要调用方提供当前使用量（used），本类不直接访问数据库。
    """

    def __init__(self, registry: Optional[QuotaRegistry] = None) -> None:
        self._registry = registry or QuotaRegistry()

    def check(
        self,
        plan_name: str,
        dimension: str,
        used: float,
    ) -> QuotaCheckResult:
        """检查指定维度的配额使用情况

        Args:
            plan_name: 用户套餐等级 (free/basic/premium)
            dimension: 配额维度
            used: 当前已使用量

        Returns:
            QuotaCheckResult
        """
        limit = self._registry.get_limit(plan_name, dimension)
        remaining = max(0, limit - used) if limit != float("inf") else float("inf")
        allowed = used < limit if limit != float("inf") else True
        warning_level = self._compute_warning_level(used, limit)

        return QuotaCheckResult(
            allowed=allowed,
            dimension=dimension,
            limit=limit,
            used=used,
            remaining=remaining,
            warning_level=warning_level,
        )

    def check_multiple(
        self,
        plan_name: str,
        usage: Dict[str, float],
    ) -> List[QuotaCheckResult]:
        """批量检查多个维度的配额"""
        return [self.check(plan_name, dim, used) for dim, used in usage.items()]

    @staticmethod
    def _compute_warning_level(used: float, limit: float) -> str:
        """计算告警级别"""
        if limit <= 0:
            return QuotaWarningLevel.EXCEEDED if used > 0 else QuotaWarningLevel.NORMAL
        if limit == float("inf"):
            return QuotaWarningLevel.NORMAL

        ratio = used / limit
        if ratio >= 1.0:
            return QuotaWarningLevel.EXCEEDED
        if ratio >= 0.95:
            return QuotaWarningLevel.CRITICAL
        if ratio >= 0.80:
            return QuotaWarningLevel.WARNING
        return QuotaWarningLevel.NORMAL


def get_warning_level(used: float, limit: float) -> str:
    """便捷函数：计算配额告警级别"""
    return QuotaChecker._compute_warning_level(used, limit)


# 全局单例
quota_registry = QuotaRegistry()
quota_checker = QuotaChecker(quota_registry)
