"""
素材库自动匹配 单元测试

覆盖：
- all 模式：返回全部 ready 视频素材 ID
- random 模式：随机选取 N 个
- smart 模式：使用 smart_match 多维评分+多样性选取
- 无 ready 视频素材时返回空列表
- count=0 时返回全部（random/smart 模式）
- 非视频素材和非 ready 状态素材被过滤
"""

from __future__ import annotations

import random
import sys
from pathlib import Path

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

from app.api.routes.generation_tasks import _select_assets_from_library

from packages.domain.smart_match import SCORE_RANDOM_NOISE_MAX


class _ZeroNoiseRandom(random.Random):
    """零噪声随机源：uniform(0, NOISE_MAX) 恒返回 0，smart 排序确定可复现。"""

    def uniform(self, a, b):
        if a == 0.0 and b == SCORE_RANDOM_NOISE_MAX:
            return 0.0
        return super().uniform(a, b)


_ZERO_NOISE = _ZeroNoiseRandom(0)

from packages.domain import Asset, AssetStatus


def _asset(
    id: str,
    name: str,
    mime_type: str = "video/mp4",
    status: AssetStatus = AssetStatus.READY,
    quality_score: float | None = None,
    duration: float | None = None,
) -> Asset:
    a = Asset.create(
        project_id="proj-1",
        library_id="lib-1",
        name=name,
        storage_key=f"uploads/{name}",
        mime_type=mime_type,
        file_size=1024,
        status=status,
        quality_score=quality_score,
        duration=duration,
    )
    # create() 会覆盖 id，手动设置
    a.id = id
    return a


class TestSelectAssetsAllMode:
    """all 模式：返回全部 ready 视频素材。"""

    def test_returns_all_ready_video_assets(self):
        assets = [
            _asset("a1", "v1.mp4"),
            _asset("a2", "v2.mp4"),
            _asset("a3", "v3.mp4"),
        ]
        result = _select_assets_from_library(assets, mode="all", count=0)
        assert sorted(result) == ["a1", "a2", "a3"]

    def test_ignores_count_in_all_mode(self):
        assets = [
            _asset("a1", "v1.mp4"),
            _asset("a2", "v2.mp4"),
        ]
        result = _select_assets_from_library(assets, mode="all", count=1)
        assert len(result) == 2

    def test_filters_non_video_assets(self):
        assets = [
            _asset("a1", "v1.mp4", mime_type="video/mp4"),
            _asset("a2", "img.jpg", mime_type="image/jpeg"),
            _asset("a3", "v2.mov", mime_type="video/quicktime"),
        ]
        result = _select_assets_from_library(assets, mode="all", count=0)
        assert sorted(result) == ["a1", "a3"]

    def test_filters_non_ready_assets(self):
        assets = [
            _asset("a1", "v1.mp4", status=AssetStatus.READY),
            _asset("a2", "v2.mp4", status=AssetStatus.UPLOADING),
            _asset("a3", "v3.mp4", status=AssetStatus.PROCESSING),
        ]
        result = _select_assets_from_library(assets, mode="all", count=0)
        assert result == ["a1"]

    def test_empty_library_returns_empty(self):
        result = _select_assets_from_library([], mode="all", count=0)
        assert result == []

    def test_no_ready_video_returns_empty(self):
        assets = [
            _asset("a1", "v1.mp4", status=AssetStatus.UPLOADING),
            _asset("a2", "img.jpg", mime_type="image/jpeg", status=AssetStatus.READY),
        ]
        result = _select_assets_from_library(assets, mode="all", count=0)
        assert result == []


class TestSelectAssetsSmartMode:
    """smart 模式：使用 smart_match 多维评分（质量40%+时长30%+新鲜度20%+未使用10%）。"""

    def test_smart_sorts_by_quality_score_desc(self):
        assets = [
            _asset("low", "low.mp4", quality_score=30),
            _asset("high", "high.mp4", quality_score=90),
            _asset("mid", "mid.mp4", quality_score=60),
        ]
        result = _select_assets_from_library(assets, mode="smart", count=0, rng=_ZERO_NOISE)
        assert result == ["high", "mid", "low"]

    def test_smart_duration_optimal_beats_too_short(self):
        """最优时长区间(5-30s)的素材得分高于过短素材。"""
        assets = [
            _asset("too_short", "short.mp4", quality_score=80, duration=1.0),
            _asset("optimal", "optimal.mp4", quality_score=80, duration=15.0),
        ]
        result = _select_assets_from_library(assets, mode="smart", count=0, rng=_ZERO_NOISE)
        # Both: quality=80*0.4=32, recency/unused equal
        # optimal(15s): duration_fitness=30 → total=62+
        # too_short(1s): duration_fitness=20+(1/5)*80=36 → 36*0.3=10.8 → total=42.8+
        assert result == ["optimal", "too_short"]

    def test_smart_with_count_limits_results(self):
        assets = [
            _asset("a1", "v1.mp4", quality_score=90),
            _asset("a2", "v2.mp4", quality_score=70),
            _asset("a3", "v3.mp4", quality_score=50),
        ]
        result = _select_assets_from_library(assets, mode="smart", count=2, rng=_ZERO_NOISE)
        assert result == ["a1", "a2"]

    def test_smart_null_quality_treated_as_default(self):
        """无质量分的素材按50分计算（0-100标度）。"""
        assets = [
            _asset("scored", "scored.mp4", quality_score=80),
            _asset("unscored", "unscored.mp4", quality_score=None),
        ]
        result = _select_assets_from_library(assets, mode="smart", count=0, rng=_ZERO_NOISE)
        # scored(80): quality=80*0.4=32; unscored(None→50): quality=50*0.4=20
        assert result == ["scored", "unscored"]

    def test_smart_count_zero_returns_all_sorted(self):
        assets = [
            _asset("a1", "v1.mp4", quality_score=10),
            _asset("a2", "v2.mp4", quality_score=90),
            _asset("a3", "v3.mp4", quality_score=50),
        ]
        result = _select_assets_from_library(assets, mode="smart", count=0, rng=_ZERO_NOISE)
        assert result == ["a2", "a3", "a1"]


class TestSelectAssetsDefaultMode:
    """默认模式（未知 mode 字符串）应回退到 all。"""

    def test_unknown_mode_falls_back_to_all(self):
        assets = [
            _asset("a1", "v1.mp4"),
            _asset("a2", "v2.mp4"),
        ]
        result = _select_assets_from_library(assets, mode="unknown", count=0)
        assert len(result) == 2
