"""测试 _build_filter_complex 正确排除纯音频 clips.

Bug: voice.mp3（纯音频文件）被错误地加入视频 filter_complex，
导致 FFmpeg 尝试访问 [N:v] 视频流时报错 "Stream specifier ':v' matches no streams".

修复：_build_filter_complex 在收集 clips 时跳过 clip_type="audio" 的 clips，
因为音频 clips 由 mix_audio() 独立处理。
"""

from pathlib import Path
from unittest.mock import MagicMock

import pytest


def _make_render_service(tmp_path):
    """创建一个最小化的 UnifiedRenderService 实例."""
    from video_processing.unified_render_service import UnifiedRenderService

    plan = MagicMock()
    plan.id = "test_plan"
    plan.config = {}
    plan.strategy_id = "test_strategy"

    clips = []
    asset_path_map = {}

    service = UnifiedRenderService(
        plan=plan,
        clips=clips,
        asset_path_map=asset_path_map,
        work_dir=tmp_path,
        output_width=480,
        output_height=854,
        output_fps=30,
    )
    return service


def _make_video_clip(clip_id: str, local_path: Path, duration: float = 5.0):
    """创建一个视频 clip."""
    from video_processing.unified_render_service import ResolvedClip

    return ResolvedClip(
        clip_id=clip_id,
        asset_id=f"asset_{clip_id}",
        local_path=local_path,
        clip_type="video",
        order=0,
        start_time=0.0,
        duration=duration,
        config={},
        actual_duration=duration,
    )


def _make_audio_clip(clip_id: str, local_path: Path, duration: float = 5.0):
    """创建一个纯音频 clip."""
    from video_processing.unified_render_service import ResolvedClip

    return ResolvedClip(
        clip_id=clip_id,
        asset_id=f"asset_{clip_id}",
        local_path=local_path,
        clip_type="audio",
        order=0,
        start_time=0.0,
        duration=duration,
        config={"volume": 1.0},
        actual_duration=duration,
    )


class TestBuildFilterComplexExcludesAudioClips:
    """_build_filter_complex 应该排除 clip_type='audio' 的 clips."""

    def test_audio_clip_not_in_filter_complex(self, tmp_path):
        """纯音频 clip 不应出现在 filter_complex 中."""
        render_service = _make_render_service(tmp_path)
        from video_processing.unified_render_service import RenderLayer

        # 准备视频和音频文件
        video_path = tmp_path / "video.mp4"
        video_path.write_bytes(b"\x00")
        audio_path = tmp_path / "voice.mp3"
        audio_path.write_bytes(b"\x00")

        video_clip = _make_video_clip("clip_0", video_path, duration=5.0)
        audio_clip = _make_audio_clip("voice_library_main", audio_path, duration=5.0)

        video_layer = RenderLayer(role="main", z_index=1)
        video_layer.clips.append(video_clip)

        audio_layer = RenderLayer(role="audio", z_index=2)
        audio_layer.clips.append(audio_clip)

        layers = [video_layer, audio_layer]

        # 执行
        filter_complex, input_args = render_service._build_filter_complex(layers)

        # 验证：filter_complex 只包含视频 clip 的处理（[0:v]），不包含音频 clip（[1:v]）
        assert "[0:v]" in filter_complex
        assert "[1:v]" not in filter_complex  # voice.mp3 不应该有视频滤镜

        # 验证：input_args 只包含视频文件，不包含音频文件
        assert str(video_path) in " ".join(input_args)
        assert str(audio_path) not in " ".join(input_args)

    def test_multiple_video_clips_with_audio(self, tmp_path):
        """多个视频 clips + 音频 clip 时，filter_complex 只处理视频."""
        render_service = _make_render_service(tmp_path)
        from video_processing.unified_render_service import RenderLayer

        # 准备文件
        video_paths = [tmp_path / f"video_{i}.mp4" for i in range(3)]
        for p in video_paths:
            p.write_bytes(b"\x00")
        audio_path = tmp_path / "voice.mp3"
        audio_path.write_bytes(b"\x00")

        video_layer = RenderLayer(role="main", z_index=1)
        for i, vp in enumerate(video_paths):
            clip = _make_video_clip(f"clip_{i}", vp, duration=3.0)
            clip.order = i
            video_layer.clips.append(clip)

        audio_layer = RenderLayer(role="audio", z_index=2)
        audio_clip = _make_audio_clip("voice_main", audio_path, duration=9.0)
        audio_layer.clips.append(audio_clip)

        layers = [video_layer, audio_layer]

        # 执行
        filter_complex, input_args = render_service._build_filter_complex(layers)

        # 验证：只有 3 个视频输入
        assert "[0:v]" in filter_complex
        assert "[1:v]" in filter_complex
        assert "[2:v]" in filter_complex
        assert "[3:v]" not in filter_complex  # 音频不应该出现

        # 验证：input_args 只有 3 个 -i
        input_files = [arg for arg in input_args if not arg.startswith("-")]
        assert len(input_files) == 3
        assert str(audio_path) not in input_files

    def test_only_audio_clips_raises_error(self, tmp_path):
        """只有音频 clips 时应该抛出 ValueError."""
        render_service = _make_render_service(tmp_path)
        from video_processing.unified_render_service import RenderLayer

        audio_path = tmp_path / "voice.mp3"
        audio_path.write_bytes(b"\x00")

        audio_layer = RenderLayer(role="audio", z_index=2)
        audio_clip = _make_audio_clip("voice_main", audio_path, duration=5.0)
        audio_layer.clips.append(audio_clip)

        layers = [audio_layer]

        with pytest.raises(ValueError, match="没有可渲染的视频片段"):
            render_service._build_filter_complex(layers)

    def test_tts_audio_clip_excluded(self, tmp_path):
        """TTS 配音 clip（clip_type='audio', config.tts=True）也不应出现在 filter_complex."""
        render_service = _make_render_service(tmp_path)
        from video_processing.unified_render_service import RenderLayer, ResolvedClip

        video_path = tmp_path / "video.mp4"
        video_path.write_bytes(b"\x00")
        tts_audio_path = tmp_path / "tts_segment.wav"
        tts_audio_path.write_bytes(b"\x00")

        video_clip = _make_video_clip("clip_0", video_path, duration=10.0)
        tts_clip = ResolvedClip(
            clip_id="tts_0.000",
            asset_id="tts_voiceover",
            local_path=tts_audio_path,
            clip_type="audio",
            order=0,
            start_time=0.0,
            duration=3.0,
            config={"volume": 1.0, "tts": True},
            actual_duration=3.0,
        )

        video_layer = RenderLayer(role="main", z_index=1)
        video_layer.clips.append(video_clip)

        audio_layer = RenderLayer(role="audio", z_index=2)
        audio_layer.clips.append(tts_clip)

        layers = [video_layer, audio_layer]

        filter_complex, input_args = render_service._build_filter_complex(layers)

        # TTS 音频不应出现在 filter_complex
        assert "[1:v]" not in filter_complex
        input_files = [arg for arg in input_args if not arg.startswith("-")]
        assert str(tts_audio_path) not in input_files

    def test_video_clip_with_audio_config_not_excluded(self, tmp_path):
        """clip_type='video' 的 clip 不应被排除（即使它有音频流）."""
        render_service = _make_render_service(tmp_path)
        from video_processing.unified_render_service import RenderLayer

        video_path = tmp_path / "video.mp4"
        video_path.write_bytes(b"\x00")

        video_clip = _make_video_clip("clip_0", video_path, duration=5.0)
        video_layer = RenderLayer(role="main", z_index=1)
        video_layer.clips.append(video_clip)

        layers = [video_layer]

        filter_complex, input_args = render_service._build_filter_complex(layers)

        # 视频 clip 应该被处理
        assert "[0:v]" in filter_complex
        input_files = [arg for arg in input_args if not arg.startswith("-")]
        assert str(video_path) in input_files

    def test_voice_library_clip_with_voice_library_flag(self, tmp_path):
        """voice_library=True 的 clip（来自 _maybe_add_voice_library_layer）应被排除."""
        render_service = _make_render_service(tmp_path)
        from video_processing.unified_render_service import RenderLayer, ResolvedClip

        video_path = tmp_path / "video.mp4"
        video_path.write_bytes(b"\x00")
        voice_path = tmp_path / "voice.mp3"
        voice_path.write_bytes(b"\x00")

        video_clip = _make_video_clip("clip_0", video_path, duration=14.0)
        # 模拟 _maybe_add_voice_library_layer 创建的 clip
        voice_clip = ResolvedClip(
            clip_id="voice_library_main",
            asset_id="voice_library",
            local_path=voice_path,
            clip_type="audio",
            order=0,
            start_time=0.0,
            duration=14.0,
            config={"volume": 1.0, "voice_library": True},
            actual_duration=14.0,
        )

        video_layer = RenderLayer(role="main", z_index=1)
        video_layer.clips.append(video_clip)

        audio_layer = RenderLayer(role="audio", z_index=2)
        audio_layer.clips.append(voice_clip)

        layers = [video_layer, audio_layer]

        filter_complex, input_args = render_service._build_filter_complex(layers)

        # 只有视频 clip 被处理
        assert "[0:v]" in filter_complex
        assert "[1:v]" not in filter_complex
        # voice.mp3 不在输入中
        assert str(voice_path) not in " ".join(input_args)
