"""FFmpeg 超时保护测试。

验证 run_ffmpeg / probe_video_info 的超时保护机制，
防止 FFmpeg hang 住导致 worker 永久阻塞。
"""

from __future__ import annotations

import subprocess
from unittest.mock import MagicMock, patch

import pytest
from video_processing.ffmpeg_utils import (
    DEFAULT_FFMPEG_TIMEOUT,
    probe_video_info,
    run_ffmpeg,
)

# ── run_ffmpeg 超时保护 ──────────────────────────────────────────────────────


class TestRunFFmpegTimeout:
    """run_ffmpeg 超时保护测试。"""

    def test_default_timeout_is_set(self):
        """默认超时应为 1800 秒（30分钟）。"""
        assert DEFAULT_FFMPEG_TIMEOUT == 1800

    def test_timeout_expired_is_raised(self):
        """超时未完成时 TimeoutExpired 异常被传播。"""
        with patch("video_processing.ffmpeg_utils.subprocess.run") as mock_run:
            mock_run.side_effect = subprocess.TimeoutExpired(cmd=["ffmpeg", "test"], timeout=1)
            with pytest.raises(subprocess.TimeoutExpired):
                run_ffmpeg(["ffmpeg", "test"])

    def test_custom_timeout(self):
        """支持自定义超时时间。"""
        with patch("video_processing.ffmpeg_utils.subprocess.run") as mock_run:
            mock_run.side_effect = subprocess.TimeoutExpired(cmd=["ffmpeg"], timeout=5)
            with pytest.raises(subprocess.TimeoutExpired):
                run_ffmpeg(["ffmpeg", "test"], timeout=5)

    def test_none_timeout_disables_protection(self):
        """timeout=None 可以禁用超时保护（不推荐）。"""
        with patch("video_processing.ffmpeg_utils.subprocess.run") as mock_run:
            mock_result = MagicMock()
            mock_result.stdout = ""
            mock_result.stderr = ""
            mock_run.return_value = mock_result
            run_ffmpeg(["ffmpeg", "test"], timeout=None)
            # 验证 timeout=None 被传递
            call_kwargs = mock_run.call_args.kwargs
            assert call_kwargs["timeout"] is None

    def test_called_process_error_still_raised(self):
        """超时异常不影响原有 CalledProcessError 的抛出。"""
        with patch("video_processing.ffmpeg_utils.subprocess.run") as mock_run:
            mock_run.side_effect = subprocess.CalledProcessError(returncode=1, cmd=["ffmpeg"], stderr="error msg")
            with pytest.raises(subprocess.CalledProcessError):
                run_ffmpeg(["ffmpeg", "test"])


# ── probe_video_info 超时保护 ────────────────────────────────────────────────


class TestProbeVideoInfoTimeout:
    """probe_video_info 超时保护测试。"""

    def test_probe_uses_timeout(self):
        """probe_video_info 调用 ffprobe 时应设置 timeout=15。"""
        with patch("video_processing.ffmpeg_utils.subprocess.run") as mock_run:
            mock_run.side_effect = subprocess.TimeoutExpired(cmd=["ffprobe"], timeout=15)
            # 超时异常被捕获，返回默认值
            result = probe_video_info("/tmp/test.mp4")
            assert result["width"] == 1280  # DEFAULT_OUTPUT_WIDTH
            assert result["height"] == 720  # DEFAULT_OUTPUT_HEIGHT

    def test_probe_success(self):
        """正常情况应解析 ffprobe JSON 输出。"""
        fake_output = """
        {
            "streams": [{"width": 1920, "height": 1080, "codec_type": "video", "r_frame_rate": "30/1", "duration": "10.5"}],
            "format": {"duration": "10.5"}
        }
        """
        with patch("video_processing.ffmpeg_utils.subprocess.run") as mock_run:
            mock_result = MagicMock()
            mock_result.stdout = fake_output
            mock_run.return_value = mock_result
            result = probe_video_info("/tmp/test.mp4")
            assert result["width"] == 1920
            assert result["height"] == 1080
            assert abs(result["duration"] - 10.5) < 0.01
