"""
Recipe 配方领域模型单元测试
"""

from packages.domain.recipe import Recipe, RecipeItem


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

    def test_create_item(self):
        item = RecipeItem(
            id="item-1",
            recipe_id="recipe-1",
            item_type="asset",
            item_id="asset-123",
            position=0,
        )
        assert item.id == "item-1"
        assert item.recipe_id == "recipe-1"
        assert item.item_type == "asset"
        assert item.item_id == "asset-123"
        assert item.position == 0
        assert item.metadata_ == {}

    def test_item_with_metadata(self):
        item = RecipeItem(
            id="item-1",
            recipe_id="r1",
            item_type="voice",
            item_id="voice-1",
            position=2,
            metadata_={"speed": 1.0, "pitch": 0},
        )
        assert item.metadata_["speed"] == 1.0
        assert item.metadata_["pitch"] == 0


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

    def test_create_minimal(self):
        r = Recipe(id="r1", user_id="u1", name="我的配方")
        assert r.id == "r1"
        assert r.user_id == "u1"
        assert r.name == "我的配方"

    def test_default_values(self):
        r = Recipe(id="r1", user_id="u1", name="n")
        assert r.description == ""
        assert r.template_id == ""
        assert r.generation_params == {}
        assert r.items == []
        assert r.is_active is True
        assert r.metadata_ == {}

    def test_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),
        ]
        r = Recipe(id="r1", user_id="u1", name="n", items=items)
        assert len(r.items) == 2
        assert r.items[0].item_type == "asset"
        assert r.items[1].item_type == "title"

    def test_with_generation_params(self):
        params = {"mode": "one_take", "duration": 30}
        r = Recipe(id="r1", user_id="u1", name="n", generation_params=params)
        assert r.generation_params["mode"] == "one_take"

    def test_recipe_inactive(self):
        r = Recipe(id="r1", user_id="u1", name="n", is_active=False)
        assert r.is_active is False

    def test_has_timestamps(self):
        r = Recipe(id="r1", user_id="u1", name="n")
        assert r.created_at is not None
        assert r.updated_at is not None

    def test_all_item_types(self):
        for itype in ["asset", "title", "voice"]:
            item = RecipeItem(id="i1", recipe_id="r1", item_type=itype, item_id="x", position=0)
            assert item.item_type == itype

    def test_item_metadata_independence(self):
        """不同 RecipeItem 的 metadata_ 互不影响"""
        item1 = RecipeItem(id="i1", recipe_id="r1", item_type="asset", item_id="a1")
        item2 = RecipeItem(id="i2", recipe_id="r1", item_type="asset", item_id="a2")
        item1.metadata_["key"] = "val"
        assert "key" not in item2.metadata_

    def test_item_position_negative(self):
        """负数 position 也能存"""
        item = RecipeItem(id="i1", recipe_id="r1", item_type="asset", item_id="a1", position=-5)
        assert item.position == -5

    def test_item_position_large(self):
        item = RecipeItem(id="i1", recipe_id="r1", item_type="asset", item_id="a1", position=9999)
        assert item.position == 9999

    def test_item_empty_item_id(self):
        item = RecipeItem(id="i1", recipe_id="r1", item_type="title", item_id="")
        assert item.item_id == ""

    def test_item_type_voice(self):
        item = RecipeItem(id="i1", recipe_id="r1", item_type="voice", item_id="v1")
        assert item.item_type == "voice"

    def test_item_type_title(self):
        item = RecipeItem(id="i1", recipe_id="r1", item_type="title", item_id="t1")
        assert item.item_type == "title"


class TestRecipeExtended:
    """Recipe 深度补充测试"""

    def test_items_order_preserved(self):
        items = [
            RecipeItem(id="i3", recipe_id="r1", item_type="asset", item_id="a1", position=2),
            RecipeItem(id="i1", recipe_id="r1", item_type="title", item_id="t1", position=0),
            RecipeItem(id="i2", recipe_id="r1", item_type="voice", item_id="v1", position=1),
        ]
        r = Recipe(id="r1", user_id="u1", name="n", items=items)
        assert len(r.items) == 3
        assert r.items[0].position == 2
        assert r.items[1].position == 0
        assert r.items[2].position == 1

    def test_empty_items_list(self):
        r = Recipe(id="r1", user_id="u1", name="n", items=[])
        assert r.items == []

    def test_many_items(self):
        items = [
            RecipeItem(id=f"i{i}", recipe_id="r1", item_type="asset", item_id=f"a{i}", position=i) for i in range(50)
        ]
        r = Recipe(id="r1", user_id="u1", name="n", items=items)
        assert len(r.items) == 50
        assert r.items[0].position == 0
        assert r.items[49].position == 49

    def test_generation_params_independence(self):
        params = {"mode": "one_take", "duration": 30}
        r1 = Recipe(id="r1", user_id="u1", name="n1", generation_params=params)
        r2 = Recipe(id="r2", user_id="u1", name="n2")
        r1.generation_params["new_key"] = "new_val"
        # 传入同一个 dict 会共享，但默认生成的互不影响
        assert r2.generation_params == {}

    def test_metadata_independence_default(self):
        r1 = Recipe(id="r1", user_id="u1", name="n1")
        r2 = Recipe(id="r2", user_id="u1", name="n2")
        r1.metadata_["key"] = "val"
        assert "key" not in r2.metadata_

    def test_with_description(self):
        r = Recipe(id="r1", user_id="u1", name="n", description="这是一个测试配方")
        assert r.description == "这是一个测试配方"

    def test_with_template_id(self):
        r = Recipe(id="r1", user_id="u1", name="n", template_id="tpl-123")
        assert r.template_id == "tpl-123"

    def test_empty_name(self):
        r = Recipe(id="r1", user_id="u1", name="")
        assert r.name == ""

    def test_long_name(self):
        long_name = "配方" * 200
        r = Recipe(id="r1", user_id="u1", name=long_name)
        assert r.name == long_name
        assert len(r.name) == 400

    def test_special_characters_in_name(self):
        special = "配!@#$%方"
        r = Recipe(id="r1", user_id="u1", name=special)
        assert r.name == special

    def test_unicode_name(self):
        r = Recipe(id="r1", user_id="u1", name="🎬 一键生成配方 · 美食探店")
        assert "🎬" in r.name
        assert "美食探店" in r.name
