"""voice_presets 配音音色预设单元测试."""

from __future__ import annotations

import pytest

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

# ── VoiceGender 枚举测试 ─────────────────────────────────────────────────────


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

    def test_male(self):
        """男声."""
        assert VoiceGender.MALE.value == "male"

    def test_female(self):
        """女声."""
        assert VoiceGender.FEMALE.value == "female"

    def test_child(self):
        """童声."""
        assert VoiceGender.CHILD.value == "child"

    def test_count(self):
        """共3种性别."""
        assert len(VoiceGender) == 3

    def test_is_str_enum(self):
        """可与字符串比较."""
        assert VoiceGender.FEMALE == "female"


# ── VoiceStyle 枚举测试 ──────────────────────────────────────────────────────


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

    def test_stable(self):
        """沉稳."""
        assert VoiceStyle.STABLE.value == "stable"

    def test_lively(self):
        """活泼."""
        assert VoiceStyle.LIVELY.value == "lively"

    def test_customer_service(self):
        """客服."""
        assert VoiceStyle.CUSTOMER_SERVICE.value == "customer_service"

    def test_narration(self):
        """旁白."""
        assert VoiceStyle.NARRATION.value == "narration"

    def test_news(self):
        """新闻."""
        assert VoiceStyle.NEWS.value == "news"

    def test_story(self):
        """故事."""
        assert VoiceStyle.STORY.value == "story"

    def test_count(self):
        """共6种风格."""
        assert len(VoiceStyle) == 6


# ── VoicePreset 数据类测试 ───────────────────────────────────────────────────


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

    def test_minimal_creation(self):
        """最小化创建（必填字段）."""
        v = VoicePreset(voice_id="v1", name="测试音色")
        assert v.voice_id == "v1"
        assert v.name == "测试音色"

    def test_default_values(self):
        """默认值正确."""
        v = VoicePreset(voice_id="v1", name="T")
        assert v.gender == VoiceGender.FEMALE
        assert v.style == VoiceStyle.NARRATION
        assert v.description == ""
        assert v.provider == "mock"
        assert v.provider_voice_id == ""
        assert v.default_speed == 1.0
        assert v.default_pitch == 0.0
        assert v.sample_rate == 22050
        assert v.language == "zh-CN"

    def test_full_creation(self):
        """完整字段创建."""
        v = VoicePreset(
            voice_id="voice_full",
            name="完整音色",
            gender=VoiceGender.MALE,
            style=VoiceStyle.NEWS,
            description="测试描述",
            provider="aliyun",
            provider_voice_id="ali_001",
            default_speed=0.9,
            default_pitch=1.5,
            sample_rate=44100,
            language="en-US",
        )
        assert v.voice_id == "voice_full"
        assert v.name == "完整音色"
        assert v.gender == VoiceGender.MALE
        assert v.style == VoiceStyle.NEWS
        assert v.description == "测试描述"
        assert v.provider == "aliyun"
        assert v.provider_voice_id == "ali_001"
        assert v.default_speed == 0.9
        assert v.default_pitch == 1.5
        assert v.sample_rate == 44100
        assert v.language == "en-US"


# ── MOCK_VOICES 列表测试 ─────────────────────────────────────────────────────


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

    def test_not_empty(self):
        """列表不为空."""
        assert len(MOCK_VOICES) > 0

    def test_count(self):
        """共8个Mock音色."""
        assert len(MOCK_VOICES) == 8

    def test_all_have_voice_id(self):
        """每个音色都有voice_id."""
        for v in MOCK_VOICES:
            assert v.voice_id, f"音色缺少voice_id: {v}"

    def test_all_have_name(self):
        """每个音色都有name."""
        for v in MOCK_VOICES:
            assert v.name, f"{v.voice_id} 缺少name"

    def test_voice_ids_unique(self):
        """voice_id唯一."""
        ids = [v.voice_id for v in MOCK_VOICES]
        assert len(ids) == len(set(ids)), f"重复ID: {[i for i in ids if ids.count(i) > 1]}"

    def test_all_mock_provider(self):
        """都是mock供应商."""
        for v in MOCK_VOICES:
            assert v.provider == "mock", f"{v.voice_id} provider不是mock"

    def test_all_chinese(self):
        """都是中文语言."""
        for v in MOCK_VOICES:
            assert v.language == "zh-CN"

    def test_sample_rate_consistent(self):
        """采样率一致为22050."""
        for v in MOCK_VOICES:
            assert v.sample_rate == 22050

    def test_gender_distribution(self):
        """性别分布：至少有男/女/童声."""
        genders = {v.gender for v in MOCK_VOICES}
        assert VoiceGender.MALE in genders
        assert VoiceGender.FEMALE in genders
        assert VoiceGender.CHILD in genders

    def test_style_coverage(self):
        """覆盖多种风格."""
        styles = {v.style for v in MOCK_VOICES}
        assert len(styles) >= 4

    def test_specific_voices_exist(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_default_speed_positive(self):
        """语速都大于0."""
        for v in MOCK_VOICES:
            assert v.default_speed > 0, f"{v.voice_id}语速<=0"

    def test_descriptions_not_empty(self):
        """都有描述."""
        for v in MOCK_VOICES:
            assert v.description, f"{v.voice_id}缺少描述"
            assert len(v.description) > 5


# ── get_voice 测试 ───────────────────────────────────────────────────────────


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

    def test_get_existing_voice(self):
        """获取存在的音色."""
        v = get_voice("female_warm")
        assert v is not None
        assert v.voice_id == "female_warm"
        assert v.name == "温暖女声"

    def test_get_male_stable(self):
        """获取沉稳男声."""
        v = get_voice("male_stable")
        assert v is not None
        assert v.gender == VoiceGender.MALE
        assert v.style == VoiceStyle.STABLE

    def test_get_nonexistent_voice(self):
        """获取不存在的音色返回None."""
        assert get_voice("nonexistent") is None

    def test_get_empty_string(self):
        """空字符串返回None."""
        assert get_voice("") is None

    def test_mock_provider(self):
        """指定mock provider."""
        v = get_voice("female_warm", provider="mock")
        assert v is not None
        assert v.provider == "mock"

    def test_other_provider_returns_none(self):
        """其他provider返回None."""
        assert get_voice("female_warm", provider="aliyun") is None
        assert get_voice("female_warm", provider="xunfei") is None

    def test_returns_same_instance(self):
        """返回同一个对象引用."""
        v1 = get_voice("female_warm")
        v2 = get_voice("female_warm")
        assert v1 is v2


# ── list_voices 测试 ─────────────────────────────────────────────────────────


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

    def test_no_filter_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
        assert all(v.gender == VoiceGender.FEMALE for v in result)

    def test_filter_by_gender_male(self):
        """按男性筛选."""
        result = list_voices(gender="male")
        assert len(result) > 0
        assert all(v.gender == VoiceGender.MALE for v in result)

    def test_filter_by_gender_child(self):
        """按童声筛选."""
        result = list_voices(gender="child")
        assert len(result) >= 1
        assert all(v.gender == VoiceGender.CHILD for v in result)

    def test_filter_by_invalid_gender(self):
        """无效性别返回空."""
        result = list_voices(gender="alien")
        assert result == []

    def test_filter_by_style_narration(self):
        """按旁白风格筛选."""
        result = list_voices(style="narration")
        assert len(result) > 0
        assert all(v.style == VoiceStyle.NARRATION for v in result)

    def test_filter_by_style_news(self):
        """按新闻风格筛选."""
        result = list_voices(style="news")
        assert len(result) >= 1
        assert all(v.style == VoiceStyle.NEWS for v in result)

    def test_filter_by_style_story(self):
        """按故事风格筛选."""
        result = list_voices(style="story")
        assert len(result) >= 1
        assert all(v.style == VoiceStyle.STORY for v in result)

    def test_filter_by_invalid_style(self):
        """无效风格返回空."""
        result = list_voices(style="unknown")
        assert result == []

    def test_filter_by_provider_mock(self):
        """mock provider返回全部."""
        result = list_voices(provider="mock")
        assert len(result) == len(MOCK_VOICES)

    def test_filter_by_provider_other(self):
        """其他provider返回空."""
        result = list_voices(provider="aliyun")
        assert result == []

    def test_keyword_search_name(self):
        """关键词搜索name."""
        result = list_voices(keyword="温暖")
        assert len(result) >= 1
        assert any(v.voice_id == "female_warm" for v in result)

    def test_keyword_search_description(self):
        """关键词搜索description."""
        result = list_voices(keyword="商务")
        assert len(result) >= 1
        assert any("商务" in v.description for v in result)

    def test_keyword_search_voice_id(self):
        """关键词搜索voice_id."""
        result = list_voices(keyword="male_stable")
        assert len(result) >= 1
        assert result[0].voice_id == "male_stable"

    def test_keyword_case_insensitive(self):
        """关键词不区分大小写（英文）."""
        r1 = list_voices(keyword="Female")
        r2 = list_voices(keyword="female")
        assert len(r1) == len(r2)

    def test_keyword_no_match(self):
        """无匹配关键词返回空."""
        result = list_voices(keyword="完全不存在的音色xyz")
        assert result == []

    def test_gender_and_style_combined(self):
        """性别+风格组合筛选."""
        result = list_voices(gender="female", style="story")
        assert len(result) >= 1
        assert all(v.gender == VoiceGender.FEMALE for v in result)
        assert all(v.style == VoiceStyle.STORY for v in result)

    def test_returns_new_list(self):
        """返回新列表，修改不影响原数据."""
        result = list_voices()
        result.clear()
        assert len(MOCK_VOICES) == 8

    def test_empty_keyword_returns_all(self):
        """空关键词返回全部."""
        result = list_voices(keyword="")
        assert len(result) == len(MOCK_VOICES)

    def test_none_keyword_returns_all(self):
        """None关键词返回全部."""
        result = list_voices(keyword=None)
        assert len(result) == len(MOCK_VOICES)


# ── get_default_voice 测试 ───────────────────────────────────────────────────


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

    def test_default_voice_exists(self):
        """默认音色存在."""
        v = get_default_voice()
        assert v is not None
        assert isinstance(v, VoicePreset)

    def test_default_is_first_mock(self):
        """默认音色是列表第一个."""
        v = get_default_voice()
        assert v is MOCK_VOICES[0]

    def test_default_is_female_warm(self):
        """默认是温暖女声."""
        v = get_default_voice()
        assert v.voice_id == "female_warm"
        assert v.name == "温暖女声"
        assert v.gender == VoiceGender.FEMALE

    def test_default_same_instance(self):
        """多次调用返回同一实例."""
        v1 = get_default_voice()
        v2 = get_default_voice()
        assert v1 is v2
