"""transition_presets 单元测试 - wave168

覆盖：
- TransitionPreset 数据类（frozen/默认值/字段）
- TRANSITION_PRESET_LIBRARY 预设库（数量/分类/ID唯一性）
- get_transition_preset 按ID获取
- list_transition_presets 筛选列表（分类/关键词）
- get_default_transition 默认转场
"""

import dataclasses

import pytest

from packages.domain.transition_presets import (
    TRANSITION_PRESET_LIBRARY,
    TransitionPreset,
    get_default_transition,
    get_transition_preset,
    list_transition_presets,
)

# ============================================================
# TransitionPreset 数据类
# ============================================================


class TestTransitionPresetDataclass:
    def test_minimal_creation(self):
        p = TransitionPreset(id="test", name="Test", category="basic")
        assert p.id == "test"
        assert p.name == "Test"
        assert p.category == "basic"

    def test_default_values(self):
        p = TransitionPreset(id="t", name="T", category="basic")
        assert p.description == ""
        assert p.tags == []
        assert p.transition == "fade"
        assert p.default_duration == 0.5
        assert p.min_duration == 0.1
        assert p.max_duration == 3.0
        assert p.has_custom_params is False

    def test_full_creation(self):
        p = TransitionPreset(
            id="full",
            name="Full",
            category="fade",
            description="test desc",
            tags=["tag1", "tag2"],
            transition="fadeblack",
            default_duration=1.0,
            min_duration=0.2,
            max_duration=5.0,
            has_custom_params=True,
        )
        assert p.description == "test desc"
        assert p.tags == ["tag1", "tag2"]
        assert p.transition == "fadeblack"
        assert p.default_duration == 1.0
        assert p.min_duration == 0.2
        assert p.max_duration == 5.0
        assert p.has_custom_params is True

    def test_frozen_immutable(self):
        p = TransitionPreset(id="t", name="T", category="basic")
        with pytest.raises(dataclasses.FrozenInstanceError):
            p.name = "Changed"

    def test_tags_default_new_list(self):
        p1 = TransitionPreset(id="t1", name="T1", category="basic")
        p2 = TransitionPreset(id="t2", name="T2", category="basic")
        assert p1.tags is not p2.tags


# ============================================================
# TRANSITION_PRESET_LIBRARY 预设库
# ============================================================


class TestTransitionPresetLibrary:
    def test_library_not_empty(self):
        assert len(TRANSITION_PRESET_LIBRARY) > 0

    def test_all_ids_unique(self):
        ids = [p.id for p in TRANSITION_PRESET_LIBRARY]
        assert len(ids) == len(set(ids))

    def test_all_have_required_fields(self):
        for p in TRANSITION_PRESET_LIBRARY:
            assert p.id, f"缺少id: {p}"
            assert p.name, f"缺少name: {p.id}"
            assert p.category, f"缺少category: {p.id}"
            assert p.transition, f"缺少transition: {p.id}"

    def test_valid_categories(self):
        valid = {"basic", "fade", "slide", "zoom", "warp", "special"}
        for p in TRANSITION_PRESET_LIBRARY:
            assert p.category in valid, f"无效分类: {p.id} -> {p.category}"

    def test_basic_category_exists(self):
        basics = [p for p in TRANSITION_PRESET_LIBRARY if p.category == "basic"]
        assert len(basics) >= 1

    def test_fade_category_exists(self):
        fades = [p for p in TRANSITION_PRESET_LIBRARY if p.category == "fade"]
        assert len(fades) >= 1

    def test_transition_none_exists(self):
        none_p = get_transition_preset("transition_none")
        assert none_p is not None
        assert none_p.transition == "none"
        assert none_p.default_duration == 0.0

    def test_duration_consistency(self):
        # min <= default <= max
        for p in TRANSITION_PRESET_LIBRARY:
            assert p.min_duration <= p.default_duration, f"{p.id}: min > default"
            assert p.default_duration <= p.max_duration, f"{p.id}: default > max"

    def test_total_count(self):
        # 至少有10个转场预设
        assert len(TRANSITION_PRESET_LIBRARY) >= 10


# ============================================================
# get_transition_preset
# ============================================================


class TestGetTransitionPreset:
    def test_existing_id(self):
        p = get_transition_preset("transition_none")
        assert p is not None
        assert p.id == "transition_none"
        assert p.name == "无转场"

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

    def test_empty_id(self):
        assert get_transition_preset("") is None

    def test_case_sensitive(self):
        assert get_transition_preset("Transition_None") is None

    def test_returns_preset_object(self):
        p = get_transition_preset("transition_none")
        assert isinstance(p, TransitionPreset)


# ============================================================
# list_transition_presets
# ============================================================


class TestListTransitionPresets:
    def test_no_filters_returns_all(self):
        result = list_transition_presets()
        assert len(result) == len(TRANSITION_PRESET_LIBRARY)

    def test_filter_by_category_basic(self):
        result = list_transition_presets(category="basic")
        assert len(result) > 0
        for p in result:
            assert p.category == "basic"

    def test_filter_by_category_fade(self):
        result = list_transition_presets(category="fade")
        for p in result:
            assert p.category == "fade"

    def test_filter_by_category_slide(self):
        result = list_transition_presets(category="slide")
        for p in result:
            assert p.category == "slide"

    def test_invalid_category_returns_empty(self):
        result = list_transition_presets(category="nonexistent")
        assert result == []

    def test_keyword_search_name(self):
        result = list_transition_presets(keyword="淡")
        assert len(result) >= 1
        names = [p.name for p in result]
        assert any("淡" in n for n in names)

    def test_keyword_search_tags(self):
        result = list_transition_presets(keyword="硬切")
        assert len(result) >= 1
        found = any(any("硬切" in t for t in p.tags) or "硬切" in p.name for p in result)
        assert found

    def test_keyword_search_description(self):
        # 搜索描述中的关键词
        result = list_transition_presets(keyword="过渡")
        assert isinstance(result, list)

    def test_keyword_nonexistent_returns_empty(self):
        result = list_transition_presets(keyword="zzz不存在的关键词zzz")
        assert result == []

    def test_category_and_keyword_combined(self):
        result = list_transition_presets(category="fade", keyword="淡")
        for p in result:
            assert p.category == "fade"
            assert "淡" in p.name or "淡" in p.description or any("淡" in t for t in p.tags)

    def test_keyword_empty_returns_all(self):
        result = list_transition_presets(keyword="")
        assert len(result) == len(TRANSITION_PRESET_LIBRARY)

    def test_returns_list_of_presets(self):
        result = list_transition_presets()
        for p in result:
            assert isinstance(p, TransitionPreset)


# ============================================================
# get_default_transition
# ============================================================


class TestGetDefaultTransition:
    def test_returns_preset(self):
        p = get_default_transition()
        assert isinstance(p, TransitionPreset)

    def test_default_is_none(self):
        p = get_default_transition()
        assert p.id == "transition_none"

    def test_default_has_zero_duration(self):
        p = get_default_transition()
        assert p.default_duration == 0.0

    def test_default_is_hard_cut(self):
        p = get_default_transition()
        assert p.transition == "none"
