"""
1080P 分辨率支持的计费逻辑测试 — 严格对齐用户三原则：
1. 不兜底/静默降级
2. 钱的计算绝对准确（纯官方公式）
3. 不隐藏 bug（非法组合 fail loud）

运行方式：
    cd backend && source venv/Scripts/activate && python -m pytest tests/test_1080p_billing.py -v
或 Django test runner：
    python manage.py test tests.test_1080p_billing
"""
import os
import sys
import django

# Django setup
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'config.settings')
sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), '..')))
django.setup()

import unittest
from utils.billing import (
    RESOLUTION_MAP,
    get_resolution,
    estimate_tokens,
    calculate_cost,
    calculate_base_cost,
)


class TestResolutionMap(unittest.TestCase):
    """验证 RESOLUTION_MAP 的 18 个组合像素值与官方文档一致。"""

    def test_1080p_pixels(self):
        # 来自 docs/API文档/创建视频生成任务API.md Seedance 2.0 & 2.0 fast 列
        self.assertEqual(get_resolution('16:9', '1080p'), (1920, 1080))
        self.assertEqual(get_resolution('9:16', '1080p'), (1080, 1920))
        self.assertEqual(get_resolution('4:3', '1080p'), (1664, 1248))
        self.assertEqual(get_resolution('1:1', '1080p'), (1440, 1440))
        self.assertEqual(get_resolution('3:4', '1080p'), (1248, 1664))
        # 21:9 特别注意：是 2206×946，不是 seedance 1.0 的 2176×928
        self.assertEqual(get_resolution('21:9', '1080p'), (2206, 946))

    def test_720p_pixels(self):
        self.assertEqual(get_resolution('16:9', '720p'), (1280, 720))
        self.assertEqual(get_resolution('21:9', '720p'), (1470, 630))

    def test_480p_pixels(self):
        self.assertEqual(get_resolution('16:9', '480p'), (864, 496))
        self.assertEqual(get_resolution('21:9', '480p'), (992, 432))

    def test_invalid_combo_raises(self):
        """原则 1：非法组合必须 fail loud，不静默降级。"""
        with self.assertRaises(KeyError):
            get_resolution('adaptive', '720p')  # adaptive 不在 map
        with self.assertRaises(KeyError):
            get_resolution('16:9', '4K')  # 不存在的 tier
        with self.assertRaises(KeyError):
            get_resolution('unknown', 'unknown')

    def test_tier_required(self):
        """tier 参数必填，不允许默认 720p 静默降级。"""
        with self.assertRaises(TypeError):
            get_resolution('16:9')  # type: ignore - 故意漏参数


class TestEstimateTokens(unittest.TestCase):
    """
    严格对齐官方公式：`(输入视频时长+输出时长) × 宽 × 高 × 帧率 / 1024`
    预估端不做最低 token 修正（那是火山计费侧逻辑）。
    """

    def test_formula_no_input_video(self):
        # 720P 16:9 (1280×720), 5s 输出, 24fps
        # 1280 × 720 × 24 × 5 / 1024 = 108000
        self.assertEqual(estimate_tokens(1280, 720, 5), 108000)

    def test_formula_with_input_video(self):
        # 720P 16:9, 5s 输出 + 5s 输入 = 10s 总时长
        # 1280 × 720 × 24 × 10 / 1024 = 216000
        self.assertEqual(estimate_tokens(1280, 720, 5, input_video_duration=5), 216000)

    def test_1080p_formula(self):
        # 1080P 16:9 (1920×1080), 5s 输出, 无输入视频
        # 1920 × 1080 × 24 × 5 / 1024 = 243000
        self.assertEqual(estimate_tokens(1920, 1080, 5), 243000)

    def test_1080p_with_input_video(self):
        # 1080P 16:9, 5s 输出 + 2s 输入 = 7s
        # 1920 × 1080 × 24 × 7 / 1024 = 340200
        self.assertEqual(estimate_tokens(1920, 1080, 5, input_video_duration=2), 340200)

    def test_no_silent_min_token_adjustment(self):
        """原则 2：预估端严格按公式，不做最低 token 修正。
        火山文档说 1080p 5s 输入含视频最低 437400 tokens，但那是火山计费侧的事，
        我们预估就老老实实按公式算 (5s+2s)×1920×1080×24/1024 = 340200，不擅自拉高。
        """
        # 1080p 5s 输出 + 2s 输入 = 7s 总时长
        # 公式值 340200，官方最低 437400
        # 我们应该返回公式值，不主动调到最低值
        result = estimate_tokens(1920, 1080, 5, input_video_duration=2)
        self.assertEqual(result, 340200, "预估端不应主动修正到火山最低 token")

    def test_float_input_duration(self):
        """输入视频时长可能是浮点数（前端 getMediaInfo 读取），要正确累加。"""
        # 720P 16:9, 5s 输出 + 3.5s 输入 = 8.5s
        # 1280 × 720 × 24 × 8.5 / 1024 = 183600
        self.assertEqual(estimate_tokens(1280, 720, 5, input_video_duration=3.5), 183600)


class TestGetTokenPrice(unittest.TestCase):
    """验证单价选择逻辑 — 4 种模型×视频组合 + 1080p 独立单价 + Fast+1080P fail loud。"""

    def setUp(self):
        # Mock QuotaConfig — 用官方文档默认值
        from types import SimpleNamespace
        from decimal import Decimal
        self.config = SimpleNamespace(
            base_token_price=Decimal('46'),
            base_token_price_video=Decimal('28'),
            base_token_price_fast=Decimal('37'),
            base_token_price_fast_video=Decimal('22'),
            base_token_price_1080p=Decimal('51'),
            base_token_price_1080p_video=Decimal('31'),
        )
        from apps.generation.views import _get_token_price
        self._get_token_price = _get_token_price

    def test_seedance_2_0_720p_no_video(self):
        """AirDrama 720P 不含视频 = 46 元/百万 tokens."""
        price = self._get_token_price(self.config, 'seedance_2.0', False, '720p')
        self.assertEqual(price, 46)

    def test_seedance_2_0_720p_with_video(self):
        """AirDrama 720P 含视频 = 28."""
        price = self._get_token_price(self.config, 'seedance_2.0', True, '720p')
        self.assertEqual(price, 28)

    def test_seedance_2_0_480p_same_as_720p(self):
        """480p 和 720p 共享同一单价（官方价格一致）。"""
        price_480 = self._get_token_price(self.config, 'seedance_2.0', False, '480p')
        price_720 = self._get_token_price(self.config, 'seedance_2.0', False, '720p')
        self.assertEqual(price_480, price_720)

    def test_seedance_2_0_1080p_no_video(self):
        """AirDrama 1080P 不含视频 = 51（独立单价，不是 720p 的 46）."""
        price = self._get_token_price(self.config, 'seedance_2.0', False, '1080p')
        self.assertEqual(price, 51)

    def test_seedance_2_0_1080p_with_video(self):
        """AirDrama 1080P 含视频 = 31（独立单价，不是 720p 的 28）."""
        price = self._get_token_price(self.config, 'seedance_2.0', True, '1080p')
        self.assertEqual(price, 31)

    def test_fast_720p_no_video(self):
        """Fast 720P 不含视频 = 37."""
        price = self._get_token_price(self.config, 'seedance_2.0_fast', False, '720p')
        self.assertEqual(price, 37)

    def test_fast_720p_with_video(self):
        """Fast 720P 含视频 = 22."""
        price = self._get_token_price(self.config, 'seedance_2.0_fast', True, '720p')
        self.assertEqual(price, 22)

    def test_fast_480p_uses_fast_price(self):
        """Fast 不分 480p/720p，都用 fast 单价。"""
        price_480 = self._get_token_price(self.config, 'seedance_2.0_fast', False, '480p')
        price_720 = self._get_token_price(self.config, 'seedance_2.0_fast', False, '720p')
        self.assertEqual(price_480, price_720)
        self.assertEqual(price_480, 37)

    def test_fast_1080p_raises_value_error(self):
        """原则 1 + 3：Fast + 1080P 必须 fail loud，不能静默按 720p 价（欺骗用户）。"""
        with self.assertRaises(ValueError) as ctx:
            self._get_token_price(self.config, 'seedance_2.0_fast', False, '1080p')
        self.assertIn('1080p', str(ctx.exception).lower())

        with self.assertRaises(ValueError):
            self._get_token_price(self.config, 'seedance_2.0_fast', True, '1080p')


class TestCalculateCost(unittest.TestCase):
    """验证扣费金额计算 = tokens × 单价 × (1 + 加价%)，精确到分."""

    def test_720p_cost_matches_official_example(self):
        """官方示例：720P 5s 16:9 = 4.97 元（无加价）."""
        # 720p 5s 公式值 108000 tokens
        tokens = estimate_tokens(1280, 720, 5)
        # 46 元/百万 × 108000 / 1000000 = 4.968 ≈ 4.97
        cost = calculate_cost(tokens, 46, 0)
        self.assertEqual(str(cost), '4.97')

    def test_1080p_no_video_cost(self):
        """1080P 5s 16:9 不含视频 = 1920×1080×24×5/1024 × 51 / 1000000 = 12.393 ≈ 12.39 元."""
        tokens = estimate_tokens(1920, 1080, 5)
        cost = calculate_cost(tokens, 51, 0)
        self.assertEqual(str(cost), '12.39')

    def test_markup_applied(self):
        """团队加价 20% 的情况。"""
        tokens = estimate_tokens(1280, 720, 5)  # 108000
        cost = calculate_cost(tokens, 46, 20)
        # 4.968 × 1.2 = 5.9616 → 5.96
        self.assertEqual(str(cost), '5.96')


if __name__ == '__main__':
    unittest.main(verbosity=2)
