"""
测试视频上传失败排查修复：
1. chunked_upload ALLOWED_MIME_TYPES 与 upload.py 保持一致
2. generated_video_repository list 方法无 N+1 查询
3. 成片库 API 端点可正常返回数据
"""

import json
from datetime import datetime, timezone
from unittest.mock import MagicMock, patch

# ─── 1. chunked_upload ALLOWED_MIME_TYPES 一致性 ───


class TestChunkedUploadMIMEConsistency:
    """chunked_upload.py 的 ALLOWED_MIME_TYPES 必须包含 upload.py 的所有类型。"""

    def _get_upload_mime_types(self) -> set:
        from apps.api.app.api.routes.upload import ALLOWED_MIME_TYPES

        return set(ALLOWED_MIME_TYPES)

    def _get_chunked_mime_types(self) -> set:
        from apps.api.app.api.routes.chunked_upload import ALLOWED_MIME_TYPES

        return set(ALLOWED_MIME_TYPES)

    def test_chunked_upload_has_all_upload_mime_types(self):
        """chunked_upload 白名单必须覆盖 upload.py 的全部类型。"""
        upload_types = self._get_upload_mime_types()
        chunked_types = self._get_chunked_mime_types()
        missing = upload_types - chunked_types
        assert not missing, f"chunked_upload 缺少以下 MIME 类型: {missing}"

    def test_chunked_upload_supports_video_mpeg(self):
        from apps.api.app.api.routes.chunked_upload import ALLOWED_MIME_TYPES

        assert "video/mpeg" in ALLOWED_MIME_TYPES

    def test_chunked_upload_supports_video_matroska(self):
        from apps.api.app.api.routes.chunked_upload import ALLOWED_MIME_TYPES

        assert "video/x-matroska" in ALLOWED_MIME_TYPES

    def test_chunked_upload_supports_video_3gpp(self):
        from apps.api.app.api.routes.chunked_upload import ALLOWED_MIME_TYPES

        assert "video/3gpp" in ALLOWED_MIME_TYPES

    def test_chunked_upload_supports_audio_flac(self):
        from apps.api.app.api.routes.chunked_upload import ALLOWED_MIME_TYPES

        assert "audio/flac" in ALLOWED_MIME_TYPES

    def test_chunked_upload_supports_audio_aac(self):
        from apps.api.app.api.routes.chunked_upload import ALLOWED_MIME_TYPES

        assert "audio/aac" in ALLOWED_MIME_TYPES

    def test_chunked_upload_supports_audio_m4a(self):
        from apps.api.app.api.routes.chunked_upload import ALLOWED_MIME_TYPES

        assert "audio/x-m4a" in ALLOWED_MIME_TYPES

    def test_chunked_upload_supports_audio_webm(self):
        from apps.api.app.api.routes.chunked_upload import ALLOWED_MIME_TYPES

        assert "audio/webm" in ALLOWED_MIME_TYPES

    def test_chunked_upload_supports_extra_image_types(self):
        from apps.api.app.api.routes.chunked_upload import ALLOWED_MIME_TYPES

        assert "image/bmp" in ALLOWED_MIME_TYPES
        assert "image/tiff" in ALLOWED_MIME_TYPES
        assert "image/svg+xml" in ALLOWED_MIME_TYPES

    def test_both_have_same_core_video_types(self):
        """两条路径的核心视频类型必须一致。"""
        upload_types = self._get_upload_mime_types()
        chunked_types = self._get_chunked_mime_types()
        core_video = {"video/mp4", "video/quicktime", "video/webm"}
        for vt in core_video:
            assert vt in upload_types, f"upload.py 缺少 {vt}"
            assert vt in chunked_types, f"chunked_upload.py 缺少 {vt}"


# ─── 2. GeneratedVideo Repository N+1 修复验证 ───


class TestGeneratedVideoRepositoryNoNPlus1:
    """list_by_project 和 list_by_generation_task 应使用 _to_domain 而非 self.get。"""

    def _make_model(self, video_id: str, project_id: str = "proj-1", task_id: str = "task-1"):
        model = MagicMock()
        model.id = video_id
        model.project_id = project_id
        model.generation_task_id = task_id
        model.name = f"video-{video_id}.mp4"
        model.file_url = f"https://oss.example.com/{video_id}.mp4"
        model.file_size = 1024
        model.duration = 5.0
        model.thumbnail_url = None
        model.width = 1280
        model.height = 720
        model.fps = 25.0
        model.status = "completed"
        model.review_status = "pending_review"
        model.generation_params = json.dumps({"mode": "one_take"})
        model.video_fingerprint = None
        model.is_duplicate = False
        model.duplicate_of = None
        model.generated_at = datetime(2026, 1, 1, tzinfo=timezone.utc)
        model.created_at = datetime(2026, 1, 1, tzinfo=timezone.utc)
        return model

    def test_list_by_project_uses_to_domain(self):
        """list_by_project 不应调用 self.get（N+1），应使用 _to_domain。"""
        from packages.adapters.sqlalchemy_impl.generated_video_repository import (
            SQLAlchemyGeneratedVideoRepository,
        )

        session = MagicMock()
        models = [self._make_model(f"v{i}") for i in range(5)]

        query_mock = MagicMock()
        query_mock.filter.return_value.all.return_value = models
        session.query.return_value = query_mock

        repo = SQLAlchemyGeneratedVideoRepository(session)
        with patch.object(SQLAlchemyGeneratedVideoRepository, "get") as mock_get:
            result = repo.list_by_project("proj-1")
            mock_get.assert_not_called()

        assert len(result) == 5
        assert all(v.id.startswith("v") for v in result)

    def test_list_by_generation_task_uses_to_domain(self):
        """list_by_generation_task 不应调用 self.get（N+1），应使用 _to_domain。"""
        from packages.adapters.sqlalchemy_impl.generated_video_repository import (
            SQLAlchemyGeneratedVideoRepository,
        )

        session = MagicMock()
        models = [self._make_model(f"v{i}", task_id="task-42") for i in range(3)]

        query_mock = MagicMock()
        query_mock.filter.return_value.all.return_value = models
        session.query.return_value = query_mock

        repo = SQLAlchemyGeneratedVideoRepository(session)
        with patch.object(SQLAlchemyGeneratedVideoRepository, "get") as mock_get:
            result = repo.list_by_generation_task("task-42")
            mock_get.assert_not_called()

        assert len(result) == 3

    def test_list_by_project_returns_empty_when_no_videos(self):
        from packages.adapters.sqlalchemy_impl.generated_video_repository import (
            SQLAlchemyGeneratedVideoRepository,
        )

        session = MagicMock()
        query_mock = MagicMock()
        query_mock.filter.return_value.all.return_value = []
        session.query.return_value = query_mock

        repo = SQLAlchemyGeneratedVideoRepository(session)
        result = repo.list_by_project("empty-project")
        assert result == []


# ─── 3. 成片库 Schema 及替代端点可用性确认 ───


class TestGeneratedVideosSchemaAndAlternativeEndpoints:
    """确认成片库 Schema 字段完整，替代端点（generation_tasks）可用。"""

    def test_generated_video_response_schema_complete(self):
        """GeneratedVideoResponse 包含所有必要字段。"""
        from apps.api.app.schemas.generated_video import GeneratedVideoResponse

        fields = GeneratedVideoResponse.model_fields
        required_fields = [
            "id",
            "project_id",
            "name",
            "file_url",
            "status",
            "review_status",
            "download_url",
        ]
        for field in required_fields:
            assert field in fields, f"GeneratedVideoResponse 缺少字段: {field}"

    def test_list_generated_videos_response_schema(self):
        """ListGeneratedVideosResponse 包含 items 列表。"""
        from apps.api.app.schemas.generated_video import ListGeneratedVideosResponse

        fields = ListGeneratedVideosResponse.model_fields
        assert "items" in fields, "ListGeneratedVideosResponse 缺少 items 字段"

    def test_generation_task_results_endpoint_exists(self):
        """GET /generation/tasks/{task_id}/results 端点存在。"""
        from apps.api.app.api.routes.generation_tasks import router

        paths = [r.path for r in router.routes if hasattr(r, "path")]
        assert "/tasks/{task_id}/results" in paths, "generation results 端点不存在"


# ─── 4. GeneratedVideo Use Cases 可用性 ───


class TestGeneratedVideoUseCases:
    """确认成片库 Use Case 层可正常工作。"""

    def _make_video(self, video_id: str, project_id: str = "proj-1"):
        from packages.domain import GeneratedVideo

        return GeneratedVideo(
            id=video_id,
            project_id=project_id,
            generation_task_id="task-1",
            name=f"video-{video_id}.mp4",
            file_url=f"https://oss.example.com/{video_id}.mp4",
            file_size=1024,
            duration=5.0,
            width=1280,
            height=720,
            fps=25.0,
            status="completed",
        )

    def test_list_generated_videos_use_case(self):
        """ListGeneratedVideosUseCase 可正常列出视频。"""
        from packages.application import ListGeneratedVideosUseCase

        repo = MagicMock()
        videos = [self._make_video(f"v{i}") for i in range(3)]
        repo.list_by_project.return_value = videos

        use_case = ListGeneratedVideosUseCase(repo)
        result = use_case.execute("proj-1")

        repo.list_by_project.assert_called_once_with("proj-1")
        assert len(result) == 3

    def test_get_generated_video_use_case(self):
        """GetGeneratedVideoUseCase 可正常获取单个视频。"""
        from packages.application import GetGeneratedVideoUseCase

        repo = MagicMock()
        video = self._make_video("v1")
        repo.get.return_value = video

        use_case = GetGeneratedVideoUseCase(repo)
        result = use_case.execute("v1")

        repo.get.assert_called_once_with("v1")
        assert result.id == "v1"

    def test_list_by_task_use_case(self):
        """ListGeneratedVideosByTaskUseCase 可按任务列出视频。"""
        from packages.application import ListGeneratedVideosByTaskUseCase

        repo = MagicMock()
        videos = [self._make_video(f"v{i}") for i in range(2)]
        repo.list_by_generation_task.return_value = videos

        use_case = ListGeneratedVideosByTaskUseCase(repo)
        result = use_case.execute("task-1")

        repo.list_by_generation_task.assert_called_once_with("task-1")
        assert len(result) == 2
