"""
积分 Service 单元测试
=====================

不依赖数据库，仅测试配置加载、计费计算、充值档位等纯函数逻辑。
"""

import math

import pytest

from app.services import point_service as ps


class TestPointsConfig:
    """积分配置加载"""

    def test_points_config_loaded(self) -> None:
        assert "script" in ps.POINTS_CONFIG
        assert "tts" in ps.POINTS_CONFIG
        assert "video" in ps.POINTS_CONFIG

    def test_get_chargeable_source_types(self) -> None:
        types = ps.get_chargeable_source_types()
        assert "script" in types
        assert "tts" in types
        # 免费业务不应出现在扣费列表
        assert "caption" not in types

    def test_get_recharge_options_returns_list(self) -> None:
        options = ps.get_recharge_options()
        assert isinstance(options, list)
        if options:
            assert "points" in options[0]


class TestCalculateCost:
    """后置计费计算"""

    def test_fixed_cost(self) -> None:
        assert ps._calculate_cost("script") == 5
        assert ps._calculate_cost("polish") == 1
        assert ps._calculate_cost("voice_clone") == 200

    def test_free_cost(self) -> None:
        free_types = [
            key
            for key, cfg in ps.POINTS_CONFIG.items()
            if not key.startswith("_") and cfg.get("mode") == "free"
        ]
        for source_type in free_types:
            assert ps._calculate_cost(source_type) == 0

    def test_unknown_source_type(self) -> None:
        with pytest.raises(ValueError, match="未知的消费类型"):
            ps._calculate_cost("not_exists")

    def test_tts_duration_cost(self) -> None:
        # tts: divisor=5，按 5 秒为单位计费
        assert ps._calculate_cost("tts", {"seconds": 4}) == 1
        assert ps._calculate_cost("tts", {"seconds": 5}) == 1
        assert ps._calculate_cost("tts", {"seconds": 6}) == 2
        assert ps._calculate_cost("tts", {"seconds": 5.1}) == 2

    def test_video_duration_cost(self) -> None:
        # video: multiplier=1，按秒向上取整
        assert ps._calculate_cost("video", {"seconds": 10}) == 10
        assert ps._calculate_cost("video", {"seconds": 10.2}) == 11


class TestEstimateMaxCost:
    """预估上限计算"""

    def test_tts_estimate_by_char_count(self) -> None:
        cfg = ps.POINTS_CONFIG["tts"]
        seconds_per_char = cfg["estimation"]["seconds_per_char"]
        char_count = 100
        estimated_seconds = char_count * seconds_per_char
        expected = max(
            cfg["min_points"],
            math.ceil(estimated_seconds / cfg["divisor"]),
        )
        result = ps._estimate_max_cost("tts", {"char_count": char_count})
        assert result == expected

    def test_video_estimate_by_input_seconds(self) -> None:
        with pytest.raises(ValueError, match="input_seconds"):
            ps._estimate_max_cost("video", {})

        assert ps._estimate_max_cost("video", {"input_seconds": 10}) == 10
        assert ps._estimate_max_cost("video", {"input_seconds": 10.2}) == 11

    def test_unknown_source_type(self) -> None:
        with pytest.raises(ValueError, match="未知的消费类型"):
            ps._estimate_max_cost("not_exists")
