"""拼接引擎单元测试 - 配置解析等纯逻辑."""

from __future__ import annotations

import pytest
from video_processing.concat_engine import ConcatConfig, ConcatSegment


class TestConcatSegmentDefaults:
    """ConcatSegment 默认值测试."""

    def test_default_values(self):
        """默认值正确."""
        seg = ConcatSegment(video_path="/a.mp4")
        assert seg.video_path == "/a.mp4"
        assert seg.start_time == 0.0
        assert seg.duration == 0.0
        assert seg.has_audio is True


class TestConcatSegmentFromDict:
    """ConcatSegment.from_dict 测试."""

    def test_basic_path(self):
        """基本路径."""
        seg = ConcatSegment.from_dict({"video_path": "/a.mp4"})
        assert seg.video_path == "/a.mp4"
        assert seg.start_time == 0.0
        assert seg.duration == 0.0

    def test_custom_start_time(self):
        """自定义开始时间."""
        seg = ConcatSegment.from_dict(
            {
                "video_path": "/a.mp4",
                "start_time": 5.0,
            }
        )
        assert seg.start_time == 5.0

    def test_custom_duration(self):
        """自定义时长."""
        seg = ConcatSegment.from_dict(
            {
                "video_path": "/a.mp4",
                "duration": 10.0,
            }
        )
        assert seg.duration == 10.0

    def test_start_time_negative_clamped(self):
        """负开始时间钳制到0."""
        seg = ConcatSegment.from_dict(
            {
                "video_path": "/a.mp4",
                "start_time": -5.0,
            }
        )
        assert seg.start_time == 0.0

    def test_duration_negative_clamped(self):
        """负时长钳制到0."""
        seg = ConcatSegment.from_dict(
            {
                "video_path": "/a.mp4",
                "duration": -3.0,
            }
        )
        assert seg.duration == 0.0

    def test_invalid_start_time_falls_back(self):
        """无效start_time回退到0."""
        seg = ConcatSegment.from_dict(
            {
                "video_path": "/a.mp4",
                "start_time": "invalid",
            }
        )
        assert seg.start_time == 0.0

    def test_invalid_duration_falls_back(self):
        """无效duration回退到0."""
        seg = ConcatSegment.from_dict(
            {
                "video_path": "/a.mp4",
                "duration": "not_a_number",
            }
        )
        assert seg.duration == 0.0

    def test_no_audio(self):
        """无音频."""
        seg = ConcatSegment.from_dict(
            {
                "video_path": "/a.mp4",
                "has_audio": False,
            }
        )
        assert seg.has_audio is False

    def test_full_config(self):
        """完整配置."""
        seg = ConcatSegment.from_dict(
            {
                "video_path": "/video.mp4",
                "start_time": 2.5,
                "duration": 15.0,
                "has_audio": False,
            }
        )
        assert seg.video_path == "/video.mp4"
        assert seg.start_time == 2.5
        assert seg.duration == 15.0
        assert seg.has_audio is False


class TestConcatConfigDefaults:
    """ConcatConfig 默认值测试."""

    def test_default_values(self):
        """默认值正确."""
        config = ConcatConfig()
        assert config.segments == []
        assert config.output_width == 0
        assert config.output_height == 0
        assert config.output_fps == 0.0
        assert config.force_reencode is False
        assert config.transition == "none"
        assert config.transition_duration == 0.3


class TestConcatConfigFromConfigDict:
    """ConcatConfig.from_config_dict 测试."""

    def test_none_returns_default(self):
        """None返回默认配置."""
        config = ConcatConfig.from_config_dict(None)
        assert config.segments == []

    def test_empty_dict_returns_default(self):
        """空dict返回默认."""
        config = ConcatConfig.from_config_dict({})
        assert config.segments == []

    def test_single_segment(self):
        """单片段."""
        config = ConcatConfig.from_config_dict(
            {
                "segments": [{"video_path": "/a.mp4"}],
            }
        )
        assert len(config.segments) == 1
        assert config.segments[0].video_path == "/a.mp4"

    def test_multiple_segments(self):
        """多片段."""
        config = ConcatConfig.from_config_dict(
            {
                "segments": [
                    {"video_path": "/a.mp4", "start_time": 1.0},
                    {"video_path": "/b.mp4", "duration": 5.0},
                    {"video_path": "/c.mp4"},
                ],
            }
        )
        assert len(config.segments) == 3
        assert config.segments[0].start_time == 1.0
        assert config.segments[1].duration == 5.0

    def test_skips_no_path(self):
        """跳过无video_path的片段."""
        config = ConcatConfig.from_config_dict(
            {
                "segments": [
                    {"video_path": "/a.mp4"},
                    {"other": "value"},
                    {"video_path": ""},
                ],
            }
        )
        assert len(config.segments) == 1

    def test_segments_not_list_ignored(self):
        """segments不是列表忽略."""
        config = ConcatConfig.from_config_dict(
            {
                "segments": "not_a_list",
            }
        )
        assert config.segments == []

    def test_output_size(self):
        """输出尺寸."""
        config = ConcatConfig.from_config_dict(
            {
                "segments": [{"video_path": "/a.mp4"}],
                "output_width": 1920,
                "output_height": 1080,
            }
        )
        assert config.output_width == 1920
        assert config.output_height == 1080

    def test_negative_output_size_clamped(self):
        """负输出尺寸钳制到0."""
        config = ConcatConfig.from_config_dict(
            {
                "segments": [{"video_path": "/a.mp4"}],
                "output_width": -100,
                "output_height": -50,
            }
        )
        assert config.output_width == 0
        assert config.output_height == 0

    def test_invalid_output_size_falls_back(self):
        """无效输出尺寸回退."""
        config = ConcatConfig.from_config_dict(
            {
                "segments": [{"video_path": "/a.mp4"}],
                "output_width": "wide",
                "output_fps": "sixty",
            }
        )
        assert config.output_width == 0
        assert config.output_fps == 0.0

    def test_output_fps(self):
        """输出帧率."""
        config = ConcatConfig.from_config_dict(
            {
                "segments": [{"video_path": "/a.mp4"}],
                "output_fps": 60.0,
            }
        )
        assert config.output_fps == 60.0

    def test_force_reencode(self):
        """强制重新编码."""
        config = ConcatConfig.from_config_dict(
            {
                "segments": [{"video_path": "/a.mp4"}],
                "force_reencode": True,
            }
        )
        assert config.force_reencode is True

    def test_transition_config(self):
        """转场配置."""
        config = ConcatConfig.from_config_dict(
            {
                "segments": [{"video_path": "/a.mp4"}, {"video_path": "/b.mp4"}],
                "transition": "crossfade",
                "transition_duration": 1.0,
            }
        )
        assert config.transition == "crossfade"
        assert config.transition_duration == 1.0

    def test_non_dict_config_returns_default(self):
        """非dict配置返回默认."""
        config = ConcatConfig.from_config_dict("not_a_dict")
        assert config.segments == []


class TestHasEffect:
    """has_effect 属性测试."""

    def test_no_segments_no_effect(self):
        """无片段无效果."""
        config = ConcatConfig()
        assert config.has_effect is False

    def test_one_segment_no_effect(self):
        """单片段无效果（拼接至少需要2段）."""
        config = ConcatConfig(
            segments=[
                ConcatSegment(video_path="/a.mp4"),
            ]
        )
        assert config.has_effect is False

    def test_two_segments_has_effect(self):
        """两段及以上有效果."""
        config = ConcatConfig(
            segments=[
                ConcatSegment(video_path="/a.mp4"),
                ConcatSegment(video_path="/b.mp4"),
            ]
        )
        assert config.has_effect is True


class TestTotalSegments:
    """total_segments 属性测试."""

    def test_no_segments(self):
        """零片段."""
        config = ConcatConfig()
        assert config.total_segments == 0

    def test_three_segments(self):
        """三个片段."""
        config = ConcatConfig(
            segments=[
                ConcatSegment(video_path="/a.mp4"),
                ConcatSegment(video_path="/b.mp4"),
                ConcatSegment(video_path="/c.mp4"),
            ]
        )
        assert config.total_segments == 3
