import os
import sys
from pathlib import Path

# 设置必要环境变量（必须在导入 app 模块之前）
os.environ.setdefault("JWT_SECRET_KEY", "unit-test-secret-key-for-testing")
os.environ.setdefault("DATABASE_URL", "sqlite:///test.db")

sys.path.insert(0, str(Path(__file__).resolve().parents[2] / "apps" / "api"))

from app.api.routes.asset_diagnosis import _build_diagnosis
from fastapi import FastAPI
from fastapi.testclient import TestClient

from packages.domain import (
    Asset,
    AssetLibrary,
    AssetLibraryKind,
    AssetStatus,
    ClassificationStatus,
    Project,
)


def _asset(name: str, mime_type: str, *, status=AssetStatus.READY, duration=None, quality_score=None):
    return Asset.create(
        project_id="project-1",
        library_id="library-1",
        name=name,
        storage_key=f"uploads/{name}",
        mime_type=mime_type,
        file_size=1024,
        duration=duration,
        status=status,
        classification_status=ClassificationStatus.COMPLETED,
        quality_score=quality_score,
    )


def test_asset_diagnosis_reports_missing_video_gap():
    diagnosis = _build_diagnosis(
        "project-1",
        [_asset("voice.mp3", "audio/mpeg"), _asset("image.jpg", "image/jpeg")],
    )

    assert diagnosis.readiness_score < 80
    assert diagnosis.video_assets == 0
    assert any(gap.key == "missing_video" and gap.severity == "critical" for gap in diagnosis.gaps)


def test_asset_diagnosis_scores_ready_video_assets():
    used_asset = _asset("video-1.mp4", "video/mp4", duration=8)
    used_asset.metadata = {"generation_use_count": 1, "review_status": "pending_review"}
    diagnosis = _build_diagnosis(
        "project-1",
        [
            used_asset,
            _asset("video-2.mp4", "video/mp4", duration=8),
            _asset("video-3.mov", "video/quicktime", duration=8),
            _asset("voice.mp3", "audio/mpeg"),
        ],
    )

    assert diagnosis.readiness_score >= 80
    assert diagnosis.video_assets == 3
    assert diagnosis.voice_assets == 1
    assert diagnosis.estimated_video_count == 3
    assert diagnosis.used_assets == 1
    assert diagnosis.unused_assets == 3
    assert diagnosis.pending_review_assets == 1
    smart_view_counts = {item.key: item.count for item in diagnosis.smart_views}
    assert smart_view_counts["recommended"] == 3
    assert smart_view_counts["used"] == 1
    assert smart_view_counts["pending_review"] == 1


def test_asset_diagnosis_flags_unready_and_low_quality_assets():
    diagnosis = _build_diagnosis(
        "project-1",
        [
            _asset("video.mp4", "video/mp4", duration=10, quality_score=40),
            _asset("pending.mp4", "video/mp4", status=AssetStatus.UPLOADING),
        ],
    )

    gap_keys = {gap.key for gap in diagnosis.gaps}
    assert "not_ready_assets" in gap_keys
    assert "low_quality_assets" in gap_keys
    smart_view_counts = {item.key: item.count for item in diagnosis.smart_views}
    assert smart_view_counts["needs_attention"] == 2
    assert smart_view_counts["high_risk"] == 1


# ---------------------------------------------------------------------------
# 路由层测试 — 验证 find_by_project 调用正确性
# ---------------------------------------------------------------------------


class _StubProjectRepository:
    def __init__(self, projects: dict[str, Project] | None = None):
        self._projects = projects or {}

    def get(self, project_id: str) -> Project | None:
        return self._projects.get(project_id)

    def find_by_id(self, project_id: str) -> Project | None:
        return self._projects.get(project_id)


class _StubAssetLibraryRepository:
    def __init__(self, libraries: dict[str, AssetLibrary] | None = None):
        self._libraries = libraries or {}
        self.find_by_project_called_with: list[str] = []

    def find_by_project(self, project_id: str, kind=None) -> list[AssetLibrary]:
        self.find_by_project_called_with.append(project_id)
        return [lib for lib in self._libraries.values() if lib.project_id == project_id]

    def list_by_project(self, project_id: str) -> list[AssetLibrary]:
        raise AssertionError("路由不应调用 list_by_project，应调用 find_by_project")


class _StubAssetRepository:
    def __init__(self, assets: dict[str, Asset] | None = None):
        self._assets = assets or {}
        self.list_by_library_called_with: list[str] = []

    def list_by_library(self, library_id: str, skip: int = 0, limit: int = 50) -> list[Asset]:
        self.list_by_library_called_with.append(library_id)
        return [a for a in self._assets.values() if a.library_id == library_id]

    def count_by_library(self, library_id: str) -> int:
        return len([a for a in self._assets.values() if a.library_id == library_id])


def _dep(name: str):
    from app import dependencies

    return getattr(dependencies, name)


def _build_route_test_app(project_repo, library_repo, asset_repo):
    from unittest.mock import MagicMock

    from app.api.routes.asset_diagnosis import router
    from app.auth import AuthenticatedUser, get_current_user

    app = FastAPI()
    app.include_router(router, prefix="/api/v1")

    mock_user = MagicMock(spec=AuthenticatedUser)
    mock_user.id = "user-1"
    mock_user.email = "test@example.com"
    mock_user.user.id = "user-1"
    app.dependency_overrides[get_current_user] = lambda: mock_user
    app.dependency_overrides[_dep("get_project_repository")] = lambda: project_repo
    app.dependency_overrides[_dep("get_asset_library_repository")] = lambda: library_repo
    app.dependency_overrides[_dep("get_asset_repository")] = lambda: asset_repo
    return app


class TestAssetDiagnosisRoute:
    """路由层测试 — 验证 find_by_project 调用正确性。"""

    def test_returns_404_when_project_not_found(self):
        project_repo = _StubProjectRepository()
        library_repo = _StubAssetLibraryRepository()
        asset_repo = _StubAssetRepository()
        app = _build_route_test_app(project_repo, library_repo, asset_repo)
        client = TestClient(app)

        resp = client.get("/api/v1/projects/nonexistent/asset-diagnosis")
        assert resp.status_code == 404

    def test_find_by_project_called_with_correct_project_id(self):
        project = Project(id="proj-123", name="Test", owner_user_id="user-1")
        library = AssetLibrary(id="lib-1", name="Lib", project_id="proj-123", kind=AssetLibraryKind.VIDEO)
        project_repo = _StubProjectRepository({"proj-123": project})
        library_repo = _StubAssetLibraryRepository({"lib-1": library})
        asset_repo = _StubAssetRepository()
        app = _build_route_test_app(project_repo, library_repo, asset_repo)
        client = TestClient(app)

        resp = client.get("/api/v1/projects/proj-123/asset-diagnosis")
        assert resp.status_code == 200
        assert library_repo.find_by_project_called_with == ["proj-123"]

    def test_list_by_library_called_for_each_library(self):
        project = Project(id="proj-1", name="Test", owner_user_id="user-1")
        lib1 = AssetLibrary(id="lib-1", name="Lib1", project_id="proj-1", kind=AssetLibraryKind.VIDEO)
        lib2 = AssetLibrary(id="lib-2", name="Lib2", project_id="proj-1", kind=AssetLibraryKind.VOICE)
        project_repo = _StubProjectRepository({"proj-1": project})
        library_repo = _StubAssetLibraryRepository({"lib-1": lib1, "lib-2": lib2})
        asset_repo = _StubAssetRepository()
        app = _build_route_test_app(project_repo, library_repo, asset_repo)
        client = TestClient(app)

        resp = client.get("/api/v1/projects/proj-1/asset-diagnosis")
        assert resp.status_code == 200
        assert set(asset_repo.list_by_library_called_with) == {"lib-1", "lib-2"}

    def test_find_by_project_not_list_by_project(self):
        """路由调用 find_by_project 而非 list_by_project（否则会触发 AssertionError）。"""
        project = Project(id="proj-1", name="Test", owner_user_id="user-1")
        library = AssetLibrary(id="lib-1", name="Lib", project_id="proj-1", kind=AssetLibraryKind.VIDEO)
        project_repo = _StubProjectRepository({"proj-1": project})
        library_repo = _StubAssetLibraryRepository({"lib-1": library})
        asset_repo = _StubAssetRepository()
        app = _build_route_test_app(project_repo, library_repo, asset_repo)
        client = TestClient(app)

        resp = client.get("/api/v1/projects/proj-1/asset-diagnosis")
        # 如果调用了 list_by_project，会抛 AssertionError → 500
        assert resp.status_code == 200
