"""FFmpeg工具函数纯逻辑测试 — chain_filters / resolve_xfade_transition / build_xfade_filter_chain."""

from __future__ import annotations

import pytest
from video_processing.ffmpeg_utils import (
    XFADE_TRANSITION_MAP,
    build_xfade_filter_chain,
    chain_filters,
    resolve_xfade_transition,
)


class TestChainFilters:
    """chain_filters 滤镜串联测试."""

    def test_single_filter(self):
        """单个滤镜."""
        result = chain_filters(["scale=1280:720"], "v0")
        assert result == "[0:v]scale=1280:720[v0]"

    def test_multiple_filters(self):
        """多个滤镜用逗号连接."""
        result = chain_filters(["scale=1280:720", "fps=25", "format=yuv420p"], "out")
        assert result == "[0:v]scale=1280:720,fps=25,format=yuv420p[out]"

    def test_empty_filters(self):
        """空滤镜列表."""
        result = chain_filters([], "v0")
        assert result == "[0:v][v0]"

    def test_custom_input_label(self):
        """自定义输入标签."""
        result = chain_filters(["scale=640:480"], "v1", input_label="1:v")
        assert result == "[1:v]scale=640:480[v1]"


class TestResolveXfadeTransition:
    """resolve_xfade_transition 转场名称映射测试."""

    def test_direct_match_fade(self):
        """fade直接匹配."""
        assert resolve_xfade_transition("fade") == "fade"

    def test_direct_match_dissolve(self):
        """dissolve直接匹配."""
        assert resolve_xfade_transition("dissolve") == "dissolve"

    def test_alias_crossfade(self):
        """crossfade别名→dissolve."""
        assert resolve_xfade_transition("crossfade") == "dissolve"

    def test_alias_slide_left(self):
        """slide_left别名→slideleft."""
        assert resolve_xfade_transition("slide_left") == "slideleft"

    def test_unknown_fallback_to_fade(self):
        """未知值回退到fade."""
        assert resolve_xfade_transition("nonexistent_effect") == "fade"

    def test_empty_string_fallback(self):
        """空字符串回退."""
        assert resolve_xfade_transition("") == "fade"

    def test_enum_value_support(self):
        """支持带value属性的枚举对象."""

        class FakeEnum:
            value = "slideup"

        assert resolve_xfade_transition(FakeEnum()) == "slideup"

    def test_all_map_keys_resolve(self):
        """映射表中所有key都能解析到有效值."""
        for key in XFADE_TRANSITION_MAP:
            result = resolve_xfade_transition(key)
            assert result and isinstance(result, str)
            assert result != ""

    def test_cut_is_special_fallback(self):
        """cut不在映射表中→回退到fade（硬切由调用方处理）."""
        # cut是特殊值，不在映射表里
        result = resolve_xfade_transition("cut")
        # 不在映射表里就fallback到fade
        assert result == "fade"


class TestBuildXfadeFilterChain:
    """build_xfade_filter_chain 转场滤镜链构建测试."""

    def test_zero_clips(self):
        """0个片段→空字符串+0时长."""
        filter_str, total_dur = build_xfade_filter_chain([], [], [])
        assert filter_str == ""
        assert total_dur == 0.0

    def test_single_clip(self):
        """1个片段→直接copy，总时长等于片段时长."""
        filter_str, total_dur = build_xfade_filter_chain([10.0], ["v0"], [], output_label="outv")
        assert "[v0]copy[outv]" in filter_str
        assert total_dur == pytest.approx(10.0)

    def test_two_clips_basic(self):
        """2个片段基本转场."""
        filter_str, total_dur = build_xfade_filter_chain(
            [5.0, 5.0],
            ["v0", "v1"],
            ["", "fade"],
            transition_duration=0.5,
            output_label="outv",
        )
        assert "xfade=transition=fade" in filter_str
        assert "offset=" in filter_str
        # 总时长 = 5 + 5 - 转场重叠
        assert total_dur == pytest.approx(9.5)

    def test_three_clips_chain(self):
        """3个片段形成链式转场."""
        filter_str, total_dur = build_xfade_filter_chain(
            [3.0, 4.0, 5.0],
            ["v0", "v1", "v2"],
            ["", "fade", "dissolve"],
            transition_duration=0.5,
            output_label="out",
        )
        # 应该有2个xfade操作
        assert filter_str.count("xfade=") == 2
        assert "transition=fade" in filter_str
        assert "transition=dissolve" in filter_str
        # 总时长 = 3+4+5 - 2*0.5 = 11
        assert total_dur == pytest.approx(11.0)

    def test_transition_duration_clamped_to_clip(self):
        """转场时长不能超过单个片段时长."""
        filter_str, total_dur = build_xfade_filter_chain(
            [2.0, 1.0],
            ["v0", "v1"],
            ["", "fade"],
            transition_duration=3.0,  # 比第二个片段还长
            output_label="outv",
        )
        # 转场时长被钳制到第二个片段时长(1.0)
        assert "duration=1.000" in filter_str
        assert total_dur == pytest.approx(2.0)  # 2 + 1 - 1 = 2

    def test_very_short_clip_min_transition(self):
        """极短片段至少保留1ms转场."""
        filter_str, total_dur = build_xfade_filter_chain(
            [1.0, 0.0001],
            ["v0", "v1"],
            ["", "fade"],
            transition_duration=0.5,
            output_label="outv",
        )
        # 至少有1ms
        assert "duration=0.001" in filter_str

    def test_transition_offset_calculation(self):
        """offset计算验证."""
        filter_str, _ = build_xfade_filter_chain(
            [10.0, 10.0],
            ["v0", "v1"],
            ["", "fade"],
            transition_duration=1.0,
            output_label="outv",
        )
        # offset = max(0, 10 - 1*1) = 9
        assert "offset=9.000" in filter_str

    def test_fewer_transitions_than_clips(self):
        """转场列表比片段少时使用cut(fallback to fade)."""
        filter_str, total_dur = build_xfade_filter_chain(
            [5.0, 5.0, 5.0],
            ["v0", "v1", "v2"],
            ["fade"],  # 只有1个转场，第2个转场缺省
            transition_duration=0.5,
            output_label="out",
        )
        # 应该有2个xfade
        assert filter_str.count("xfade=") == 2
        # 第二个xfade的转场是cut→fade fallback
        assert filter_str.count("transition=fade") == 2

    def test_output_label_final_clip(self):
        """最后一个xfade的输出标签是output_label."""
        filter_str, _ = build_xfade_filter_chain(
            [3.0, 4.0, 5.0],
            ["v0", "v1", "v2"],
            ["", "fade", "slideleft"],
            output_label="final_v",
        )
        assert filter_str.rstrip().endswith("[final_v]")

    def test_intermediate_labels(self):
        """中间步骤使用xf1, xf2等标签（从i=1开始计数）."""
        filter_str, _ = build_xfade_filter_chain(
            [2.0, 3.0, 4.0, 5.0],
            ["v0", "v1", "v2", "v3"],
            ["", "fade", "fade", "fade"],
            output_label="out",
        )
        # 4个片段3次xfade，中间标签是xf1, xf2
        assert "[xf1]" in filter_str
        assert "[xf2]" in filter_str
        # 最后一个是[out]
        assert filter_str.rstrip().endswith("[out]")
