"""domain层小模块批量单测.

覆盖：recipe / tag / editing_mode / voice_library / title_library / template / edit_template
共 7 个模块，纯逻辑 0 外部依赖。
"""

from __future__ import annotations

from packages.domain.edit_template import EditTemplate, EditTemplateStatus
from packages.domain.editing_mode import EditingMode
from packages.domain.recipe import Recipe, RecipeItem
from packages.domain.tag import Tag
from packages.domain.template import Template, TemplateCategory, TemplateSegment
from packages.domain.title_library import TitleLibraryItem
from packages.domain.voice_library import VoiceLibraryItem


class TestEditingMode:
    """EditingMode 枚举测试."""

    def test_four_modes(self):
        """四种剪辑模式."""
        assert len(EditingMode) == 4

    def test_one_take(self):
        assert EditingMode.ONE_TAKE == "one_take"

    def test_pip(self):
        assert EditingMode.PIP == "pip"

    def test_voice_over(self):
        assert EditingMode.VOICE_OVER == "voice_over"

    def test_voice_pip(self):
        assert EditingMode.VOICE_PIP == "voice_pip"

    def test_all_values_unique(self):
        values = [m.value for m in EditingMode]
        assert len(values) == len(set(values))


class TestTag:
    """Tag 测试."""

    def test_create_valid(self):
        """正常创建."""
        tag = Tag.create(user_id="u1", name="  搞笑  ")
        assert tag.user_id == "u1"
        assert tag.name == "搞笑"
        assert isinstance(tag.id, str)
        assert len(tag.id) > 0
        assert tag.created_at is not None

    def test_create_empty_name(self):
        """空名称无效."""
        try:
            Tag.create("u1", "")
            assert False
        except ValueError as e:
            assert "不能为空" in str(e)

    def test_create_whitespace_name(self):
        """纯空白名称无效."""
        try:
            Tag.create("u1", "   ")
            assert False
        except ValueError as e:
            assert "不能为空" in str(e)

    def test_create_unique_id(self):
        t1 = Tag.create("u", "t1")
        t2 = Tag.create("u", "t2")
        assert t1.id != t2.id


class TestRecipeItem:
    """RecipeItem 测试."""

    def test_create_asset_item(self):
        """素材配方项."""
        item = RecipeItem(
            id="item1",
            recipe_id="r1",
            item_type="asset",
            item_id="a1",
            position=0,
        )
        assert item.item_type == "asset"
        assert item.item_id == "a1"
        assert item.position == 0

    def test_create_title_item(self):
        """标题配方项."""
        item = RecipeItem(id="i2", recipe_id="r1", item_type="title", item_id="t1", position=1)
        assert item.item_type == "title"
        assert item.position == 1

    def test_default_metadata(self):
        """默认 metadata 为空 dict."""
        item = RecipeItem(id="i1", recipe_id="r1", item_type="voice", item_id="v1")
        assert item.metadata_ == {}


class TestRecipe:
    """Recipe 测试."""

    def test_create_minimal(self):
        """最简配方."""
        recipe = Recipe(id="r1", user_id="u1", name="我的配方")
        assert recipe.name == "我的配方"
        assert recipe.description == ""
        assert recipe.template_id == ""
        assert recipe.generation_params == {}
        assert recipe.items == []
        assert recipe.is_active is True
        assert recipe.created_at is not None
        assert recipe.updated_at is not None

    def test_create_with_items(self):
        """带配方项."""
        items = [
            RecipeItem(id="i1", recipe_id="r1", item_type="asset", item_id="a1", position=0),
            RecipeItem(id="i2", recipe_id="r1", item_type="title", item_id="t1", position=1),
        ]
        recipe = Recipe(id="r1", user_id="u1", name="配方", items=items)
        assert len(recipe.items) == 2
        assert recipe.items[0].item_type == "asset"
        assert recipe.items[1].position == 1

    def test_default_items_empty_list(self):
        """默认 items 为空列表."""
        r1 = Recipe(id="r1", user_id="u1", name="r1")
        r2 = Recipe(id="r2", user_id="u2", name="r2")
        r1.items.append("fake")
        assert r2.items == []


class TestVoiceLibraryItem:
    """VoiceLibraryItem 测试."""

    def test_create_minimal(self):
        """最简创建."""
        item = VoiceLibraryItem(id="v1", user_id="u1", name="我的配音")
        assert item.name == "我的配音"
        assert item.text == ""
        assert item.voice_provider == ""
        assert item.voice_id == ""
        assert item.voice_name == ""
        assert item.audio_url == ""
        assert item.duration == 0
        assert item.file_size == 0
        assert item.status == "completed"
        assert item.project_id is None
        assert item.tags == []
        assert item.metadata_ == {}

    def test_create_full(self):
        """带全部字段."""
        item = VoiceLibraryItem(
            id="v1",
            user_id="u1",
            name="旁白",
            text="大家好",
            voice_provider="xf",
            voice_id="v1",
            voice_name="小云",
            audio_url="http://x/a.mp3",
            duration=10.5,
            file_size=102400,
            status="processing",
            project_id="p1",
            tags=["旁白", "正式"],
        )
        assert item.text == "大家好"
        assert item.duration == 10.5
        assert item.file_size == 102400
        assert item.project_id == "p1"
        assert item.tags == ["旁白", "正式"]

    def test_tags_independent(self):
        """不同实例的 tags 独立."""
        i1 = VoiceLibraryItem(id="v1", user_id="u", name="n1")
        i2 = VoiceLibraryItem(id="v2", user_id="u", name="n2")
        i1.tags.append("x")
        assert i2.tags == []


class TestTitleLibraryItem:
    """TitleLibraryItem 测试."""

    def test_create_required(self):
        """必填字段."""
        item = TitleLibraryItem(id="t1", user_id="u1", name="爆款标题", text="这也太牛了")
        assert item.name == "爆款标题"
        assert item.text == "这也太牛了"
        assert item.category == "default"
        assert item.description == ""
        assert item.tags == []
        assert item.usage_count == 0
        assert item.is_active is True
        assert item.metadata_ == {}

    def test_create_full(self):
        """带全部字段."""
        item = TitleLibraryItem(
            id="t1",
            user_id="u1",
            name="科技标题",
            text="震惊！",
            category="tech",
            description="科技类标题",
            tags=["科技", "爆款"],
            usage_count=100,
            is_active=False,
        )
        assert item.category == "tech"
        assert item.usage_count == 100
        assert item.is_active is False
        assert item.tags == ["科技", "爆款"]

    def test_tags_independent(self):
        i1 = TitleLibraryItem(id="t1", user_id="u", name="n", text="t")
        i2 = TitleLibraryItem(id="t2", user_id="u", name="n", text="t")
        i1.tags.append("x")
        assert i2.tags == []


class TestTemplateSegment:
    """TemplateSegment 测试."""

    def test_create(self):
        """正常创建."""
        seg = TemplateSegment(
            id="s1",
            template_id="t1",
            segment_order=0,
            duration_min=2.0,
            duration_max=5.0,
        )
        assert seg.segment_order == 0
        assert seg.duration_min == 2.0
        assert seg.duration_max == 5.0
        assert seg.material_type is None

    def test_with_material_type(self):
        """带素材类型（voice_over模式）."""
        seg = TemplateSegment(
            id="s1",
            template_id="t1",
            segment_order=0,
            duration_min=3.0,
            duration_max=8.0,
            material_type="person",
        )
        assert seg.material_type == "person"

    def test_has_timestamps(self):
        seg = TemplateSegment(id="s1", template_id="t1", segment_order=0, duration_min=1, duration_max=2)
        assert seg.created_at is not None
        assert seg.updated_at is not None


class TestTemplate:
    """Template 测试."""

    def test_create_minimal(self):
        """最简模板."""
        tpl = Template(id="t1", user_id="u1", name="通用模板", mode="one_take")
        assert tpl.name == "通用模板"
        assert tpl.mode == "one_take"
        assert tpl.category == ""
        assert tpl.tags == []
        assert tpl.title_config == {}
        assert tpl.subtitle_config == {}
        assert tpl.bgm_config == {}
        assert tpl.estimated_duration == 0.0
        assert tpl.segments == []
        assert tpl.is_active is True

    def test_create_with_segments(self):
        """带片段."""
        segs = [
            TemplateSegment(id="s1", template_id="t1", segment_order=0, duration_min=2, duration_max=5),
            TemplateSegment(id="s2", template_id="t1", segment_order=1, duration_min=3, duration_max=7),
        ]
        tpl = Template(id="t1", user_id="u1", name="模板", mode="pip", segments=segs)
        assert len(tpl.segments) == 2
        assert tpl.segments[0].segment_order == 0

    def test_segments_independent(self):
        t1 = Template(id="t1", user_id="u", name="n1", mode="one_take")
        t2 = Template(id="t2", user_id="u", name="n2", mode="pip")
        t1.segments.append("fake")
        assert t2.segments == []


class TestTemplateCategory:
    """TemplateCategory 测试."""

    def test_create(self):
        cat = TemplateCategory(id="c1", user_id="u1", name="科技")
        assert cat.name == "科技"
        assert cat.created_at is not None


class TestEditTemplateStatus:
    """EditTemplateStatus 枚举测试."""

    def test_two_statuses(self):
        assert len(EditTemplateStatus) == 2

    def test_active(self):
        assert EditTemplateStatus.ACTIVE == "active"

    def test_inactive(self):
        assert EditTemplateStatus.INACTIVE == "inactive"


class TestEditTemplate:
    """EditTemplate 测试."""

    def test_create_minimal(self):
        """最简创建."""
        tpl = EditTemplate.create(name="  通用模板  ")
        assert tpl.name == "通用模板"
        assert tpl.description == ""
        assert tpl.template_type == "default"
        assert tpl.config == {}
        assert tpl.preview_url == ""
        assert tpl.sort_weight == 0
        assert tpl.status == EditTemplateStatus.ACTIVE
        assert isinstance(tpl.id, str)
        assert len(tpl.id) > 0

    def test_create_full(self):
        """带全部字段."""
        tpl = EditTemplate.create(
            name="口播模板",
            description="口播类模板",
            template_type="voice_over",
            config={"style": "formal"},
            preview_url="https://x/preview.mp4",
            sort_weight=100,
            status=EditTemplateStatus.INACTIVE,
        )
        assert tpl.name == "口播模板"
        assert tpl.description == "口播类模板"
        assert tpl.template_type == "voice_over"
        assert tpl.config == {"style": "formal"}
        assert tpl.preview_url == "https://x/preview.mp4"
        assert tpl.sort_weight == 100
        assert tpl.status == EditTemplateStatus.INACTIVE

    def test_create_empty_name(self):
        """空名称无效."""
        try:
            EditTemplate.create(name="")
            assert False
        except ValueError as e:
            assert "不能为空" in str(e)

    def test_create_whitespace_name(self):
        """空白名称无效."""
        try:
            EditTemplate.create(name="   ")
            assert False
        except ValueError as e:
            assert "不能为空" in str(e)

    def test_empty_template_type_defaults(self):
        """空 template_type 默认 default."""
        tpl = EditTemplate.create(name="t", template_type="")
        assert tpl.template_type == "default"

    def test_whitespace_template_type_defaults(self):
        """空白 template_type 默认 default."""
        tpl = EditTemplate.create(name="t", template_type="   ")
        assert tpl.template_type == "default"

    def test_config_none_defaults_empty(self):
        """config=None 默认为空 dict."""
        tpl = EditTemplate.create(name="t", config=None)
        assert tpl.config == {}

    def test_activate(self):
        """激活."""
        tpl = EditTemplate.create(name="t", status=EditTemplateStatus.INACTIVE)
        old = tpl.updated_at
        tpl.activate()
        assert tpl.is_active is True
        assert tpl.status == EditTemplateStatus.ACTIVE
        assert tpl.updated_at >= old

    def test_deactivate(self):
        """停用."""
        tpl = EditTemplate.create(name="t")
        old = tpl.updated_at
        tpl.deactivate()
        assert tpl.is_active is False
        assert tpl.status == EditTemplateStatus.INACTIVE
        assert tpl.updated_at >= old

    def test_is_active_property(self):
        """is_active 属性."""
        tpl = EditTemplate.create(name="t")
        assert tpl.is_active is True
        tpl.deactivate()
        assert tpl.is_active is False
        tpl.activate()
        assert tpl.is_active is True

    def test_unique_id(self):
        t1 = EditTemplate.create(name="t1")
        t2 = EditTemplate.create(name="t2")
        assert t1.id != t2.id

    def test_config_independent(self):
        t1 = EditTemplate.create(name="t1")
        t2 = EditTemplate.create(name="t2")
        t1.config["k"] = "v"
        assert "k" not in t2.config
