"""ReverseEngine 纯逻辑单测 — 配置解析 + 滤镜构建 + 安全限制.

全纯函数测试，不依赖 FFmpeg。
"""

from __future__ import annotations

from unittest.mock import patch

import pytest
from video_processing.reverse_engine import ReverseConfig, ReverseEngine

# ── ReverseConfig 解析 ────────────────────────────────────────────────────


class TestReverseConfigFromDict:
    """ReverseConfig.from_dict 配置解析测试."""

    def test_none_returns_disabled(self):
        """None 输入返回 disabled 默认配置."""
        config = ReverseConfig.from_dict(None)
        assert config.enabled is False
        assert config.reverse_video is True
        assert config.reverse_audio is True

    def test_empty_dict_returns_disabled(self):
        """空 dict 返回 disabled."""
        config = ReverseConfig.from_dict({})
        assert config.enabled is False

    def test_enabled_false_returns_disabled(self):
        """显式 enabled=False."""
        config = ReverseConfig.from_dict({"enabled": False})
        assert config.enabled is False

    def test_enabled_default_flags_default_video_audio(self):
        """只启用时默认视频音频都倒放."""
        config = ReverseConfig.from_dict({"enabled": True})
        assert config.enabled is True
        assert config.reverse_video is True
        assert config.reverse_audio is True

    def test_video_only(self):
        """只倒放视频."""
        config = ReverseConfig.from_dict(
            {
                "enabled": True,
                "reverse_video": True,
                "reverse_audio": False,
            }
        )
        assert config.enabled is True
        assert config.reverse_video is True
        assert config.reverse_audio is False

    def test_audio_only(self):
        """只倒放音频."""
        config = ReverseConfig.from_dict(
            {
                "enabled": True,
                "reverse_video": False,
                "reverse_audio": True,
            }
        )
        assert config.enabled is True
        assert config.reverse_video is False
        assert config.reverse_audio is True

    def test_both_disabled_but_enabled_flag_true(self):
        """enabled=True 但两个子选项都关了（边缘情况）."""
        config = ReverseConfig.from_dict(
            {
                "enabled": True,
                "reverse_video": False,
                "reverse_audio": False,
            }
        )
        assert config.enabled is True
        assert config.reverse_video is False
        assert config.reverse_audio is False

    def test_invalid_type_falls_back(self):
        """非 dict 类型回退到默认 disabled."""
        config = ReverseConfig.from_dict("reverse=true")
        assert config.enabled is False

    def test_attribute_error_falls_back(self):
        """属性错误时回退到默认."""

        class WeirdObj:
            def get(self, key, default=None):
                raise AttributeError("nope")

        config = ReverseConfig.from_dict(WeirdObj())
        assert config.enabled is False

    def test_type_error_falls_back(self):
        """类型错误时回退到默认."""
        config = ReverseConfig.from_dict([1, 2, 3])
        assert config.enabled is False


# ── ReverseEngine 视频滤镜 ──────────────────────────────────────────────────


class TestReverseEngineBuildVideoFilter:
    """ReverseEngine.build_video_filter 视频滤镜测试."""

    def test_disabled_returns_empty(self):
        """disabled 返回空字符串."""
        config = ReverseConfig(enabled=False)
        assert ReverseEngine.build_video_filter(config) == ""

    def test_enabled_returns_reverse(self):
        """启用返回 reverse 滤镜."""
        config = ReverseConfig(enabled=True, reverse_video=True)
        assert ReverseEngine.build_video_filter(config) == "reverse"

    def test_video_disabled_returns_empty(self):
        """reverse_video=False 返回空."""
        config = ReverseConfig(enabled=True, reverse_video=False, reverse_audio=True)
        assert ReverseEngine.build_video_filter(config) == ""

    def test_short_duration_ok(self):
        """短时长正常返回 reverse."""
        config = ReverseConfig(enabled=True)
        result = ReverseEngine.build_video_filter(config, duration=10.0)
        assert result == "reverse"

    def test_exactly_max_duration_ok(self):
        """恰好等于安全上限正常."""
        config = ReverseConfig(enabled=True)
        result = ReverseEngine.build_video_filter(config, duration=ReverseEngine.MAX_SAFE_DURATION)
        assert result == "reverse"

    def test_over_max_duration_skipped(self):
        """超过安全时长跳过倒放."""
        config = ReverseConfig(enabled=True)
        result = ReverseEngine.build_video_filter(config, duration=200.0)
        assert result == ""

    def test_zero_duration_ok(self):
        """零时长正常倒放."""
        config = ReverseConfig(enabled=True)
        result = ReverseEngine.build_video_filter(config, duration=0.0)
        assert result == "reverse"


# ── ReverseEngine 音频滤镜 ──────────────────────────────────────────────


class TestReverseEngineBuildAudioFilter:
    """ReverseEngine.build_audio_filter 音频滤镜测试."""

    def test_disabled_returns_empty(self):
        """disabled 返回空字符串."""
        config = ReverseConfig(enabled=False)
        assert ReverseEngine.build_audio_filter(config) == ""

    def test_enabled_returns_areverse(self):
        """启用返回 areverse 滤镜."""
        config = ReverseConfig(enabled=True, reverse_audio=True)
        assert ReverseEngine.build_audio_filter(config) == "areverse"

    def test_audio_disabled_returns_empty(self):
        """reverse_audio=False 返回空."""
        config = ReverseConfig(enabled=True, reverse_video=True, reverse_audio=False)
        assert ReverseEngine.build_audio_filter(config) == ""

    def test_short_duration_ok(self):
        """短时长正常返回 areverse."""
        config = ReverseConfig(enabled=True)
        result = ReverseEngine.build_audio_filter(config, duration=30.0)
        assert result == "areverse"

    def test_over_max_duration_skipped(self):
        """超过安全时长跳过音频倒放."""
        config = ReverseConfig(enabled=True)
        result = ReverseEngine.build_audio_filter(config, duration=150.0)
        assert result == ""

    def test_exactly_max_duration_ok(self):
        """恰好等于安全上限正常."""
        config = ReverseConfig(enabled=True)
        result = ReverseEngine.build_audio_filter(config, duration=ReverseEngine.MAX_SAFE_DURATION)
        assert result == "areverse"


# ── 组合场景 ────────────────────────────────────────────────────────────


class TestReverseEngineCombined:
    """组合场景测试."""

    def test_both_video_audio_reverse(self):
        """视频音频都倒放."""
        config = ReverseConfig(enabled=True)
        vf = ReverseEngine.build_video_filter(config)
        af = ReverseEngine.build_audio_filter(config)
        assert vf == "reverse"
        assert af == "areverse"

    def test_neither_video_nor_audio(self):
        """都不倒放."""
        config = ReverseConfig(enabled=True, reverse_video=False, reverse_audio=False)
        assert ReverseEngine.build_video_filter(config) == ""
        assert ReverseEngine.build_audio_filter(config) == ""

    def test_long_video_both_skipped(self):
        """超长视频两个都跳过."""
        config = ReverseConfig(enabled=True)
        duration = ReverseEngine.MAX_SAFE_DURATION + 1
        assert ReverseEngine.build_video_filter(config, duration=duration) == ""
        assert ReverseEngine.build_audio_filter(config, duration=duration) == ""

    def test_from_dict_full_config_flow(self):
        """从 dict 解析到滤镜构建的完整流程."""
        data = {"enabled": True, "reverse_video": True, "reverse_audio": False}
        config = ReverseConfig.from_dict(data)
        assert ReverseEngine.build_video_filter(config, duration=10) == "reverse"
        assert ReverseEngine.build_audio_filter(config, duration=10) == ""

    def test_from_dict_disabled_flow(self):
        """disabled 配置完整流程."""
        config = ReverseConfig.from_dict({"enabled": False})
        assert ReverseEngine.build_video_filter(config) == ""
        assert ReverseEngine.build_audio_filter(config) == ""
