"""生成视频 UseCase 单元测试."""

from __future__ import annotations

from unittest.mock import MagicMock

import pytest

from packages.application.generated_videos import (
    GetGeneratedVideoDownloadUrlUseCase,
    GetGeneratedVideoUseCase,
    GetVideosByIdsUseCase,
    ListGeneratedVideosByTaskUseCase,
    ListGeneratedVideosPaginatedUseCase,
    ListGeneratedVideosUseCase,
    UpdateVideoReviewStatusUseCase,
)
from packages.domain.generated_video import GeneratedVideo


@pytest.fixture
def mock_repo():
    return MagicMock()


@pytest.fixture
def sample_video():
    v = GeneratedVideo.create(
        project_id="proj_1",
        generation_task_id="task_1",
        name="测试视频",
        file_url="https://oss.example.com/videos/test.mp4",
        user_id="user_1",
        file_size=1024000,
        duration=30.5,
        width=1920,
        height=1080,
        fps=30.0,
    )
    v.id = "video_123"
    return v


class TestListGeneratedVideosUseCase:
    """ListGeneratedVideosUseCase 测试"""

    def test_list_returns_results(self, mock_repo, sample_video):
        """正常返回项目生成视频列表"""
        mock_repo.list_by_project.return_value = [sample_video]
        use_case = ListGeneratedVideosUseCase(mock_repo)

        result = use_case.execute("proj_1")

        assert len(result) == 1
        assert result[0].id == "video_123"
        mock_repo.list_by_project.assert_called_once_with("proj_1")

    def test_empty_project_id_raises(self, mock_repo):
        """空 project_id 抛出"""
        use_case = ListGeneratedVideosUseCase(mock_repo)

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

        mock_repo.list_by_project.assert_not_called()

    def test_whitespace_project_id_raises(self, mock_repo):
        """纯空格 project_id 抛出"""
        use_case = ListGeneratedVideosUseCase(mock_repo)

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

    def test_project_id_stripped(self, mock_repo, sample_video):
        """project_id 会被 strip"""
        mock_repo.list_by_project.return_value = [sample_video]
        use_case = ListGeneratedVideosUseCase(mock_repo)

        use_case.execute("  proj_1  ")

        mock_repo.list_by_project.assert_called_once_with("proj_1")


class TestListGeneratedVideosPaginatedUseCase:
    """ListGeneratedVideosPaginatedUseCase 测试"""

    def test_paginated_default_params(self, mock_repo, sample_video):
        """默认分页参数正确传递"""
        mock_repo.list_paginated.return_value = ([sample_video], 1)
        use_case = ListGeneratedVideosPaginatedUseCase(mock_repo)

        results, total = use_case.execute(user_id="user_1")

        assert len(results) == 1
        assert total == 1
        mock_repo.list_paginated.assert_called_once_with(
            user_id="user_1",
            project_id=None,
            status=None,
            review_status=None,
            page=1,
            page_size=20,
        )

    def test_page_less_than_1_clamped(self, mock_repo, sample_video):
        """page < 1 被修正为 1"""
        mock_repo.list_paginated.return_value = ([sample_video], 1)
        use_case = ListGeneratedVideosPaginatedUseCase(mock_repo)

        use_case.execute(page=0)

        call_kwargs = mock_repo.list_paginated.call_args[1]
        assert call_kwargs["page"] == 1

    def test_page_size_less_than_1_clamped(self, mock_repo, sample_video):
        """page_size < 1 被修正为 20"""
        mock_repo.list_paginated.return_value = ([sample_video], 1)
        use_case = ListGeneratedVideosPaginatedUseCase(mock_repo)

        use_case.execute(page_size=0)

        call_kwargs = mock_repo.list_paginated.call_args[1]
        assert call_kwargs["page_size"] == 20

    def test_page_size_greater_than_100_clamped(self, mock_repo, sample_video):
        """page_size > 100 被修正为 20"""
        mock_repo.list_paginated.return_value = ([sample_video], 1)
        use_case = ListGeneratedVideosPaginatedUseCase(mock_repo)

        use_case.execute(page_size=200)

        call_kwargs = mock_repo.list_paginated.call_args[1]
        assert call_kwargs["page_size"] == 20

    def test_full_filter_params(self, mock_repo, sample_video):
        """所有过滤参数正确传递"""
        mock_repo.list_paginated.return_value = ([sample_video], 1)
        use_case = ListGeneratedVideosPaginatedUseCase(mock_repo)

        use_case.execute(
            user_id="user_1",
            project_id="proj_1",
            status="completed",
            review_status="approved",
            page=2,
            page_size=50,
        )

        mock_repo.list_paginated.assert_called_once_with(
            user_id="user_1",
            project_id="proj_1",
            status="completed",
            review_status="approved",
            page=2,
            page_size=50,
        )


class TestGetGeneratedVideoUseCase:
    """GetGeneratedVideoUseCase 测试"""

    def test_get_existing(self, mock_repo, sample_video):
        """获取存在的视频"""
        mock_repo.get.return_value = sample_video
        use_case = GetGeneratedVideoUseCase(mock_repo)

        result = use_case.execute("video_123")

        assert result is not None
        assert result.id == "video_123"

    def test_get_nonexistent_returns_none(self, mock_repo):
        """获取不存在的视频返回 None"""
        mock_repo.get.return_value = None
        use_case = GetGeneratedVideoUseCase(mock_repo)

        result = use_case.execute("nonexistent")

        assert result is None


class TestListGeneratedVideosByTaskUseCase:
    """ListGeneratedVideosByTaskUseCase 测试"""

    def test_list_by_task(self, mock_repo, sample_video):
        """按任务ID查询视频"""
        mock_repo.list_by_generation_task.return_value = [sample_video]
        use_case = ListGeneratedVideosByTaskUseCase(mock_repo)

        result = use_case.execute("task_1")

        assert len(result) == 1
        mock_repo.list_by_generation_task.assert_called_once_with("task_1")

    def test_empty_task_id_raises(self, mock_repo):
        """空任务ID抛出"""
        use_case = ListGeneratedVideosByTaskUseCase(mock_repo)

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

    def test_task_id_stripped(self, mock_repo, sample_video):
        """任务ID被 strip"""
        mock_repo.list_by_generation_task.return_value = [sample_video]
        use_case = ListGeneratedVideosByTaskUseCase(mock_repo)

        use_case.execute("  task_1  ")

        mock_repo.list_by_generation_task.assert_called_once_with("task_1")


class TestGetGeneratedVideoDownloadUrlUseCase:
    """GetGeneratedVideoDownloadUrlUseCase 测试"""

    def test_get_url_success(self, mock_repo, sample_video):
        """成功获取下载URL"""
        mock_repo.get.return_value = sample_video
        use_case = GetGeneratedVideoDownloadUrlUseCase(mock_repo)

        result = use_case.execute("video_123")

        assert result == sample_video.file_url
        assert "test.mp4" in result

    def test_get_url_nonexistent_returns_none(self, mock_repo):
        """视频不存在返回 None"""
        mock_repo.get.return_value = None
        use_case = GetGeneratedVideoDownloadUrlUseCase(mock_repo)

        result = use_case.execute("nonexistent")

        assert result is None


class TestUpdateVideoReviewStatusUseCase:
    """UpdateVideoReviewStatusUseCase 测试"""

    def test_update_status_pending_review(self, mock_repo, sample_video):
        """更新为待审核"""
        mock_repo.update_review_status.return_value = sample_video
        use_case = UpdateVideoReviewStatusUseCase(mock_repo)

        result = use_case.execute("video_123", "pending_review")

        assert result is not None
        mock_repo.update_review_status.assert_called_once_with("video_123", "pending_review")

    def test_update_status_approved(self, mock_repo, sample_video):
        """更新为审核通过"""
        mock_repo.update_review_status.return_value = sample_video
        use_case = UpdateVideoReviewStatusUseCase(mock_repo)

        use_case.execute("video_123", "approved")

        mock_repo.update_review_status.assert_called_once_with("video_123", "approved")

    def test_update_status_rejected(self, mock_repo, sample_video):
        """更新为审核拒绝"""
        mock_repo.update_review_status.return_value = sample_video
        use_case = UpdateVideoReviewStatusUseCase(mock_repo)

        use_case.execute("video_123", "rejected")

        mock_repo.update_review_status.assert_called_once_with("video_123", "rejected")

    def test_invalid_status_raises(self, mock_repo):
        """无效状态抛出 ValueError"""
        use_case = UpdateVideoReviewStatusUseCase(mock_repo)

        with pytest.raises(ValueError, match="无效的 review_status"):
            use_case.execute("video_123", "invalid_status")

        mock_repo.update_review_status.assert_not_called()

    def test_empty_video_id_raises(self, mock_repo):
        """空视频ID抛出"""
        use_case = UpdateVideoReviewStatusUseCase(mock_repo)

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

    def test_video_id_stripped(self, mock_repo, sample_video):
        """视频ID被 strip"""
        mock_repo.update_review_status.return_value = sample_video
        use_case = UpdateVideoReviewStatusUseCase(mock_repo)

        use_case.execute("  video_123  ", "approved")

        mock_repo.update_review_status.assert_called_once_with("video_123", "approved")


class TestGetVideosByIdsUseCase:
    """GetVideosByIdsUseCase 测试"""

    def test_get_by_ids(self, mock_repo, sample_video):
        """按ID批量获取"""
        video2 = GeneratedVideo.create(
            project_id="proj_1",
            generation_task_id="task_2",
            name="视频2",
            file_url="https://oss.example.com/videos/v2.mp4",
        )
        video2.id = "video_456"
        mock_repo.get_by_ids.return_value = [sample_video, video2]
        use_case = GetVideosByIdsUseCase(mock_repo)

        result = use_case.execute(["video_123", "video_456"])

        assert len(result) == 2
        mock_repo.get_by_ids.assert_called_once_with(["video_123", "video_456"])

    def test_empty_list(self, mock_repo):
        """空ID列表返回空"""
        mock_repo.get_by_ids.return_value = []
        use_case = GetVideosByIdsUseCase(mock_repo)

        result = use_case.execute([])

        assert result == []
