"""种子缓存音乐实体识别测试。"""

import asyncio
from unittest.mock import AsyncMock, Mock, patch

from app.api.endpoints.torrent import reidentify_cache, torrents_cache
from app.domain.context import MUSIC_ENTITY_ALBUM, Context, MusicInfo, TorrentInfo
from app.domain.meta.metamusic import MetaMusic
from app.schemas.types import MediaType
from app.foundation.crypto import HashUtils


def _album_context() -> Context:
    """构造带完整专辑身份的缓存上下文。"""
    album = MusicInfo(
        media_source="musicbrainz",
        media_id="release-group-1",
        music_type=MUSIC_ENTITY_ALBUM,
        title="叶惠美",
        artists=["周杰伦"],
    )
    return Context(
        meta_info=MetaMusic.from_music_info(album),
        media_info=album,
        torrent_info=TorrentInfo(
            title="周杰伦 - 叶惠美 FLAC",
            description="11 tracks",
            category=MediaType.MUSIC.value,
            site_name="Music Site",
        ),
    )


def _mock_torrents_chain(context: Context) -> Mock:
    """构造可记录缓存读写的种子链替身。"""
    chain = Mock()
    chain.async_get_torrents = AsyncMock(return_value={"music.example": [context]})
    chain.split_cache_contexts.return_value = ({}, {"music.example": [context]})
    chain.cache_files.return_value = ("video-cache", "music-cache")
    chain.async_save_cache = AsyncMock()
    return chain


def test_torrent_cache_exposes_music_identity_namespace():
    """缓存列表应返回重识别所需的数据源、原生 ID 和实体类型。"""
    context = _album_context()
    torrents_chain = _mock_torrents_chain(context)

    with patch(
        "app.api.endpoints.torrent.TorrentsChain",
        return_value=torrents_chain,
    ):
        response = asyncio.run(torrents_cache(_=Mock()))

    item = response.data["data"][0]
    assert item["media_source"] == "musicbrainz"
    assert item["media_id"] == "release-group-1"
    assert item["music_type"] == MUSIC_ENTITY_ALBUM


def test_torrent_cache_exact_reidentify_forwards_album_namespace():
    """缓存按专辑 ID 重识别时必须在进入 MediaChain 前绑定 album。"""
    context = _album_context()
    torrents_chain = _mock_torrents_chain(context)
    media_chain = Mock()
    media_chain.async_recognize_media = AsyncMock(return_value=context.media_info)
    torrent_hash = HashUtils.md5(
        f"{context.torrent_info.title}{context.torrent_info.description}"
    )

    with patch(
        "app.api.endpoints.torrent.TorrentsChain",
        return_value=torrents_chain,
    ), patch("app.api.endpoints.torrent.MediaChain", return_value=media_chain):
        response = asyncio.run(
            reidentify_cache(
                domain="music.example",
                torrent_hash=torrent_hash,
                media_source="musicbrainz",
                media_id="release-group-1",
                music_type="album",
                _=Mock(),
            )
        )

    assert response.success is True
    recognize_kwargs = media_chain.async_recognize_media.await_args.kwargs
    assert recognize_kwargs["mtype"] == MediaType.MUSIC
    assert recognize_kwargs["music_type"] == MUSIC_ENTITY_ALBUM
    assert recognize_kwargs["media_id"] == "release-group-1"


def test_torrent_cache_auto_reidentify_keeps_music_meta_and_entity():
    """不指定 ID 的专辑重识别应沿用 MetaMusic 和原有 album 命名空间。"""
    context = _album_context()
    torrents_chain = _mock_torrents_chain(context)
    media_chain = Mock()
    media_chain.async_recognize_by_meta = AsyncMock(return_value=context.media_info)
    torrent_hash = HashUtils.md5(
        f"{context.torrent_info.title}{context.torrent_info.description}"
    )

    with patch(
        "app.api.endpoints.torrent.TorrentsChain",
        return_value=torrents_chain,
    ), patch("app.api.endpoints.torrent.MediaChain", return_value=media_chain):
        response = asyncio.run(
            reidentify_cache(
                domain="music.example",
                torrent_hash=torrent_hash,
                _=Mock(),
            )
        )

    assert response.success is True
    meta = media_chain.async_recognize_by_meta.await_args.args[0]
    recognize_kwargs = media_chain.async_recognize_by_meta.await_args.kwargs
    assert meta.type == MediaType.MUSIC
    assert recognize_kwargs["mtype"] == MediaType.MUSIC
    assert recognize_kwargs["music_type"] == MUSIC_ENTITY_ALBUM
