"""template_clip_config 领域模型单元测试."""

import pytest
from domain.template_clip_config import (
    ClipType,
    TemplateClipConfig,
    TransitionEffect,
)


class TestClipType:
    """ClipType 枚举测试."""

    def test_values(self):
        assert ClipType.INTRO == "intro"
        assert ClipType.MAIN == "main"
        assert ClipType.TRANSITION == "transition"
        assert ClipType.OUTRO == "outro"
        assert ClipType.TITLE == "title"
        assert ClipType.SUBTITLE == "subtitle"


class TestTransitionEffect:
    """TransitionEffect 枚举测试."""

    def test_values(self):
        assert TransitionEffect.CUT == "cut"
        assert TransitionEffect.FADE == "fade"
        assert TransitionEffect.SLIDE_LEFT == "slide_left"
        assert TransitionEffect.SLIDE_RIGHT == "slide_right"
        assert TransitionEffect.DISSOLVE == "dissolve"
        assert TransitionEffect.WIPE == "wipe"


class TestTemplateClipConfigCreate:
    """TemplateClipConfig.create 工厂方法测试."""

    def test_create_with_required_fields(self):
        clip = TemplateClipConfig.create(template_id="tpl_001", clip_type=ClipType.MAIN, order=1)
        assert clip.id
        assert len(clip.id) == 32
        assert clip.template_id == "tpl_001"
        assert clip.clip_type == ClipType.MAIN
        assert clip.order == 1
        assert clip.min_duration == 0.0
        assert clip.max_duration == 0.0
        assert clip.text_template == ""
        assert clip.material_requirements == {}
        assert clip.transition_effect == TransitionEffect.CUT
        assert clip.config == {}

    def test_create_with_all_fields(self):
        clip = TemplateClipConfig.create(
            template_id="tpl_002",
            clip_type=ClipType.INTRO,
            order=2,
            min_duration=3.0,
            max_duration=10.0,
            text_template="欢迎来到{channel}",
            material_requirements={"type": "video", "min_count": 1},
            transition_effect=TransitionEffect.FADE,
            config={"key": "value"},
        )
        assert clip.clip_type == ClipType.INTRO
        assert clip.min_duration == 3.0
        assert clip.max_duration == 10.0
        assert clip.text_template == "欢迎来到{channel}"
        assert clip.material_requirements == {"type": "video", "min_count": 1}
        assert clip.transition_effect == TransitionEffect.FADE
        assert clip.config == {"key": "value"}

    def test_create_with_string_clip_type(self):
        clip = TemplateClipConfig.create(template_id="tpl_003", clip_type="title", order=1)
        assert clip.clip_type == ClipType.TITLE

    def test_create_with_string_transition_effect(self):
        clip = TemplateClipConfig.create(
            template_id="tpl_004",
            clip_type=ClipType.MAIN,
            order=1,
            transition_effect="dissolve",
        )
        assert clip.transition_effect == TransitionEffect.DISSOLVE

    def test_create_strips_strings(self):
        clip = TemplateClipConfig.create(
            template_id="  tpl_005  ",
            clip_type=ClipType.MAIN,
            order=1,
            text_template="  测试模板  ",
        )
        assert clip.template_id == "tpl_005"
        assert clip.text_template == "测试模板"

    def test_create_empty_template_id_raises(self):
        with pytest.raises(ValueError, match="template_id"):
            TemplateClipConfig.create(template_id="", clip_type=ClipType.MAIN, order=1)

    def test_create_whitespace_template_id_raises(self):
        with pytest.raises(ValueError, match="template_id"):
            TemplateClipConfig.create(template_id="   ", clip_type=ClipType.MAIN, order=1)

    def test_create_invalid_clip_type_raises(self):
        with pytest.raises(ValueError):
            TemplateClipConfig.create(template_id="tpl", clip_type="invalid_type", order=1)

    def test_create_invalid_transition_effect_raises(self):
        with pytest.raises(ValueError):
            TemplateClipConfig.create(
                template_id="tpl",
                clip_type=ClipType.MAIN,
                order=1,
                transition_effect="invalid_effect",
            )

    def test_create_negative_min_duration_raises(self):
        with pytest.raises(ValueError, match="min_duration"):
            TemplateClipConfig.create(template_id="tpl", clip_type=ClipType.MAIN, order=1, min_duration=-1.0)

    def test_create_negative_max_duration_raises(self):
        with pytest.raises(ValueError, match="max_duration"):
            TemplateClipConfig.create(template_id="tpl", clip_type=ClipType.MAIN, order=1, max_duration=-1.0)

    def test_create_min_greater_than_max_raises(self):
        with pytest.raises(ValueError, match="min_duration 不能大于 max_duration"):
            TemplateClipConfig.create(
                template_id="tpl",
                clip_type=ClipType.MAIN,
                order=1,
                min_duration=10.0,
                max_duration=5.0,
            )

    def test_create_min_equals_max_ok(self):
        clip = TemplateClipConfig.create(
            template_id="tpl",
            clip_type=ClipType.MAIN,
            order=1,
            min_duration=5.0,
            max_duration=5.0,
        )
        assert clip.min_duration == 5.0
        assert clip.max_duration == 5.0

    def test_create_zero_duration_range_ok(self):
        clip = TemplateClipConfig.create(template_id="tpl", clip_type=ClipType.MAIN, order=1)
        assert clip.min_duration == 0.0
        assert clip.max_duration == 0.0

    def test_create_none_material_requirements_defaults_to_empty_dict(self):
        clip = TemplateClipConfig.create(
            template_id="tpl", clip_type=ClipType.MAIN, order=1, material_requirements=None
        )
        assert clip.material_requirements == {}

    def test_create_none_config_defaults_to_empty_dict(self):
        clip = TemplateClipConfig.create(template_id="tpl", clip_type=ClipType.MAIN, order=1, config=None)
        assert clip.config == {}

    def test_create_ids_are_unique(self):
        c1 = TemplateClipConfig.create(template_id="tpl", clip_type=ClipType.MAIN, order=1)
        c2 = TemplateClipConfig.create(template_id="tpl", clip_type=ClipType.MAIN, order=2)
        assert c1.id != c2.id

    def test_create_timestamps_are_utc(self):
        clip = TemplateClipConfig.create(template_id="tpl", clip_type=ClipType.MAIN, order=1)
        assert clip.created_at.tzinfo is not None
        assert clip.updated_at.tzinfo is not None


class TestTemplateClipConfigProperties:
    """属性方法测试."""

    def test_has_duration_range_false_when_both_zero(self):
        clip = TemplateClipConfig.create(template_id="tpl", clip_type=ClipType.MAIN, order=1)
        assert clip.has_duration_range is False

    def test_has_duration_range_true_when_min_set(self):
        clip = TemplateClipConfig.create(template_id="tpl", clip_type=ClipType.MAIN, order=1, min_duration=2.0)
        assert clip.has_duration_range is True

    def test_has_duration_range_true_when_max_set(self):
        clip = TemplateClipConfig.create(template_id="tpl", clip_type=ClipType.MAIN, order=1, max_duration=10.0)
        assert clip.has_duration_range is True

    def test_default_duration_both_zero(self):
        clip = TemplateClipConfig.create(template_id="tpl", clip_type=ClipType.MAIN, order=1)
        assert clip.default_duration == 0.0

    def test_default_duration_only_min(self):
        clip = TemplateClipConfig.create(template_id="tpl", clip_type=ClipType.MAIN, order=1, min_duration=5.0)
        assert clip.default_duration == 5.0

    def test_default_duration_only_max(self):
        clip = TemplateClipConfig.create(template_id="tpl", clip_type=ClipType.MAIN, order=1, max_duration=10.0)
        assert clip.default_duration == 10.0

    def test_default_duration_both_set_is_midpoint(self):
        clip = TemplateClipConfig.create(
            template_id="tpl", clip_type=ClipType.MAIN, order=1, min_duration=5.0, max_duration=15.0
        )
        assert clip.default_duration == 10.0

    def test_default_duration_min_equals_max(self):
        clip = TemplateClipConfig.create(
            template_id="tpl", clip_type=ClipType.MAIN, order=1, min_duration=5.0, max_duration=5.0
        )
        assert clip.default_duration == 5.0
