"""MediaKit AI 封面生成单元测试.

测试 #1208: AI封面接入MediaKit视频截帧
"""

from pathlib import Path
from unittest.mock import MagicMock, Mock, patch

import pytest

from packages.shared.mediakit_client import MediaKitClient, get_mediakit_client


class TestMediaKitClient:
    """MediaKit 客户端测试."""

    def test_is_available_with_key(self):
        """配置了 API Key 时可用."""
        with patch("packages.shared.mediakit_client.get_shared_settings") as mock_settings:
            mock_settings.return_value.mediakit_api_key = "test-key"
            mock_settings.return_value.mediakit_base_url = "https://test.com/api/v1"
            mock_settings.return_value.mediakit_timeout = 60

            client = MediaKitClient()
            assert client.is_available is True

    def test_is_available_without_key(self):
        """未配置 API Key 时不可用."""
        with patch("packages.shared.mediakit_client.get_shared_settings") as mock_settings:
            mock_settings.return_value.mediakit_api_key = ""
            mock_settings.return_value.mediakit_base_url = "https://test.com/api/v1"
            mock_settings.return_value.mediakit_timeout = 60

            client = MediaKitClient()
            assert client.is_available is False

    @patch("packages.shared.mediakit_client.httpx.post")
    @patch("packages.shared.mediakit_client.httpx.get")
    def test_extract_frames_success(self, mock_get, mock_post):
        """抽帧成功."""
        with patch("packages.shared.mediakit_client.get_shared_settings") as mock_settings:
            mock_settings.return_value.mediakit_api_key = "test-key"
            mock_settings.return_value.mediakit_base_url = "https://test.com/api/v1"
            mock_settings.return_value.mediakit_timeout = 60

            # Mock submit task
            mock_post.return_value.status_code = 200
            mock_post.return_value.json.return_value = {"task_id": "task-123"}
            mock_post.return_value.raise_for_status = Mock()

            # Mock poll task
            mock_get.return_value.status_code = 200
            mock_get.return_value.json.return_value = {
                "status": "success",
                "result": {
                    "snapshots": [
                        {"image_url": "https://example.com/frame1.jpg", "timestamp": 2.5},
                        {"image_url": "https://example.com/frame2.jpg", "timestamp": 5.0},
                    ]
                },
            }
            mock_get.return_value.raise_for_status = Mock()

            client = MediaKitClient()
            frames = client.extract_frames(
                video_url="https://example.com/video.mp4",
                strategy="SceneChange",
                max_frames=5,
            )

            assert frames is not None
            assert len(frames) == 2
            assert frames[0]["image_url"] == "https://example.com/frame1.jpg"
            assert frames[0]["timestamp"] == 2.5

    @patch("packages.shared.mediakit_client.httpx.post")
    def test_extract_frames_submit_failed(self, mock_post):
        """提交任务失败."""
        with patch("packages.shared.mediakit_client.get_shared_settings") as mock_settings:
            mock_settings.return_value.mediakit_api_key = "test-key"
            mock_settings.return_value.mediakit_base_url = "https://test.com/api/v1"
            mock_settings.return_value.mediakit_timeout = 60

            mock_post.side_effect = Exception("Network error")

            client = MediaKitClient()
            frames = client.extract_frames(
                video_url="https://example.com/video.mp4",
                strategy="SceneChange",
                max_frames=5,
            )

            assert frames is None

    @patch("packages.shared.mediakit_client.httpx.post")
    @patch("packages.shared.mediakit_client.httpx.get")
    def test_extract_frames_task_failed(self, mock_get, mock_post):
        """任务执行失败."""
        with patch("packages.shared.mediakit_client.get_shared_settings") as mock_settings:
            mock_settings.return_value.mediakit_api_key = "test-key"
            mock_settings.return_value.mediakit_base_url = "https://test.com/api/v1"
            mock_settings.return_value.mediakit_timeout = 60

            # Mock submit task
            mock_post.return_value.status_code = 200
            mock_post.return_value.json.return_value = {"task_id": "task-123"}
            mock_post.return_value.raise_for_status = Mock()

            # Mock poll task - failed
            mock_get.return_value.status_code = 200
            mock_get.return_value.json.return_value = {
                "status": "failed",
                "error": "Video processing failed",
            }
            mock_get.return_value.raise_for_status = Mock()

            client = MediaKitClient()
            frames = client.extract_frames(
                video_url="https://example.com/video.mp4",
                strategy="SceneChange",
                max_frames=5,
            )

            assert frames is None

    def test_extract_frames_not_available(self):
        """客户端不可用时返回 None."""
        with patch("packages.shared.mediakit_client.get_shared_settings") as mock_settings:
            mock_settings.return_value.mediakit_api_key = ""
            mock_settings.return_value.mediakit_base_url = "https://test.com/api/v1"
            mock_settings.return_value.mediakit_timeout = 60

            client = MediaKitClient()
            frames = client.extract_frames(
                video_url="https://example.com/video.mp4",
                strategy="SceneChange",
                max_frames=5,
            )

            assert frames is None


class TestAICoverService:
    """AI 封面服务测试（统一封面管道后）。"""

    def test_call_ai_cover_ai_frame_raises(self):
        """ai_frame type raises RuntimeError in unified pipeline."""
        from packages.shared.ai_service import _call_ai_cover_service

        with pytest.raises(RuntimeError, match="封面数据不可用"):
            _call_ai_cover_service(
                plan_id="plan-123",
                asset_ids=["asset-1"],
                cover_type="ai_frame",
                primary_video_url="https://example.com/video.mp4",
            )

    def test_call_ai_cover_ai_regenerate_raises(self):
        """ai_regenerate type raises RuntimeError in unified pipeline."""
        from packages.shared.ai_service import _call_ai_cover_service

        with pytest.raises(RuntimeError, match="封面数据不可用"):
            _call_ai_cover_service(
                plan_id="plan-123",
                asset_ids=["asset-1"],
                cover_type="ai_regenerate",
            )

    def test_call_ai_cover_without_video_url_raises(self):
        """ai_frame without video URL still raises RuntimeError."""
        from packages.shared.ai_service import _call_ai_cover_service

        with pytest.raises(RuntimeError, match="封面数据不可用"):
            _call_ai_cover_service(
                plan_id="plan-123",
                asset_ids=["asset-1"],
                cover_type="ai_frame",
                primary_video_url=None,
            )

    def test_call_ai_cover_upload_type(self):
        """upload 类型直接返回."""
        from packages.shared.ai_service import _call_ai_cover_service

        result = _call_ai_cover_service(
            plan_id="plan-123",
            asset_ids=["asset-1"],
            cover_type="upload",
            primary_video_url="https://example.com/video.mp4",
        )

        assert result["type"] == "upload"
        assert result["image_url"] == ""

    def test_call_ai_cover_manual_type(self):
        """manual 类型返回指定时间点."""
        from packages.shared.ai_service import _call_ai_cover_service

        result = _call_ai_cover_service(
            plan_id="plan-123",
            asset_ids=["asset-1"],
            cover_type="manual",
            frame_time=5.0,
            primary_video_url="https://example.com/video.mp4",
        )

        assert result["type"] == "manual"
        assert result["frame_time"] == 5.0


class TestGenerateCover:
    """run_generate_cover 测试."""

    @patch("packages.shared.ai_service._call_ai_cover_service")
    def test_run_generate_cover_passes_video_url(self, mock_cover_service):
        """正确传递 video_url 参数."""
        mock_cover_service.return_value = {
            "type": "ai_frame",
            "image_url": "https://example.com/cover.jpg",
            "frame_time": 2.0,
            "confidence": 0.9,
        }

        from packages.shared.ai_service import run_generate_cover

        result = run_generate_cover(
            plan_id="plan-123",
            asset_ids=["asset-1"],
            cover_type="ai_frame",
            primary_video_url="https://example.com/video.mp4",
        )

        assert result["type"] == "ai_frame"
        mock_cover_service.assert_called_once()
        call_kwargs = mock_cover_service.call_args[1]
        assert call_kwargs["primary_video_url"] == "https://example.com/video.mp4"

    @patch("packages.shared.ai_service._call_ai_cover_service")
    def test_run_generate_cover_without_video_url(self, mock_cover_service):
        """不传 video_url 时默认为 None."""
        mock_cover_service.return_value = {
            "type": "ai_frame",
            "image_url": "https://example.com/cover.jpg",
            "frame_time": 2.0,
            "confidence": 0.9,
        }

        from packages.shared.ai_service import run_generate_cover

        result = run_generate_cover(
            plan_id="plan-123",
            asset_ids=["asset-1"],
            cover_type="ai_frame",
        )

        assert result["type"] == "ai_frame"
        call_kwargs = mock_cover_service.call_args[1]
        assert call_kwargs["primary_video_url"] is None
