"""TTS 配音配置领域模型单元测试."""

from __future__ import annotations

from packages.domain.tts_config import TtsConfig


class TestTtsConfigDefaults:
    """默认值测试."""

    def test_default_values(self):
        config = TtsConfig()
        assert config.enabled is False
        assert config.voice_id == ""
        assert config.speed == 1.0
        assert config.pitch == 0.0
        assert config.volume == 0.8
        assert config.text == ""
        assert config.align_mode == "full"
        assert config.overlap_mode == "replace"


class TestTtsConfigParse:
    """parse 方法测试."""

    def test_parse_none_returns_default(self):
        config = TtsConfig.parse(None)
        assert config.enabled is False

    def test_parse_empty_dict_returns_default(self):
        config = TtsConfig.parse({})
        assert config.enabled is False

    def test_parse_not_dict_returns_default(self):
        config = TtsConfig.parse("invalid")
        assert config.enabled is False
        config2 = TtsConfig.parse(123)
        assert config2.enabled is False
        config3 = TtsConfig.parse([])
        assert config3.enabled is False

    def test_parse_enabled_false_ignores_other_fields(self):
        data = {
            "enabled": False,
            "voice_id": "test_voice",
            "speed": 2.0,
            "text": "hello",
        }
        config = TtsConfig.parse(data)
        assert config.enabled is False
        assert config.voice_id == ""
        assert config.speed == 1.0

    def test_parse_basic_enabled(self):
        data = {"enabled": True, "voice_id": "voice_001"}
        config = TtsConfig.parse(data)
        assert config.enabled is True
        assert config.voice_id == "voice_001"
        assert config.speed == 1.0
        assert config.pitch == 0.0
        assert config.volume == 0.8

    def test_parse_full_config(self):
        data = {
            "enabled": True,
            "voice_id": "voice_001",
            "speed": 1.5,
            "pitch": 2.0,
            "volume": 0.9,
            "text": "测试配音文本",
            "align_mode": "subtitle",
            "overlap_mode": "mix",
        }
        config = TtsConfig.parse(data)
        assert config.enabled is True
        assert config.voice_id == "voice_001"
        assert config.speed == 1.5
        assert config.pitch == 2.0
        assert config.volume == 0.9
        assert config.text == "测试配音文本"
        assert config.align_mode == "subtitle"
        assert config.overlap_mode == "mix"

    def test_parse_enabled_non_bool_fallback(self):
        data = {"enabled": "true", "voice_id": "v1"}
        config = TtsConfig.parse(data)
        assert config.enabled is False

    def test_parse_voice_id_non_string_fallback(self):
        data = {"enabled": True, "voice_id": 123}
        config = TtsConfig.parse(data)
        assert config.voice_id == ""

    def test_parse_speed_non_numeric_fallback(self):
        data = {"enabled": True, "speed": "fast"}
        config = TtsConfig.parse(data)
        assert config.speed == 1.0

    def test_parse_pitch_non_numeric_fallback(self):
        data = {"enabled": True, "pitch": "high"}
        config = TtsConfig.parse(data)
        assert config.pitch == 0.0

    def test_parse_volume_non_numeric_fallback(self):
        data = {"enabled": True, "volume": "loud"}
        config = TtsConfig.parse(data)
        assert config.volume == 0.8

    def test_parse_text_non_string_fallback(self):
        data = {"enabled": True, "text": 12345}
        config = TtsConfig.parse(data)
        assert config.text == ""

    def test_parse_align_mode_invalid_fallback(self):
        data = {"enabled": True, "align_mode": "invalid"}
        config = TtsConfig.parse(data)
        assert config.align_mode == "full"

    def test_parse_overlap_mode_invalid_fallback(self):
        data = {"enabled": True, "overlap_mode": "invalid"}
        config = TtsConfig.parse(data)
        assert config.overlap_mode == "replace"


class TestTtsConfigClamp:
    """边界钳制测试."""

    def test_speed_below_min_clamped(self):
        data = {"enabled": True, "speed": 0.1}
        config = TtsConfig.parse(data)
        assert config.speed == 0.5

    def test_speed_above_max_clamped(self):
        data = {"enabled": True, "speed": 3.0}
        config = TtsConfig.parse(data)
        assert config.speed == 2.0

    def test_speed_at_min_ok(self):
        data = {"enabled": True, "speed": 0.5}
        config = TtsConfig.parse(data)
        assert config.speed == 0.5

    def test_speed_at_max_ok(self):
        data = {"enabled": True, "speed": 2.0}
        config = TtsConfig.parse(data)
        assert config.speed == 2.0

    def test_pitch_below_min_clamped(self):
        data = {"enabled": True, "pitch": -20}
        config = TtsConfig.parse(data)
        assert config.pitch == -12

    def test_pitch_above_max_clamped(self):
        data = {"enabled": True, "pitch": 20}
        config = TtsConfig.parse(data)
        assert config.pitch == 12

    def test_pitch_at_min_ok(self):
        data = {"enabled": True, "pitch": -12}
        config = TtsConfig.parse(data)
        assert config.pitch == -12

    def test_pitch_at_max_ok(self):
        data = {"enabled": True, "pitch": 12}
        config = TtsConfig.parse(data)
        assert config.pitch == 12

    def test_volume_below_min_clamped(self):
        data = {"enabled": True, "volume": -0.5}
        config = TtsConfig.parse(data)
        assert config.volume == 0.0

    def test_volume_above_max_clamped(self):
        data = {"enabled": True, "volume": 2.0}
        config = TtsConfig.parse(data)
        assert config.volume == 1.0

    def test_volume_at_min_ok(self):
        data = {"enabled": True, "volume": 0.0}
        config = TtsConfig.parse(data)
        assert config.volume == 0.0

    def test_volume_at_max_ok(self):
        data = {"enabled": True, "volume": 1.0}
        config = TtsConfig.parse(data)
        assert config.volume == 1.0

    def test_int_speed_converted_to_float(self):
        data = {"enabled": True, "speed": 1}
        config = TtsConfig.parse(data)
        assert isinstance(config.speed, float)
        assert config.speed == 1.0

    def test_int_pitch_converted_to_float(self):
        data = {"enabled": True, "pitch": 5}
        config = TtsConfig.parse(data)
        assert isinstance(config.pitch, float)
        assert config.pitch == 5.0

    def test_int_volume_converted_to_float(self):
        data = {"enabled": True, "volume": 1}
        config = TtsConfig.parse(data)
        assert isinstance(config.volume, float)
        assert config.volume == 1.0


class TestTtsConfigTextEdge:
    """文本字段边界测试."""

    def test_long_text_preserved(self):
        long_text = "配音文本" * 500
        data = {"enabled": True, "voice_id": "v1", "text": long_text}
        config = TtsConfig.parse(data)
        assert config.text == long_text
        assert len(config.text) == 2000

    def test_unicode_text_preserved(self):
        data = {"enabled": True, "voice_id": "v1", "text": "こんにちは世界🎵"}
        config = TtsConfig.parse(data)
        assert config.text == "こんにちは世界🎵"

    def test_special_chars_text_preserved(self):
        data = {"enabled": True, "voice_id": "v1", "text": "line1\nline2\t tab <>&\"'"}
        config = TtsConfig.parse(data)
        assert config.text == "line1\nline2\t tab <>&\"'"

    def test_empty_text_ok(self):
        data = {"enabled": True, "voice_id": "v1", "text": ""}
        config = TtsConfig.parse(data)
        assert config.text == ""

    def test_text_none_fallback(self):
        data = {"enabled": True, "voice_id": "v1", "text": None}
        config = TtsConfig.parse(data)
        assert config.text == ""


class TestTtsConfigVoiceIdEdge:
    """voice_id 边界测试."""

    def test_very_long_voice_id_preserved(self):
        long_id = "voice_" + "x" * 200
        data = {"enabled": True, "voice_id": long_id}
        config = TtsConfig.parse(data)
        assert config.voice_id == long_id

    def test_voice_id_empty_string_ok(self):
        data = {"enabled": True, "voice_id": ""}
        config = TtsConfig.parse(data)
        assert config.voice_id == ""

    def test_voice_id_unicode_ok(self):
        data = {"enabled": True, "voice_id": "音色_测试_001"}
        config = TtsConfig.parse(data)
        assert config.voice_id == "音色_测试_001"


class TestTtsConfigClampEdge:
    """钳制边界附近值测试."""

    def test_speed_just_below_min_clamped(self):
        data = {"enabled": True, "speed": 0.499}
        config = TtsConfig.parse(data)
        assert config.speed == 0.5

    def test_speed_just_above_max_clamped(self):
        data = {"enabled": True, "speed": 2.001}
        config = TtsConfig.parse(data)
        assert config.speed == 2.0

    def test_pitch_just_below_min_clamped(self):
        data = {"enabled": True, "pitch": -12.1}
        config = TtsConfig.parse(data)
        assert config.pitch == -12

    def test_pitch_just_above_max_clamped(self):
        data = {"enabled": True, "pitch": 12.1}
        config = TtsConfig.parse(data)
        assert config.pitch == 12

    def test_volume_just_below_min_clamped(self):
        data = {"enabled": True, "volume": -0.001}
        config = TtsConfig.parse(data)
        assert config.volume == 0.0

    def test_volume_just_above_max_clamped(self):
        data = {"enabled": True, "volume": 1.001}
        config = TtsConfig.parse(data)
        assert config.volume == 1.0

    def test_direct_construct_clamp_speed(self):
        config = TtsConfig(enabled=True, speed=0.1)
        config._clamp()
        assert config.speed == 0.5

    def test_direct_construct_clamp_pitch_volume(self):
        config = TtsConfig(enabled=True, pitch=-20, volume=2.0)
        config._clamp()
        assert config.pitch == -12
        assert config.volume == 1.0


class TestTtsConfigAlignOverlapEdge:
    """对齐与叠加模式边界."""

    def test_align_mode_empty_string_fallback(self):
        data = {"enabled": True, "align_mode": ""}
        config = TtsConfig.parse(data)
        assert config.align_mode == "full"

    def test_overlap_mode_empty_string_fallback(self):
        data = {"enabled": True, "overlap_mode": ""}
        config = TtsConfig.parse(data)
        assert config.overlap_mode == "replace"

    def test_align_mode_case_sensitive(self):
        data = {"enabled": True, "align_mode": "SUBTITLE"}
        config = TtsConfig.parse(data)
        assert config.align_mode == "full"


class TestTtsConfigEquality:
    """相等性与独立性测试."""

    def test_same_config_equal(self):
        c1 = TtsConfig(enabled=True, voice_id="v1", speed=1.5)
        c2 = TtsConfig(enabled=True, voice_id="v1", speed=1.5)
        assert c1 == c2

    def test_different_config_not_equal(self):
        c1 = TtsConfig(enabled=True, voice_id="v1")
        c2 = TtsConfig(enabled=True, voice_id="v2")
        assert c1 != c2

    def test_modify_one_does_not_affect_other(self):
        c1 = TtsConfig(enabled=True, voice_id="v1")
        c2 = TtsConfig(enabled=True, voice_id="v1")
        c2.speed = 2.0
        assert c1.speed == 1.0
        assert c1 != c2
