"""voice_presets 模块单元测试."""

import pytest
from domain.voice_presets import (
    MOCK_VOICES,
    VoiceGender,
    VoicePreset,
    VoiceStyle,
    get_default_voice,
    get_voice,
    list_voices,
)


class TestVoiceGender:
    """VoiceGender 枚举测试."""

    def test_values(self):
        assert VoiceGender.MALE == "male"
        assert VoiceGender.FEMALE == "female"
        assert VoiceGender.CHILD == "child"

    def test_is_str_enum(self):
        # StrEnum 在不同 Python 版本 str() 行为可能不同（3.11+ 返回值，旧版自定义 StrEnum 可能返回类名）
        # 用 value 比较更稳妥
        assert isinstance(VoiceGender.FEMALE, str)
        assert VoiceGender.MALE.value == "male"
        assert VoiceGender.FEMALE.value == "female"


class TestVoiceStyle:
    """VoiceStyle 枚举测试."""

    def test_values(self):
        assert VoiceStyle.STABLE == "stable"
        assert VoiceStyle.LIVELY == "lively"
        assert VoiceStyle.CUSTOMER_SERVICE == "customer_service"
        assert VoiceStyle.NARRATION == "narration"
        assert VoiceStyle.NEWS == "news"
        assert VoiceStyle.STORY == "story"


class TestVoicePreset:
    """VoicePreset 数据类测试."""

    def test_create_with_required_fields(self):
        v = VoicePreset(voice_id="test_001", name="测试音色")
        assert v.voice_id == "test_001"
        assert v.name == "测试音色"
        # 默认值
        assert v.gender == VoiceGender.FEMALE
        assert v.style == VoiceStyle.NARRATION
        assert v.provider == "mock"
        assert v.default_speed == 1.0
        assert v.default_pitch == 0.0
        assert v.sample_rate == 22050
        assert v.language == "zh-CN"

    def test_create_with_all_fields(self):
        v = VoicePreset(
            voice_id="male_news",
            name="新闻男声",
            gender=VoiceGender.MALE,
            style=VoiceStyle.NEWS,
            description="字正腔圆",
            provider="aliyun",
            provider_voice_id="zhiqiang",
            default_speed=0.9,
            default_pitch=1.0,
            sample_rate=16000,
            language="zh-CN",
        )
        assert v.gender == VoiceGender.MALE
        assert v.style == VoiceStyle.NEWS
        assert v.provider == "aliyun"
        assert v.default_speed == 0.9
        assert v.sample_rate == 16000

    def test_slots(self):
        """dataclass slots=True，不能添加新属性."""
        v = VoicePreset(voice_id="test", name="测试")
        with pytest.raises(AttributeError):
            v.new_field = "value"  # type: ignore[attr-defined]


class TestMockVoices:
    """MOCK_VOICES 预设列表测试."""

    def test_not_empty(self):
        assert len(MOCK_VOICES) > 0

    def test_all_have_unique_voice_id(self):
        ids = [v.voice_id for v in MOCK_VOICES]
        assert len(ids) == len(set(ids)), "voice_id 不能重复"

    def test_all_are_voice_preset_instances(self):
        for v in MOCK_VOICES:
            assert isinstance(v, VoicePreset)
            assert v.provider == "mock"

    def test_contains_expected_voices(self):
        ids = {v.voice_id for v in MOCK_VOICES}
        assert "female_warm" in ids
        assert "male_stable" in ids
        assert "female_lively" in ids
        assert "child_cute" in ids

    def test_voice_genders_coverage(self):
        genders = {v.gender for v in MOCK_VOICES}
        assert VoiceGender.FEMALE in genders
        assert VoiceGender.MALE in genders
        assert VoiceGender.CHILD in genders


class TestGetVoice:
    """get_voice 函数测试."""

    def test_existing_mock_voice(self):
        v = get_voice("female_warm")
        assert v is not None
        assert v.voice_id == "female_warm"
        assert v.name == "温暖女声"

    def test_nonexistent_voice(self):
        assert get_voice("nonexistent") is None

    def test_provider_mock(self):
        v = get_voice("male_stable", provider="mock")
        assert v is not None
        assert v.voice_id == "male_stable"

    def test_unknown_provider_returns_none(self):
        assert get_voice("female_warm", provider="aliyun") is None

    def test_empty_string_returns_none(self):
        assert get_voice("") is None


class TestListVoices:
    """list_voices 函数测试."""

    def test_no_filters_returns_all(self):
        result = list_voices()
        assert len(result) == len(MOCK_VOICES)

    def test_filter_by_gender_female(self):
        result = list_voices(gender="female")
        assert len(result) > 0
        for v in result:
            assert v.gender == VoiceGender.FEMALE

    def test_filter_by_gender_male(self):
        result = list_voices(gender="male")
        assert len(result) > 0
        for v in result:
            assert v.gender == VoiceGender.MALE

    def test_filter_by_gender_child(self):
        result = list_voices(gender="child")
        assert len(result) > 0
        for v in result:
            assert v.gender == VoiceGender.CHILD

    def test_filter_by_style(self):
        result = list_voices(style="story")
        assert len(result) > 0
        for v in result:
            assert v.style == VoiceStyle.STORY

    def test_filter_by_style_narration(self):
        result = list_voices(style="narration")
        assert len(result) >= 1

    def test_filter_by_unknown_provider_returns_empty(self):
        result = list_voices(provider="aliyun")
        assert result == []

    def test_filter_by_keyword_name(self):
        result = list_voices(keyword="男声")
        assert len(result) > 0
        for v in result:
            assert "男声" in v.name or "男声" in v.description

    def test_filter_by_keyword_description(self):
        result = list_voices(keyword="vlog")
        assert len(result) > 0

    def test_filter_by_keyword_voice_id(self):
        result = list_voices(keyword="female")
        assert len(result) > 0
        for v in result:
            assert "female" in v.voice_id.lower()

    def test_filter_by_keyword_case_insensitive(self):
        r1 = list_voices(keyword="FEMALE")
        r2 = list_voices(keyword="female")
        assert len(r1) == len(r2)

    def test_filter_keyword_no_match(self):
        result = list_voices(keyword="xyz_nonexistent_keyword")
        assert result == []

    def test_combined_filters_gender_and_style(self):
        result = list_voices(gender="female", style="story")
        assert len(result) > 0
        for v in result:
            assert v.gender == VoiceGender.FEMALE
            assert v.style == VoiceStyle.STORY

    def test_combined_filters_gender_and_keyword(self):
        result = list_voices(gender="male", keyword="新闻")
        assert len(result) > 0
        for v in result:
            assert v.gender == VoiceGender.MALE

    def test_combined_no_match(self):
        result = list_voices(gender="child", style="news")
        # 童声没有新闻风格
        assert result == []


class TestGetDefaultVoice:
    """get_default_voice 函数测试."""

    def test_returns_first_mock_voice(self):
        v = get_default_voice()
        assert v == MOCK_VOICES[0]
        assert v.voice_id == "female_warm"

    def test_returns_voice_preset(self):
        assert isinstance(get_default_voice(), VoicePreset)
