"""P0/P1 修复单元测试 — 一键生成 P0 问题 + P1 校验.

覆盖：
  P0-2: OSS 上传失败抛异常 + URL 可访问性校验
  P0-3: FFmpeg 失败时完整 stderr 日志
  P1:   template_id 存在性校验 + asset_ids 归属校验
"""

from __future__ import annotations

import logging
import subprocess
import sys
from pathlib import Path
from unittest.mock import MagicMock, patch

import pytest

# 添加 worker app 到 sys.path
_WORKER_ROOT = Path(__file__).resolve().parents[2] / "apps" / "worker"
if str(_WORKER_ROOT) not in sys.path:
    sys.path.insert(0, str(_WORKER_ROOT))


# ── P0-1: _download_library_assets ────────────────────────────────────────────


class TestOSSUploadAndVerify:
    """P0-2: OSS 上传失败抛异常 + URL 可访问性校验."""

    def test_verify_url_accessible_success(self):
        """URL 可访问时返回 True."""
        from worker_app.tasks.generation import _verify_url_accessible

        mock_response = MagicMock()
        mock_response.status = 200
        mock_response.__enter__ = MagicMock(return_value=mock_response)
        mock_response.__exit__ = MagicMock(return_value=False)

        with patch("urllib.request.OpenerDirector.open", return_value=mock_response):
            assert _verify_url_accessible("https://example.com/test.mp4") is True

    def test_verify_url_accessible_failure(self):
        """URL 不可访问时返回 False."""
        from worker_app.tasks.generation import _verify_url_accessible

        with patch("urllib.request.OpenerDirector.open", side_effect=Exception("connection refused")):
            assert _verify_url_accessible("https://example.com/test.mp4") is False

    def test_verify_url_404(self):
        """URL 返回 404 时返回 False."""
        from worker_app.tasks.generation import _verify_url_accessible

        mock_response = MagicMock()
        mock_response.status = 404
        mock_response.__enter__ = MagicMock(return_value=mock_response)
        mock_response.__exit__ = MagicMock(return_value=False)

        with patch("urllib.request.OpenerDirector.open", return_value=mock_response):
            assert _verify_url_accessible("https://example.com/test.mp4") is False


# ── P0-3: FFmpeg stderr 日志 ─────────────────────────────────────────────────


class TestFFmpegStderrLogging:
    """P0-3: FFmpeg 失败时完整 stderr 打到日志."""

    def test_run_ffmpeg_logs_stderr_on_failure(self, caplog):
        """run_ffmpeg 失败时记录 stderr 到日志."""
        from video_processing.ffmpeg_utils import run_ffmpeg

        error = subprocess.CalledProcessError(
            returncode=183,
            cmd=["ffmpeg", "-y", "-i", "input.mp4", "output.mp4"],
            output="",
            stderr="Error message from ffmpeg: filter graph error details here",
        )

        with patch("subprocess.run", side_effect=error):
            with caplog.at_level(logging.ERROR):
                with pytest.raises(subprocess.CalledProcessError):
                    run_ffmpeg(["ffmpeg", "-y", "-i", "input.mp4", "output.mp4"])

        assert "FFmpeg 命令失败" in caplog.text
        assert "exit_code=183" in caplog.text
        assert "filter graph error" in caplog.text

    def test_run_ffmpeg_success(self):
        """run_ffmpeg 成功时正常返回."""
        from video_processing.ffmpeg_utils import run_ffmpeg

        mock_result = MagicMock()
        mock_result.stdout = "output"
        mock_result.stderr = ""

        with patch("subprocess.run", return_value=mock_result):
            stdout, stderr = run_ffmpeg(["ffmpeg", "-version"])
            assert stdout == "output"
            assert stderr == ""


# ── P1: template_id + asset_ids 校验 ─────────────────────────────────────────


class TestP1Validations:
    """P1: template_id 存在性校验 + asset_ids 归属校验."""

    def test_validate_template_exists_success(self):
        """模板存在时不抛异常."""
        from worker_app.tasks.generation import _validate_template_exists

        mock_template = MagicMock()
        mock_template.id = "tmpl_001"
        mock_template.name = "Test Template"
        mock_template.is_active = True
        mock_template.status = "active"
        mock_template.version = 1

        session = MagicMock()

        # EditTemplateModel 查询返回 None（走旧模板系统 fallback）
        edit_query = MagicMock()
        edit_filter = MagicMock()
        edit_query.filter.return_value = edit_filter
        edit_filter.first.return_value = None

        # TemplateModel 查询返回 mock_template
        old_query = MagicMock()
        old_filter = MagicMock()
        old_query.filter.return_value = old_filter
        old_filter.first.return_value = mock_template

        def _query_side_effect(model):
            # 根据 model 类型返回不同的 query mock
            name = getattr(model, "__name__", "")
            if "EditTemplate" in name:
                return edit_query
            return old_query

        session.query.side_effect = _query_side_effect

        with patch("worker_app.tasks.generation.SessionLocal", return_value=session):
            _validate_template_exists("tmpl_001")  # 不抛异常

    def test_validate_template_exists_not_found(self):
        """模板不存在时抛 ValueError."""
        from worker_app.tasks.generation import _validate_template_exists

        session = MagicMock()

        # 两个系统查询都返回 None
        def _query_side_effect(model):
            q = MagicMock()
            f = MagicMock()
            q.filter.return_value = f
            f.first.return_value = None
            return q

        session.query.side_effect = _query_side_effect

        with patch("worker_app.tasks.generation.SessionLocal", return_value=session):
            with pytest.raises(ValueError, match="模板不存在"):
                _validate_template_exists("tmpl_nonexistent")


# ── P1: 一键生成 clip 级效果层映射 ───────────────────────────────────────────
