"""Quota 配额系统单元测试。"""

import pytest

from packages.domain.quota import (
    QUOTA_TIERS,
    QuotaChecker,
    QuotaCheckResult,
    QuotaDimension,
    QuotaRegistry,
    QuotaTier,
    QuotaWarningLevel,
    get_warning_level,
    quota_checker,
    quota_registry,
)


class TestQuotaDimension:
    def test_core_dimensions_exist(self):
        assert QuotaDimension.STORAGE_GB.value == "storage_gb"
        assert QuotaDimension.VIDEOS_PER_MONTH.value == "videos_per_month"
        assert QuotaDimension.MAX_CONCURRENT.value == "max_concurrent"
        assert QuotaDimension.MAX_TEMPLATES.value == "max_templates"
        assert QuotaDimension.MAX_TITLES.value == "max_titles"
        assert QuotaDimension.MAX_VOICEOVERS.value == "max_voiceovers"
        assert QuotaDimension.AI_VOICE_ENABLED.value == "ai_voice_enabled"

    def test_extended_dimensions_exist(self):
        assert QuotaDimension.AI_VOICE_CREDITS.value == "ai_voice_credits"
        assert QuotaDimension.BATCH_EXPORT_ENABLED.value == "batch_export_enabled"
        assert QuotaDimension.MULTI_PLATFORM_ENABLED.value == "multi_platform_enabled"
        assert QuotaDimension.DEDUP_REPORT_ENABLED.value == "dedup_report_enabled"

    def test_all_dimensions_are_strings(self):
        for dim in QuotaDimension:
            assert isinstance(dim.value, str)


class TestQuotaTier:
    def test_get_limit_defined(self):
        tier = QuotaTier(name="test", limits={"storage_gb": 10, "videos": 5})
        assert tier.get_limit("storage_gb") == 10
        assert tier.get_limit("videos") == 5

    def test_get_limit_undefined_returns_zero(self):
        tier = QuotaTier(name="test", limits={"storage_gb": 10})
        assert tier.get_limit("unknown_dim") == 0

    def test_is_unlimited_false_for_finite(self):
        tier = QuotaTier(name="test", limits={"storage_gb": 10})
        assert tier.is_unlimited("storage_gb") is False

    def test_is_unlimited_true_for_inf(self):
        tier = QuotaTier(name="test", limits={"templates": float("inf")})
        assert tier.is_unlimited("templates") is True

    def test_is_unlimited_undefined(self):
        tier = QuotaTier(name="test", limits={})
        # 未定义的维度，limits.get 返回默认 inf，所以 is_unlimited 返回 True
        assert tier.is_unlimited("unknown") is True

    def test_empty_limits(self):
        tier = QuotaTier(name="empty")
        assert tier.limits == {}
        assert tier.name == "empty"


class TestQuotaTiers:
    def test_three_tiers_exist(self):
        assert "free" in QUOTA_TIERS
        assert "basic" in QUOTA_TIERS
        assert "premium" in QUOTA_TIERS

    def test_free_tier_limits(self):
        free = QUOTA_TIERS["free"]
        assert free.get_limit("storage_gb") == 2
        assert free.get_limit("videos_per_month") == 5
        assert free.get_limit("max_concurrent") == 3
        assert free.get_limit("max_templates") == 3
        assert free.get_limit("max_titles") == 50
        assert free.get_limit("max_voiceovers") == 10
        assert free.get_limit("ai_voice_enabled") == 0

    def test_basic_tier_limits(self):
        basic = QUOTA_TIERS["basic"]
        assert basic.get_limit("storage_gb") == 20
        assert basic.get_limit("videos_per_month") == 30
        assert basic.get_limit("max_concurrent") == 10
        assert basic.get_limit("max_templates") == 15
        assert basic.get_limit("max_titles") == 500
        assert basic.get_limit("max_voiceovers") == 100
        assert basic.get_limit("ai_voice_enabled") == 1
        assert basic.get_limit("ai_voice_credits") == 100
        assert basic.get_limit("batch_export_enabled") == 1

    def test_premium_tier_limits(self):
        premium = QUOTA_TIERS["premium"]
        assert premium.get_limit("storage_gb") == 100
        assert premium.get_limit("videos_per_month") == 100
        assert premium.get_limit("max_concurrent") == 20
        assert premium.is_unlimited("max_templates") is True
        assert premium.get_limit("ai_voice_enabled") == 1
        assert premium.get_limit("ai_voice_credits") == 500
        assert premium.get_limit("batch_export_enabled") == 1
        assert premium.get_limit("multi_platform_enabled") == 1
        assert premium.get_limit("dedup_report_enabled") == 1

    def test_tier_increase_monotonic(self):
        free = QUOTA_TIERS["free"]
        basic = QUOTA_TIERS["basic"]
        premium = QUOTA_TIERS["premium"]
        # 高级套餐应该 >= 低级套餐的所有限制
        for dim in [
            "storage_gb",
            "videos_per_month",
            "max_concurrent",
            "max_titles",
            "max_voiceovers",
            "ai_voice_credits",
        ]:
            assert basic.get_limit(dim) >= free.get_limit(dim)
            assert premium.get_limit(dim) >= basic.get_limit(dim)


class TestQuotaWarningLevel:
    def test_levels_exist(self):
        assert QuotaWarningLevel.NORMAL == "normal"
        assert QuotaWarningLevel.WARNING == "warning"
        assert QuotaWarningLevel.CRITICAL == "critical"
        assert QuotaWarningLevel.EXCEEDED == "exceeded"


class TestQuotaCheckResult:
    def test_usage_percent_normal(self):
        result = QuotaCheckResult(
            allowed=True,
            dimension="storage_gb",
            limit=100,
            used=50,
            remaining=50,
            warning_level="normal",
        )
        assert result.usage_percent == 50.0

    def test_usage_percent_exceeded(self):
        result = QuotaCheckResult(
            allowed=False, dimension="d", limit=100, used=150, remaining=0, warning_level="exceeded"
        )
        assert result.usage_percent == 100.0  # min(100, 150%)

    def test_usage_percent_zero_used(self):
        result = QuotaCheckResult(allowed=True, dimension="d", limit=100, used=0, remaining=100, warning_level="normal")
        assert result.usage_percent == 0.0

    def test_usage_percent_zero_limit_with_usage(self):
        result = QuotaCheckResult(allowed=False, dimension="d", limit=0, used=10, remaining=0, warning_level="exceeded")
        assert result.usage_percent == 100.0

    def test_usage_percent_zero_limit_no_usage(self):
        result = QuotaCheckResult(allowed=True, dimension="d", limit=0, used=0, remaining=0, warning_level="normal")
        assert result.usage_percent == 0.0

    def test_usage_percent_unlimited(self):
        result = QuotaCheckResult(
            allowed=True,
            dimension="d",
            limit=float("inf"),
            used=1000,
            remaining=float("inf"),
            warning_level="normal",
        )
        assert result.usage_percent == 0.0


class TestQuotaRegistry:
    def test_initial_dimensions(self):
        reg = QuotaRegistry()
        dims = reg.list_dimensions()
        assert "storage_gb" in dims
        assert "videos_per_month" in dims
        assert len(dims) == len(QuotaDimension)

    def test_list_tiers(self):
        reg = QuotaRegistry()
        tiers = reg.list_tiers()
        assert "free" in tiers
        assert "pro" in tiers
        assert "basic" in tiers
        assert "premium" in tiers
        assert len(tiers) == 4

    def test_get_tier_existing(self):
        reg = QuotaRegistry()
        tier = reg.get_tier("free")
        assert tier is not None
        assert tier.name == "free"

    def test_get_tier_unknown(self):
        reg = QuotaRegistry()
        assert reg.get_tier("unknown_plan") is None

    def test_get_limit_known(self):
        reg = QuotaRegistry()
        assert reg.get_limit("free", "storage_gb") == 2
        assert reg.get_limit("premium", "storage_gb") == 100

    def test_get_limit_unknown_plan(self):
        reg = QuotaRegistry()
        assert reg.get_limit("unknown", "storage_gb") == 0

    def test_register_new_dimension(self):
        reg = QuotaRegistry()
        reg.register_dimension("new_feature", "新功能", default_limits={"free": 0, "basic": 1, "premium": 5})
        dims = reg.list_dimensions()
        assert "new_feature" in dims
        assert dims["new_feature"] == "新功能"
        assert reg.get_limit("free", "new_feature") == 0
        assert reg.get_limit("basic", "new_feature") == 1
        assert reg.get_limit("premium", "new_feature") == 5

    def test_register_dimension_idempotent(self):
        reg = QuotaRegistry()
        reg.register_dimension("storage_gb", "should not change", default_limits={"free": 999})
        # 已经存在的不覆盖
        assert reg.get_limit("free", "storage_gb") == 2

    def test_register_without_defaults(self):
        reg = QuotaRegistry()
        reg.register_dimension("new_dim", "描述")
        assert reg.get_limit("free", "new_dim") == 0
        assert reg.get_limit("basic", "new_dim") == 0
        assert reg.get_limit("premium", "new_dim") == 0

    def test_register_partial_limits(self):
        reg = QuotaRegistry()
        reg.register_dimension("partial", "partial", default_limits={"premium": 42})
        assert reg.get_limit("free", "partial") == 0  # 未设置的保持 0
        assert reg.get_limit("premium", "partial") == 42


class TestQuotaChecker:
    def test_check_within_limit(self):
        checker = QuotaChecker()
        result = checker.check("free", "storage_gb", 1)
        assert result.allowed is True
        assert result.limit == 2
        assert result.used == 1
        assert result.remaining == 1
        assert result.dimension == "storage_gb"

    def test_check_exceeded(self):
        checker = QuotaChecker()
        result = checker.check("free", "storage_gb", 3)
        assert result.allowed is False
        assert result.remaining == 0
        assert result.warning_level == "exceeded"

    def test_check_exact_limit_not_allowed(self):
        # used < limit 才 allowed，等于不算
        checker = QuotaChecker()
        result = checker.check("free", "storage_gb", 2)
        assert result.allowed is False
        assert result.remaining == 0

    def test_check_unlimited(self):
        checker = QuotaChecker()
        result = checker.check("premium", "max_templates", 999999)
        assert result.allowed is True
        assert result.remaining == float("inf")
        assert result.warning_level == "normal"

    def test_check_warning_level_normal(self):
        checker = QuotaChecker()
        result = checker.check("free", "storage_gb", 1)  # 50%
        assert result.warning_level == "normal"

    def test_check_warning_level_warning(self):
        checker = QuotaChecker()
        # 80% <= used < 95%
        result = checker.check("free", "max_templates", 2.5)  # 2.5/3 = 83%
        assert result.warning_level == "warning"

    def test_check_warning_level_critical(self):
        checker = QuotaChecker()
        # 95% <= used < 100%
        result = checker.check("free", "max_templates", 2.9)  # 2.9/3 = 97%
        assert result.warning_level == "critical"

    def test_check_warning_level_exceeded(self):
        checker = QuotaChecker()
        result = checker.check("free", "storage_gb", 5)  # 250%
        assert result.warning_level == "exceeded"

    def test_check_multiple(self):
        checker = QuotaChecker()
        results = checker.check_multiple(
            "free",
            {"storage_gb": 1, "max_templates": 2, "max_titles": 10},
        )
        assert len(results) == 3
        assert results[0].dimension == "storage_gb"
        assert results[1].dimension == "max_templates"
        assert results[2].dimension == "max_titles"
        assert all(r.allowed for r in results)

    def test_check_zero_limit(self):
        checker = QuotaChecker()
        result = checker.check("free", "ai_voice_enabled", 0)
        # limit=0, used=0: used < limit 为 False → allowed=False
        assert result.allowed is False
        assert result.remaining == 0
        assert result.warning_level == "normal"

    def test_compute_warning_level_normal(self):
        assert QuotaChecker._compute_warning_level(50, 100) == "normal"
        assert QuotaChecker._compute_warning_level(79, 100) == "normal"

    def test_compute_warning_level_warning_boundary(self):
        assert QuotaChecker._compute_warning_level(80, 100) == "warning"
        assert QuotaChecker._compute_warning_level(94, 100) == "warning"

    def test_compute_warning_level_critical_boundary(self):
        assert QuotaChecker._compute_warning_level(95, 100) == "critical"
        assert QuotaChecker._compute_warning_level(99, 100) == "critical"

    def test_compute_warning_level_exceeded(self):
        assert QuotaChecker._compute_warning_level(100, 100) == "exceeded"
        assert QuotaChecker._compute_warning_level(150, 100) == "exceeded"

    def test_compute_warning_level_unlimited(self):
        assert QuotaChecker._compute_warning_level(9999, float("inf")) == "normal"

    def test_compute_warning_level_zero_limit_with_usage(self):
        assert QuotaChecker._compute_warning_level(1, 0) == "exceeded"

    def test_compute_warning_level_zero_limit_no_usage(self):
        assert QuotaChecker._compute_warning_level(0, 0) == "normal"

    def test_compute_warning_level_negative_limit(self):
        # limit <= 0 且 used=0 → NORMAL
        assert QuotaChecker._compute_warning_level(0, -1) == "normal"


class TestGetWarningLevel:
    def test_convenience_function(self):
        assert get_warning_level(50, 100) == "normal"
        assert get_warning_level(99, 100) == "critical"
        assert get_warning_level(100, 100) == "exceeded"
        assert get_warning_level(0, 0) == "normal"
        assert get_warning_level(1, 0) == "exceeded"


class TestGlobalSingletons:
    def test_quota_registry_is_instance(self):
        assert isinstance(quota_registry, QuotaRegistry)

    def test_quota_checker_is_instance(self):
        assert isinstance(quota_checker, QuotaChecker)

    def test_global_checker_works(self):
        result = quota_checker.check("free", "storage_gb", 1)
        assert result.allowed is True
