"""transition_config 转场配置领域模型单元测试."""

from __future__ import annotations

import pytest

from packages.domain.transition_config import (
    CUT_TRANSITION,
    DEFAULT_TRANSITION_DURATION,
    MAX_TRANSITION_DURATION,
    MIN_TRANSITION_DURATION,
    TransitionConfig,
    TransitionType,
)

# ── 常量测试 ──────────────────────────────────────────────────────────────────


class TestConstants:
    """常量测试."""

    def test_min_duration(self):
        """最小转场时长."""
        assert MIN_TRANSITION_DURATION == 0.3

    def test_max_duration(self):
        """最大转场时长."""
        assert MAX_TRANSITION_DURATION == 2.0

    def test_default_duration(self):
        """默认转场时长."""
        assert DEFAULT_TRANSITION_DURATION == 0.5

    def test_duration_range_valid(self):
        """时长范围合理：min < default < max."""
        assert MIN_TRANSITION_DURATION < DEFAULT_TRANSITION_DURATION
        assert DEFAULT_TRANSITION_DURATION < MAX_TRANSITION_DURATION

    def test_cut_transition(self):
        """硬切常量."""
        assert CUT_TRANSITION == "cut"


# ── TransitionType 枚举测试 ───────────────────────────────────────────────────


class TestTransitionType:
    """TransitionType 枚举测试."""

    def test_has_cut(self):
        """有CUT类型."""
        assert TransitionType.CUT.value == "cut"

    def test_has_fade(self):
        """有FADE类型."""
        assert TransitionType.FADE.value == "fade"

    def test_has_dissolve(self):
        """有DISSOLVE类型."""
        assert TransitionType.DISSOLVE.value == "dissolve"

    def test_slide_types(self):
        """滑动系列四种."""
        assert TransitionType.SLIDE_LEFT.value == "slideleft"
        assert TransitionType.SLIDE_RIGHT.value == "slideright"
        assert TransitionType.SLIDE_UP.value == "slideup"
        assert TransitionType.SLIDE_DOWN.value == "slidedown"

    def test_wipe_types(self):
        """擦除系列四种."""
        assert TransitionType.WIPE_LEFT.value == "wipeleft"
        assert TransitionType.WIPE_RIGHT.value == "wiperight"
        assert TransitionType.WIPE_UP.value == "wipeup"
        assert TransitionType.WIPE_DOWN.value == "wipedown"

    def test_zoom_type(self):
        """缩放类型."""
        assert TransitionType.ZOOM.value == "zoom"

    def test_circle_crop_type(self):
        """圆形扩散."""
        assert TransitionType.CIRCLE_CROP.value == "circlecrop"

    def test_rect_crop_type(self):
        """矩形覆盖."""
        assert TransitionType.RECT_CROP.value == "rectcrop"

    def test_total_types(self):
        """共14种转场类型."""
        assert len(TransitionType) == 14

    def test_all_supported_excludes_cut(self):
        """all_supported()不含cut."""
        supported = TransitionType.all_supported()
        assert "cut" not in supported
        assert len(supported) == 13

    def test_all_supported_returns_strings(self):
        """all_supported()返回字符串列表."""
        supported = TransitionType.all_supported()
        assert all(isinstance(s, str) for s in supported)

    def test_is_supported_valid(self):
        """支持的转场类型."""
        assert TransitionType.is_supported("fade") is True
        assert TransitionType.is_supported("dissolve") is True
        assert TransitionType.is_supported("slideleft") is True

    def test_is_supported_invalid(self):
        """不支持的转场类型."""
        assert TransitionType.is_supported("invalid_effect") is False
        assert TransitionType.is_supported("") is False

    def test_is_supported_case_insensitive(self):
        """不区分大小写."""
        assert TransitionType.is_supported("FADE") is True
        assert TransitionType.is_supported("Fade") is True

    def test_is_supported_with_underscores(self):
        """下划线会被忽略."""
        assert TransitionType.is_supported("slide_left") is True
        assert TransitionType.is_supported("circle_crop") is True

    def test_is_supported_with_hyphens(self):
        """中划线会被忽略."""
        assert TransitionType.is_supported("slide-left") is True

    def test_is_supported_cut(self):
        """cut不被算作supported（all_supported不含cut）."""
        # is_supported是检查是否在支持的xfade效果里，cut是特殊值
        # 看实现：is_supported检查_NAME_TO_ENUM_MAP，cut应该也在里面
        pass


# ── TransitionConfig.parse - effect 测试 ─────────────────────────────────────


class TestTransitionConfigParseEffect:
    """TransitionConfig.parse effect参数测试."""

    def test_none_effect_defaults_to_cut(self):
        """None effect → cut."""
        cfg = TransitionConfig.parse(effect=None)
        assert cfg.effect == CUT_TRANSITION

    def test_empty_effect_defaults_to_cut(self):
        """空字符串 → cut."""
        cfg = TransitionConfig.parse(effect="")
        assert cfg.effect == CUT_TRANSITION

    def test_whitespace_effect_defaults_to_cut(self):
        """纯空白 → cut."""
        cfg = TransitionConfig.parse(effect="   ")
        assert cfg.effect == CUT_TRANSITION

    def test_cut_effect(self):
        """显式cut."""
        cfg = TransitionConfig.parse(effect="cut")
        assert cfg.effect == "cut"

    def test_cut_case_insensitive(self):
        """CUT不区分大小写."""
        cfg = TransitionConfig.parse(effect="CUT")
        assert cfg.effect == "cut"

    def test_valid_fade_effect(self):
        """有效的fade效果."""
        cfg = TransitionConfig.parse(effect="fade")
        assert cfg.effect == "fade"

    def test_valid_dissolve_effect(self):
        """有效的dissolve效果."""
        cfg = TransitionConfig.parse(effect="dissolve")
        assert cfg.effect == "dissolve"

    def test_effect_stripped(self):
        """effect去除空白."""
        cfg = TransitionConfig.parse(effect="  fade  ")
        assert cfg.effect == "fade"

    def test_invalid_effect_falls_back_to_cut(self):
        """无效效果 → 降级为cut."""
        cfg = TransitionConfig.parse(effect="super_fancy_effect")
        assert cfg.effect == CUT_TRANSITION

    def test_alias_crossfade(self):
        """别名crossfade → dissolve."""
        cfg = TransitionConfig.parse(effect="crossfade")
        assert cfg.effect == "dissolve"

    def test_alias_crossdissolve(self):
        """别名crossdissolve → dissolve."""
        cfg = TransitionConfig.parse(effect="crossdissolve")
        assert cfg.effect == "dissolve"

    def test_alias_fadein(self):
        """别名fadein → fade."""
        cfg = TransitionConfig.parse(effect="fadein")
        assert cfg.effect == "fade"

    def test_alias_fadeout(self):
        """别名fadeout → fade."""
        cfg = TransitionConfig.parse(effect="fadeout")
        assert cfg.effect == "fade"

    def test_alias_slide(self):
        """别名slide → slideleft."""
        cfg = TransitionConfig.parse(effect="slide")
        assert cfg.effect == "slideleft"

    def test_alias_wipe(self):
        """别名wipe → wipeleft."""
        cfg = TransitionConfig.parse(effect="wipe")
        assert cfg.effect == "wipeleft"

    def test_alias_zoomin(self):
        """别名zoomin → zoom（ZOOM枚举value为zoom）."""
        cfg = TransitionConfig.parse(effect="zoomin")
        assert cfg.effect == "zoom"

    def test_alias_circle(self):
        """别名circle → circlecrop."""
        cfg = TransitionConfig.parse(effect="circle")
        assert cfg.effect == "circlecrop"

    def test_alias_rect(self):
        """别名rect → rectcrop."""
        cfg = TransitionConfig.parse(effect="rect")
        assert cfg.effect == "rectcrop"

    def test_underscore_format(self):
        """下划线格式能解析."""
        cfg = TransitionConfig.parse(effect="slide_left")
        assert cfg.effect == "slideleft"

    def test_case_insensitive_alias(self):
        """别名不区分大小写."""
        cfg = TransitionConfig.parse(effect="CrossFade")
        assert cfg.effect == "dissolve"

    def test_slide_left_direct(self):
        """直接用slideleft."""
        cfg = TransitionConfig.parse(effect="slideleft")
        assert cfg.effect == "slideleft"


# ── TransitionConfig.parse - duration 测试 ───────────────────────────────────


class TestTransitionConfigParseDuration:
    """TransitionConfig.parse duration参数测试."""

    def test_none_duration_uses_default(self):
        """None duration → 默认值."""
        cfg = TransitionConfig.parse(duration=None)
        assert cfg.duration == DEFAULT_TRANSITION_DURATION

    def test_normal_duration(self):
        """正常范围内的时长."""
        cfg = TransitionConfig.parse(duration=1.0)
        assert cfg.duration == 1.0

    def test_min_duration(self):
        """刚好等于最小值."""
        cfg = TransitionConfig.parse(duration=MIN_TRANSITION_DURATION)
        assert cfg.duration == MIN_TRANSITION_DURATION

    def test_max_duration(self):
        """刚好等于最大值."""
        cfg = TransitionConfig.parse(duration=MAX_TRANSITION_DURATION)
        assert cfg.duration == MAX_TRANSITION_DURATION

    def test_below_min_clamped(self):
        """低于最小值钳制到最小值."""
        cfg = TransitionConfig.parse(duration=0.1)
        assert cfg.duration == MIN_TRANSITION_DURATION

    def test_zero_duration_clamped(self):
        """0时长钳制到最小值."""
        cfg = TransitionConfig.parse(duration=0)
        assert cfg.duration == MIN_TRANSITION_DURATION

    def test_negative_duration_clamped(self):
        """负时长钳制到最小值."""
        cfg = TransitionConfig.parse(duration=-1)
        assert cfg.duration == MIN_TRANSITION_DURATION

    def test_above_max_clamped(self):
        """超过最大值钳制到最大值."""
        cfg = TransitionConfig.parse(duration=5.0)
        assert cfg.duration == MAX_TRANSITION_DURATION

    def test_string_duration(self):
        """字符串形式的duration."""
        cfg = TransitionConfig.parse(duration="1.5")
        assert cfg.duration == 1.5

    def test_invalid_string_duration_uses_default(self):
        """无效字符串duration → 默认值."""
        cfg = TransitionConfig.parse(duration="abc")
        assert cfg.duration == DEFAULT_TRANSITION_DURATION

    def test_int_duration(self):
        """整数时长."""
        cfg = TransitionConfig.parse(duration=1)
        assert cfg.duration == 1.0

    def test_default_duration_when_no_args(self):
        """无参时duration为默认值."""
        cfg = TransitionConfig.parse()
        assert cfg.duration == DEFAULT_TRANSITION_DURATION


# ── TransitionConfig 属性测试 ────────────────────────────────────────────────


class TestTransitionConfigProperties:
    """TransitionConfig 属性测试."""

    def test_is_cut_true(self):
        """cut是硬切."""
        cfg = TransitionConfig(effect="cut")
        assert cfg.is_cut is True

    def test_is_cut_false(self):
        """非cut不是硬切."""
        cfg = TransitionConfig(effect="fade")
        assert cfg.is_cut is False

    def test_is_cut_default(self):
        """默认配置是硬切."""
        cfg = TransitionConfig()
        assert cfg.is_cut is True

    def test_ffmpeg_transition_cut_returns_empty(self):
        """硬切返回空字符串（无xfade）."""
        cfg = TransitionConfig(effect="cut")
        assert cfg.ffmpeg_transition == ""

    def test_ffmpeg_transition_fade(self):
        """fade → fade."""
        cfg = TransitionConfig(effect="fade")
        assert cfg.ffmpeg_transition == "fade"

    def test_ffmpeg_transition_dissolve(self):
        """dissolve → dissolve."""
        cfg = TransitionConfig(effect="dissolve")
        assert cfg.ffmpeg_transition == "dissolve"

    def test_ffmpeg_transition_slideleft(self):
        """slideleft → slideleft."""
        cfg = TransitionConfig(effect="slideleft")
        assert cfg.ffmpeg_transition == "slideleft"

    def test_ffmpeg_transition_zoom(self):
        """zoom → zoomin（FFmpeg中叫zoomin）."""
        cfg = TransitionConfig(effect="zoom")
        assert cfg.ffmpeg_transition == "zoomin"

    def test_ffmpeg_transition_circlecrop(self):
        """circlecrop → circlecrop."""
        cfg = TransitionConfig(effect="circlecrop")
        assert cfg.ffmpeg_transition == "circlecrop"

    def test_ffmpeg_transition_default_fade_fallback(self):
        """未知效果回退到fade."""
        # 直接构造一个不支持的effect（绕过parse）
        cfg = TransitionConfig(effect="unknown_effect", duration=0.5)
        # 会回退到fade
        assert cfg.ffmpeg_transition == "fade"


# ── TransitionConfig.validate 测试 ───────────────────────────────────────────


class TestTransitionConfigValidate:
    """TransitionConfig.validate 测试."""

    def test_valid_default_cut(self):
        """默认cut配置是合法的."""
        cfg = TransitionConfig()
        ok, msg = cfg.validate()
        assert ok is True
        assert msg == ""

    def test_valid_fade(self):
        """fade配置是合法的."""
        cfg = TransitionConfig(effect="fade", duration=1.0)
        ok, msg = cfg.validate()
        assert ok is True
        assert msg == ""

    def test_valid_min_duration(self):
        """最小时长是合法的."""
        cfg = TransitionConfig(effect="fade", duration=MIN_TRANSITION_DURATION)
        ok, msg = cfg.validate()
        assert ok is True

    def test_valid_max_duration(self):
        """最大时长是合法的."""
        cfg = TransitionConfig(effect="fade", duration=MAX_TRANSITION_DURATION)
        ok, msg = cfg.validate()
        assert ok is True

    def test_invalid_duration_too_low(self):
        """时长小于最小值不合法."""
        cfg = TransitionConfig(effect="fade", duration=0.1)
        ok, msg = cfg.validate()
        assert ok is False
        assert "不能小于" in msg

    def test_invalid_duration_too_high(self):
        """时长大于最大值不合法."""
        cfg = TransitionConfig(effect="fade", duration=5.0)
        ok, msg = cfg.validate()
        assert ok is False
        assert "不能大于" in msg

    def test_invalid_unknown_effect(self):
        """未知效果不合法."""
        cfg = TransitionConfig(effect="super_fancy", duration=0.5)
        ok, msg = cfg.validate()
        assert ok is False
        assert "不支持" in msg

    def test_cut_always_valid(self):
        """cut效果总是合法的（即使duration异常...要看实现）."""
        # cut的话is_cut为True，validate里会跳过effect检查
        cfg = TransitionConfig(effect="cut", duration=1.0)
        ok, msg = cfg.validate()
        assert ok is True


# ── 默认值测试 ───────────────────────────────────────────────────────────────


class TestTransitionConfigDefaults:
    """TransitionConfig 默认值测试."""

    def test_default_effect_is_cut(self):
        """默认effect是cut."""
        cfg = TransitionConfig()
        assert cfg.effect == CUT_TRANSITION

    def test_default_duration(self):
        """默认duration."""
        cfg = TransitionConfig()
        assert cfg.duration == DEFAULT_TRANSITION_DURATION

    def test_parse_no_args(self):
        """parse无参 → 默认值."""
        cfg = TransitionConfig.parse()
        assert cfg.effect == CUT_TRANSITION
        assert cfg.duration == DEFAULT_TRANSITION_DURATION

    def test_parse_both_none(self):
        """两个参数都None → 默认值."""
        cfg = TransitionConfig.parse(effect=None, duration=None)
        assert cfg.effect == CUT_TRANSITION
        assert cfg.duration == DEFAULT_TRANSITION_DURATION

    def test_is_dataclass(self):
        """是dataclass."""
        from dataclasses import is_dataclass

        assert is_dataclass(TransitionConfig)
