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

from dataclasses import FrozenInstanceError

import pytest
from domain.preset_bgm import (
    BGM_STYLES,
    PRESET_BGM_LIBRARY,
    PresetBGM,
    get_preset_bgm,
    list_preset_bgm_by_style,
    search_preset_bgm,
)


class TestPresetBGM:
    """PresetBGM 数据类测试."""

    def test_create_required_fields(self):
        bgm = PresetBGM(id="test_001", name="测试音乐", style="upbeat", duration=120.0)
        assert bgm.id == "test_001"
        assert bgm.name == "测试音乐"
        assert bgm.style == "upbeat"
        assert bgm.duration == 120.0
        # 默认值
        assert bgm.artist == ""
        assert bgm.description == ""
        assert bgm.tags == []
        assert bgm.audio_url == ""

    def test_create_all_fields(self):
        bgm = PresetBGM(
            id="test_002",
            name="完整版",
            style="relax",
            duration=180.5,
            artist="测试艺术家",
            description="测试描述",
            tags=["标签1", "标签2"],
            audio_url="https://example.com/test.mp3",
        )
        assert bgm.artist == "测试艺术家"
        assert bgm.description == "测试描述"
        assert bgm.tags == ["标签1", "标签2"]
        assert bgm.audio_url == "https://example.com/test.mp3"

    def test_frozen_immutable(self):
        """frozen=True，实例不可变."""
        bgm = PresetBGM(id="test", name="测试", style="upbeat", duration=60.0)
        with pytest.raises(FrozenInstanceError):
            bgm.name = "修改"  # type: ignore[misc]

    def test_tags_default_new_list(self):
        """每次创建都有独立的 tags 列表."""
        b1 = PresetBGM(id="1", name="a", style="upbeat", duration=60.0)
        b2 = PresetBGM(id="2", name="b", style="upbeat", duration=60.0)
        assert b1.tags is not b2.tags
        assert b1.tags == []
        assert b2.tags == []


class TestPresetBGMLibrary:
    """PRESET_BGM_LIBRARY 预设库测试."""

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

    def test_all_unique_ids(self):
        ids = [b.id for b in PRESET_BGM_LIBRARY]
        assert len(ids) == len(set(ids)), "BGM ID 不能重复"

    def test_all_are_preset_bgm_instances(self):
        for bgm in PRESET_BGM_LIBRARY:
            assert isinstance(bgm, PresetBGM)

    def test_all_have_positive_duration(self):
        for bgm in PRESET_BGM_LIBRARY:
            assert bgm.duration > 0, f"{bgm.id} duration 必须为正"

    def test_styles_are_known(self):
        for bgm in PRESET_BGM_LIBRARY:
            assert bgm.style in BGM_STYLES, f"{bgm.id} style {bgm.style} 不在 BGM_STYLES 中"

    def test_style_distribution(self):
        """每种风格至少有 1 个 BGM."""
        styles_found = {b.style for b in PRESET_BGM_LIBRARY}
        for style in ["upbeat", "relax", "tech", "commerce"]:
            assert style in styles_found


class TestBGMStyles:
    """BGM_STYLES 风格字典测试."""

    def test_has_expected_styles(self):
        assert "upbeat" in BGM_STYLES
        assert "relax" in BGM_STYLES
        assert "tech" in BGM_STYLES
        assert "commerce" in BGM_STYLES
        assert "emotional" in BGM_STYLES
        assert "cinematic" in BGM_STYLES

    def test_values_are_chinese_labels(self):
        assert BGM_STYLES["upbeat"] == "轻快"
        assert BGM_STYLES["relax"] == "治愈"


class TestGetPresetBGM:
    """get_preset_bgm 函数测试."""

    def test_existing_id(self):
        bgm = get_preset_bgm("bgm_upbeat_001")
        assert bgm is not None
        assert bgm.id == "bgm_upbeat_001"
        assert bgm.name == "阳光清晨"
        assert bgm.style == "upbeat"

    def test_nonexistent_id(self):
        assert get_preset_bgm("nonexistent") is None

    def test_empty_string(self):
        assert get_preset_bgm("") is None

    def test_returns_preset_bgm_instance(self):
        bgm = get_preset_bgm("bgm_relax_001")
        assert isinstance(bgm, PresetBGM)


class TestListPresetBGMByStyle:
    """list_preset_bgm_by_style 函数测试."""

    def test_upbeat_style(self):
        result = list_preset_bgm_by_style("upbeat")
        assert len(result) >= 3
        for bgm in result:
            assert bgm.style == "upbeat"

    def test_relax_style(self):
        result = list_preset_bgm_by_style("relax")
        assert len(result) >= 3
        for bgm in result:
            assert bgm.style == "relax"

    def test_tech_style(self):
        result = list_preset_bgm_by_style("tech")
        assert len(result) >= 2

    def test_unknown_style_returns_empty(self):
        result = list_preset_bgm_by_style("nonexistent_style")
        assert result == []

    def test_empty_style_returns_empty(self):
        result = list_preset_bgm_by_style("")
        assert result == []


class TestSearchPresetBGM:
    """search_preset_bgm 函数测试."""

    def test_search_by_name(self):
        result = search_preset_bgm("阳光")
        assert len(result) >= 1
        assert any(b.name == "阳光清晨" for b in result)

    def test_search_by_tag(self):
        result = search_preset_bgm("钢琴")
        assert len(result) >= 1
        for bgm in result:
            assert any("钢琴" in tag for tag in bgm.tags) or "钢琴" in bgm.name or "钢琴" in bgm.description

    def test_search_by_description(self):
        result = search_preset_bgm("vlog")
        assert len(result) >= 1

    def test_search_case_insensitive(self):
        r1 = search_preset_bgm("BGM")
        r2 = search_preset_bgm("bgm")
        assert len(r1) == len(r2)

    def test_search_no_match(self):
        result = search_preset_bgm("xyz_nonexistent_keyword_12345")
        assert result == []

    def test_search_empty_keyword_returns_all(self):
        """空关键词应该匹配所有（keyword in string 恒成立）."""
        result = search_preset_bgm("")
        assert len(result) == len(PRESET_BGM_LIBRARY)

    def test_search_partial_match(self):
        result = search_preset_bgm("科技")
        assert len(result) >= 1
