"""视频调速引擎单元测试 - 配置解析 + 滤镜生成等纯逻辑."""

from __future__ import annotations

import pytest
from video_processing.speed_engine import (
    MAX_SPEED,
    MIN_SPEED,
    SpeedConfig,
    SpeedEngine,
)

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


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

    def test_speed_ranges(self):
        """速度范围合理."""
        assert MIN_SPEED == 0.25
        assert MAX_SPEED == 4.0
        assert MIN_SPEED < MAX_SPEED


# ── SpeedConfig 测试 ────────────────────────────────────────


class TestSpeedConfigDefaults:
    """默认配置测试."""

    def test_default_values(self):
        """默认值正确."""
        config = SpeedConfig()
        assert config.speed == 1.0
        assert config.pitch_correct is True

    def test_is_original_default(self):
        """默认配置是原速."""
        config = SpeedConfig()
        assert config.is_original is True


class TestSpeedConfigParse:
    """parse 配置解析测试."""

    def test_none_returns_default(self):
        """None 返回默认配置."""
        config = SpeedConfig.parse(None)
        assert config.speed == 1.0
        assert config.pitch_correct is True

    def test_empty_dict_returns_default(self):
        """空 dict 返回默认配置."""
        config = SpeedConfig.parse({})
        assert config.is_original is True

    def test_custom_speed(self):
        """自定义速度."""
        config = SpeedConfig.parse({"speed": 2.0})
        assert config.speed == 2.0
        assert config.is_original is False

    def test_pitch_correct_disabled(self):
        """禁用音调修正."""
        config = SpeedConfig.parse({"speed": 1.5, "pitch_correct": False})
        assert config.pitch_correct is False

    def test_invalid_speed_type_falls_back(self):
        """无效速度类型回退到默认."""
        config = SpeedConfig.parse({"speed": "fast"})
        assert config.speed == 1.0

    def test_invalid_pitch_correct_type_falls_back(self):
        """无效pitch_correct类型回退到默认."""
        config = SpeedConfig.parse({"speed": 2.0, "pitch_correct": "yes"})
        assert config.pitch_correct is True

    def test_non_dict_input_returns_default(self):
        """非dict输入返回默认."""
        config = SpeedConfig.parse("not_a_dict")
        assert config.is_original is True


class TestSpeedConfigClamp:
    """clamp 边界钳制测试."""

    def test_speed_below_min_clamped(self):
        """低于最小值钳制."""
        config = SpeedConfig(speed=0.1)
        config.clamp()
        assert config.speed == MIN_SPEED

    def test_speed_above_max_clamped(self):
        """高于最大值钳制."""
        config = SpeedConfig(speed=10.0)
        config.clamp()
        assert config.speed == MAX_SPEED

    def test_zero_speed_falls_back_to_default(self):
        """速度为0回退到默认."""
        config = SpeedConfig(speed=0)
        config.clamp()
        assert config.speed == 1.0

    def test_negative_speed_falls_back_to_default(self):
        """负速度回退到默认."""
        config = SpeedConfig(speed=-2.0)
        config.clamp()
        assert config.speed == 1.0

    def test_speed_at_min_ok(self):
        """最小值边界."""
        config = SpeedConfig(speed=MIN_SPEED)
        config.clamp()
        assert config.speed == MIN_SPEED

    def test_speed_at_max_ok(self):
        """最大值边界."""
        config = SpeedConfig(speed=MAX_SPEED)
        config.clamp()
        assert config.speed == MAX_SPEED

    def test_speed_in_range_unchanged(self):
        """合法范围内不修改."""
        config = SpeedConfig(speed=1.5)
        config.clamp()
        assert config.speed == 1.5

    def test_parse_auto_clamps(self):
        """parse 自动钳制."""
        config = SpeedConfig.parse({"speed": 100.0})
        assert config.speed == MAX_SPEED


class TestIsOriginal:
    """is_original 属性测试."""

    def test_exactly_one(self):
        """速度恰好为1."""
        assert SpeedConfig(speed=1.0).is_original is True

    def test_very_close_to_one(self):
        """非常接近1也算原速."""
        assert SpeedConfig(speed=1.0000001).is_original is True

    def test_not_one(self):
        """不是1."""
        assert SpeedConfig(speed=1.1).is_original is False
        assert SpeedConfig(speed=0.9).is_original is False


# ── SpeedEngine 测试 ────────────────────────────────────────


class TestBuildVideoFilter:
    """build_video_filter 测试."""

    def setup_method(self):
        self.engine = SpeedEngine()

    def test_original_speed_empty_filter(self):
        """原速返回空字符串（跳过滤镜）."""
        config = SpeedConfig(speed=1.0)
        assert self.engine.build_video_filter(config) == ""

    def test_speed_up_2x(self):
        """2倍速."""
        config = SpeedConfig(speed=2.0)
        result = self.engine.build_video_filter(config)
        assert "setpts=PTS/2.0000" == result

    def test_slow_down_half(self):
        """0.5倍速."""
        config = SpeedConfig(speed=0.5)
        result = self.engine.build_video_filter(config)
        assert "setpts=PTS/0.5000" == result

    def test_contains_setpts(self):
        """包含setpts滤镜."""
        config = SpeedConfig(speed=1.5)
        result = self.engine.build_video_filter(config)
        assert "setpts=PTS/" in result


class TestBuildAudioFilter:
    """build_audio_filter 测试."""

    def setup_method(self):
        self.engine = SpeedEngine()

    def test_original_speed_empty_filter(self):
        """原速返回空字符串."""
        config = SpeedConfig(speed=1.0)
        assert self.engine.build_audio_filter(config) == ""

    def test_single_stage_2x(self):
        """2倍速单级atempo."""
        config = SpeedConfig(speed=2.0)
        result = self.engine.build_audio_filter(config)
        assert result == "atempo=2.0000"

    def test_single_stage_half(self):
        """0.5倍速单级atempo."""
        config = SpeedConfig(speed=0.5)
        result = self.engine.build_audio_filter(config)
        assert result == "atempo=0.5000"

    def test_multi_stage_4x(self):
        """4倍速需要两级 atempo=2.0,atempo=2.0."""
        config = SpeedConfig(speed=4.0)
        result = self.engine.build_audio_filter(config)
        assert result == "atempo=2.0000,atempo=2.0000"

    def test_multi_stage_quarter(self):
        """0.25倍速需要两级 atempo=0.5,atempo=0.5."""
        config = SpeedConfig(speed=0.25)
        result = self.engine.build_audio_filter(config)
        assert result == "atempo=0.5000,atempo=0.5000"

    def test_multi_stage_3x(self):
        """3倍速: 2.0 * 1.5."""
        config = SpeedConfig(speed=3.0)
        result = self.engine.build_audio_filter(config)
        stages = result.split(",")
        assert len(stages) == 2
        # 验证两级相乘等于3
        values = [float(s.split("=")[1]) for s in stages]
        assert abs(values[0] * values[1] - 3.0) < 0.01


class TestSplitAtempoStages:
    """_split_atempo_stages 测试."""

    def test_single_stage_within_range(self):
        """范围内单级."""
        stages = SpeedEngine._split_atempo_stages(1.5)
        assert len(stages) == 1
        assert stages[0] == 1.5

    def test_single_stage_at_max(self):
        """最大值边界单级."""
        stages = SpeedEngine._split_atempo_stages(2.0)
        assert len(stages) == 1

    def test_single_stage_at_min(self):
        """最小值边界单级."""
        stages = SpeedEngine._split_atempo_stages(0.5)
        assert len(stages) == 1

    def test_multi_stage_double_speed(self):
        """4x 需要两级."""
        stages = SpeedEngine._split_atempo_stages(4.0)
        assert len(stages) == 2
        assert abs(stages[0] * stages[1] - 4.0) < 0.01

    def test_multi_stage_half_speed(self):
        """0.25x 需要两级."""
        stages = SpeedEngine._split_atempo_stages(0.25)
        assert len(stages) == 2
        assert abs(stages[0] * stages[1] - 0.25) < 0.01

    def test_all_stages_within_valid_range(self):
        """所有分级都在有效范围内."""
        for speed in [0.25, 0.3, 0.5, 1.0, 1.5, 2.0, 3.0, 4.0]:
            stages = SpeedEngine._split_atempo_stages(speed)
            for s in stages:
                assert 0.5 <= s <= 2.0, f"speed={speed}, stage={s} out of range"


class TestAdjustDuration:
    """adjust_duration 时长计算测试."""

    def setup_method(self):
        self.engine = SpeedEngine()

    def test_original_speed_unchanged(self):
        """原速时长不变."""
        result = self.engine.adjust_duration(100.0, SpeedConfig(speed=1.0))
        assert result == 100.0

    def test_double_speed_half_duration(self):
        """2倍速时长减半."""
        result = self.engine.adjust_duration(100.0, SpeedConfig(speed=2.0))
        assert result == 50.0

    def test_half_speed_double_duration(self):
        """0.5倍速时长翻倍."""
        result = self.engine.adjust_duration(100.0, SpeedConfig(speed=0.5))
        assert result == 200.0

    def test_zero_duration_unchanged(self):
        """零时长不变."""
        result = self.engine.adjust_duration(0.0, SpeedConfig(speed=2.0))
        assert result == 0.0

    def test_negative_duration_unchanged(self):
        """负时长不变（异常值保护）."""
        result = self.engine.adjust_duration(-10.0, SpeedConfig(speed=2.0))
        assert result == -10.0

    def test_quarter_speed(self):
        """0.25倍速时长4倍."""
        result = self.engine.adjust_duration(60.0, SpeedConfig(speed=0.25))
        assert abs(result - 240.0) < 0.01
