"""素材库 + 素材 UseCase 单元测试（P3-1 第31波）。"""

from __future__ import annotations

from unittest.mock import MagicMock

import pytest

from packages.application.asset_libraries import (
    CreateAssetLibraryCommand,
    CreateAssetLibraryUseCase,
    ListAssetLibrariesUseCase,
)
from packages.application.assets import (
    CreateAssetCommand,
    CreateAssetUseCase,
    ListAssetsUseCase,
)
from packages.domain import AssetLibraryKind, AssetStatus, ClassificationStatus

# ── ListAssetLibrariesUseCase ────────────────────────────────────────────────


class TestListAssetLibrariesUseCase:
    """ListAssetLibrariesUseCase 单元测试。"""

    def test_list_libraries_success(self):
        """正常列出项目素材库。"""
        mock_repo = MagicMock()
        mock_repo.find_by_project.return_value = ["lib1", "lib2"]
        use_case = ListAssetLibrariesUseCase(mock_repo)

        result = use_case.execute("proj_001")

        assert result == ["lib1", "lib2"]
        mock_repo.find_by_project.assert_called_once_with("proj_001")

    def test_list_libraries_empty(self):
        """项目无素材库返回空列表。"""
        mock_repo = MagicMock()
        mock_repo.find_by_project.return_value = []
        use_case = ListAssetLibrariesUseCase(mock_repo)

        result = use_case.execute("proj_001")

        assert result == []

    def test_list_libraries_empty_project_id_raises(self):
        """空 project_id 抛 ValueError。"""
        mock_repo = MagicMock()
        use_case = ListAssetLibrariesUseCase(mock_repo)

        with pytest.raises(ValueError, match="project_id 不能为空"):
            use_case.execute("")

    def test_list_libraries_whitespace_project_id_raises(self):
        """全空格 project_id 抛 ValueError。"""
        mock_repo = MagicMock()
        use_case = ListAssetLibrariesUseCase(mock_repo)

        with pytest.raises(ValueError, match="project_id 不能为空"):
            use_case.execute("   ")

    def test_list_libraries_project_id_stripped(self):
        """project_id 首尾空格被清理。"""
        mock_repo = MagicMock()
        mock_repo.find_by_project.return_value = []
        use_case = ListAssetLibrariesUseCase(mock_repo)

        use_case.execute("  proj_001  ")

        mock_repo.find_by_project.assert_called_once_with("proj_001")


# ── CreateAssetLibraryUseCase ────────────────────────────────────────────────


class TestCreateAssetLibraryUseCase:
    """CreateAssetLibraryUseCase 单元测试。"""

    def test_create_video_library_success(self):
        """正常创建视频素材库。"""
        mock_repo = MagicMock()
        mock_repo.create.side_effect = lambda x: x
        use_case = CreateAssetLibraryUseCase(mock_repo)
        command = CreateAssetLibraryCommand(
            project_id="proj_001",
            name="我的视频",
            kind=AssetLibraryKind.VIDEO,
        )

        result = use_case.execute(command)

        assert result.project_id == "proj_001"
        assert result.name == "我的视频"
        assert result.kind == AssetLibraryKind.VIDEO
        mock_repo.create.assert_called_once()

    def test_create_audio_library_success(self):
        """创建音频素材库。"""
        mock_repo = MagicMock()
        mock_repo.create.side_effect = lambda x: x
        use_case = CreateAssetLibraryUseCase(mock_repo)
        command = CreateAssetLibraryCommand(
            project_id="proj_001",
            name="配乐库",
            kind=AssetLibraryKind.VOICE,
        )

        result = use_case.execute(command)

        assert result.kind == AssetLibraryKind.VOICE
        assert result.asset_count == 0
        assert result.total_size == 0

    def test_create_image_library_success(self):
        """创建图片素材库。"""
        mock_repo = MagicMock()
        mock_repo.create.side_effect = lambda x: x
        use_case = CreateAssetLibraryUseCase(mock_repo)
        command = CreateAssetLibraryCommand(
            project_id="proj_001",
            name="图片素材",
            kind=AssetLibraryKind.IMAGE,
        )

        result = use_case.execute(command)

        assert result.kind == AssetLibraryKind.IMAGE

    def test_create_library_name_stripped(self):
        """素材库名称首尾空格被清理。"""
        mock_repo = MagicMock()
        mock_repo.create.side_effect = lambda x: x
        use_case = CreateAssetLibraryUseCase(mock_repo)
        command = CreateAssetLibraryCommand(
            project_id="proj_001",
            name="  我的素材库  ",
            kind=AssetLibraryKind.VIDEO,
        )

        result = use_case.execute(command)

        assert result.name == "我的素材库"


# ── ListAssetsUseCase ────────────────────────────────────────────────────────


class TestListAssetsUseCase:
    """ListAssetsUseCase 单元测试。"""

    def test_list_assets_success(self):
        """正常列出素材库素材。"""
        mock_repo = MagicMock()
        mock_repo.find_by_library.return_value = ["asset1", "asset2"]
        use_case = ListAssetsUseCase(mock_repo)

        result = use_case.execute("lib_001")

        assert result == ["asset1", "asset2"]
        mock_repo.find_by_library.assert_called_once_with("lib_001")

    def test_list_assets_empty(self):
        """素材库为空返回空列表。"""
        mock_repo = MagicMock()
        mock_repo.find_by_library.return_value = []
        use_case = ListAssetsUseCase(mock_repo)

        result = use_case.execute("lib_001")

        assert result == []

    def test_list_assets_empty_library_id_raises(self):
        """空 library_id 抛 ValueError。"""
        mock_repo = MagicMock()
        use_case = ListAssetsUseCase(mock_repo)

        with pytest.raises(ValueError, match="library_id 不能为空"):
            use_case.execute("")

    def test_list_assets_whitespace_library_id_raises(self):
        """全空格 library_id 抛 ValueError。"""
        mock_repo = MagicMock()
        use_case = ListAssetsUseCase(mock_repo)

        with pytest.raises(ValueError, match="library_id 不能为空"):
            use_case.execute("   ")

    def test_list_assets_library_id_stripped(self):
        """library_id 首尾空格被清理。"""
        mock_repo = MagicMock()
        mock_repo.find_by_library.return_value = []
        use_case = ListAssetsUseCase(mock_repo)

        use_case.execute("  lib_001  ")

        mock_repo.find_by_library.assert_called_once_with("lib_001")


# ── CreateAssetUseCase ──────────────────────────────────────────────────────


class TestCreateAssetUseCase:
    """CreateAssetUseCase 单元测试。"""

    def test_create_video_asset_success(self):
        """正常创建视频素材。"""
        mock_repo = MagicMock()
        mock_repo.create.side_effect = lambda x: x
        use_case = CreateAssetUseCase(mock_repo)
        command = CreateAssetCommand(
            project_id="proj_001",
            library_id="lib_001",
            name="video.mp4",
            storage_key="assets/video.mp4",
            mime_type="video/mp4",
            file_size=1024000,
            duration=30.5,
            width=1920,
            height=1080,
            fps=25.0,
            codec="h264",
        )

        result = use_case.execute(command)

        assert result.name == "video.mp4"
        assert result.project_id == "proj_001"
        assert result.library_id == "lib_001"
        assert result.storage_key == "assets/video.mp4"
        assert result.mime_type == "video/mp4"
        assert result.file_size == 1024000
        assert result.duration == 30.5
        assert result.width == 1920
        assert result.height == 1080
        assert result.fps == 25.0
        assert result.codec == "h264"
        assert result.status == AssetStatus.UPLOADING
        assert result.classification_status == ClassificationStatus.PENDING
        mock_repo.create.assert_called_once()

    def test_create_audio_asset_success(self):
        """创建音频素材。"""
        mock_repo = MagicMock()
        mock_repo.create.side_effect = lambda x: x
        use_case = CreateAssetUseCase(mock_repo)
        command = CreateAssetCommand(
            project_id="proj_001",
            library_id="lib_002",
            name="bgm.mp3",
            storage_key="audio/bgm.mp3",
            mime_type="audio/mpeg",
            file_size=512000,
            duration=180.0,
        )

        result = use_case.execute(command)

        assert result.mime_type == "audio/mpeg"
        assert result.duration == 180.0
        assert result.width is None
        assert result.height is None

    def test_create_asset_default_status(self):
        """默认状态为 UPLOADING + PENDING。"""
        mock_repo = MagicMock()
        mock_repo.create.side_effect = lambda x: x
        use_case = CreateAssetUseCase(mock_repo)
        command = CreateAssetCommand(
            project_id="proj_001",
            library_id="lib_001",
            name="test.mp4",
            storage_key="test.mp4",
            mime_type="video/mp4",
        )

        result = use_case.execute(command)

        assert result.status == AssetStatus.UPLOADING
        assert result.classification_status == ClassificationStatus.PENDING

    def test_create_asset_custom_status(self):
        """可以指定自定义状态。"""
        mock_repo = MagicMock()
        mock_repo.create.side_effect = lambda x: x
        use_case = CreateAssetUseCase(mock_repo)
        command = CreateAssetCommand(
            project_id="proj_001",
            library_id="lib_001",
            name="test.mp4",
            storage_key="test.mp4",
            mime_type="video/mp4",
            status=AssetStatus.READY,
            classification_status=ClassificationStatus.COMPLETED,
            quality_score=85.5,
            uploaded_by_user_id="user_001",
        )

        result = use_case.execute(command)

        assert result.status == AssetStatus.READY
        assert result.classification_status == ClassificationStatus.COMPLETED
        assert result.quality_score == 85.5
        assert result.uploaded_by_user_id == "user_001"

    def test_create_asset_with_metadata(self):
        """带 metadata 创建素材。"""
        mock_repo = MagicMock()
        mock_repo.create.side_effect = lambda x: x
        use_case = CreateAssetUseCase(mock_repo)
        metadata = {"location": "beijing", "camera": "sony"}
        command = CreateAssetCommand(
            project_id="proj_001",
            library_id="lib_001",
            name="test.mp4",
            storage_key="test.mp4",
            mime_type="video/mp4",
            metadata=metadata,
        )

        result = use_case.execute(command)

        assert result.metadata == metadata

    def test_create_asset_with_thumbnail(self):
        """带缩略图创建素材。"""
        mock_repo = MagicMock()
        mock_repo.create.side_effect = lambda x: x
        use_case = CreateAssetUseCase(mock_repo)
        command = CreateAssetCommand(
            project_id="proj_001",
            library_id="lib_001",
            name="test.mp4",
            storage_key="test.mp4",
            mime_type="video/mp4",
            thumbnail_url="http://cdn.com/thumb.jpg",
        )

        result = use_case.execute(command)

        assert result.thumbnail_url == "http://cdn.com/thumb.jpg"
