import asyncio
import json
from unittest.mock import MagicMock, patch

from app.agent.tools.impl.query_download_tasks import QueryDownloadTasksTool
from app.schemas import DownloaderTorrent


def test_completed_status_returns_qbittorrent_and_transmission_completed_states():
    """
    按完成状态查询时应包含 QB/TR 中非下载中、非暂停的实际状态。
    """
    completed_torrents = [
        DownloaderTorrent(
            downloader="qb",
            hash="hash-qb",
            title="QB Done",
            size=1024,
            progress=100,
            state="completed",
            tags="moviepilot",
        ),
        DownloaderTorrent(
            downloader="tr",
            hash="hash-tr",
            title="TR Done",
            size=2048,
            progress=100,
            state="completed",
            tags="moviepilot",
        ),
    ]
    download_chain = MagicMock()
    download_chain.list_torrents.return_value = completed_torrents

    with patch(
        "app.agent.tools.impl.query_download_tasks.DownloadChain",
        return_value=download_chain,
    ), patch.object(
        QueryDownloadTasksTool,
        "_load_history_map",
        return_value={},
    ):
        result = QueryDownloadTasksTool._query_downloads_sync(status="completed")

    assert result["downloads"] == completed_torrents
    download_chain.list_torrents.assert_called_once_with(
        downloader=None,
        status="completed",
        include_all_tags=False,
    )


def test_run_completed_status_formats_completed_download_tasks():
    """
    工具输出应保留完成任务的实际下载器状态，便于用户判断来源。
    """
    completed_torrents = [
        DownloaderTorrent(
            downloader="qb",
            hash="hash-qb",
            title="QB Done",
            size=1024,
            progress=100,
            state="completed",
            tags="moviepilot",
            save_path="/downloads",
            content_path="/downloads/QB Done",
            category="电影",
            download_limit=1024,
            upload_limit=512,
            ratio_limit=2.0,
            seeding_time_limit=1440,
            trackers=["https://tracker.example/announce"],
        )
    ]

    with patch.object(
        QueryDownloadTasksTool,
        "_query_downloads_sync",
        return_value={"downloads": completed_torrents},
    ):
        result = asyncio.run(
            QueryDownloadTasksTool(session_id="session-1", user_id="10001").run(
                status="completed"
            )
        )

    payload = json.loads(result)
    assert payload[0]["hash"] == "hash-qb"
    assert payload[0]["state"] == "completed"
    assert payload[0]["save_path"] == "/downloads"
    assert payload[0]["content_path"] == "/downloads/QB Done"
    assert payload[0]["category"] == "电影"
    assert payload[0]["download_limit"] == 1024
    assert payload[0]["upload_limit"] == 512
    assert payload[0]["ratio_limit"] == 2.0
    assert payload[0]["seeding_time_limit"] == 1440
    assert payload[0]["trackers"] == ["https://tracker.example/announce"]


def test_hash_query_loads_trackers_for_matching_task():
    """
    按 Hash 查询详情时应额外加载下载器支持的 Tracker 列表。
    """
    torrent = DownloaderTorrent(
        downloader="qb",
        hash="a" * 40,
        title="Task With Trackers",
        size=1024,
        progress=10,
        state="downloading",
        tags="moviepilot",
    )
    download_chain = MagicMock()
    download_chain.list_torrents.return_value = [torrent]
    download_chain.get_torrent_trackers.return_value = {
        "qb": ["https://tracker.example/announce"]
    }

    with patch(
        "app.agent.tools.impl.query_download_tasks.DownloadChain",
        return_value=download_chain,
    ), patch.object(
        QueryDownloadTasksTool,
        "_load_history_map",
        return_value={},
    ):
        result = QueryDownloadTasksTool._query_downloads_sync(hash_value="a" * 40)

    assert result["downloads"][0].trackers == ["https://tracker.example/announce"]
    download_chain.get_torrent_trackers.assert_called_once_with(
        hash_string="a" * 40,
        downloader="qb",
    )


def test_include_all_tags_passes_scope_to_downloader_query():
    """
    智能体显式扩大范围时，应查询未打 MoviePilot 内置标签的下载任务。
    """
    all_scope_torrents = [
        DownloaderTorrent(
            downloader="qb",
            hash="hash-external",
            title="External Task",
            size=1024,
            progress=10,
            state="downloading",
            tags="external",
        )
    ]
    download_chain = MagicMock()
    download_chain.list_torrents.return_value = all_scope_torrents

    with patch(
        "app.agent.tools.impl.query_download_tasks.DownloadChain",
        return_value=download_chain,
    ), patch.object(
        QueryDownloadTasksTool,
        "_load_history_map",
        return_value={},
    ):
        result = QueryDownloadTasksTool._query_downloads_sync(
            status="all",
            include_all_tags=True,
        )

    assert result["downloads"] == all_scope_torrents
    download_chain.list_torrents.assert_called_once_with(
        downloader=None,
        status=None,
        include_all_tags=True,
    )


def test_include_all_tags_downloading_status_uses_list_torrents():
    """
    查询全部标签范围的下载中任务时，不应走只面向 MoviePilot 任务的便捷方法。
    """
    download_chain = MagicMock()
    download_chain.list_torrents.return_value = [
        DownloaderTorrent(
            downloader="tr",
            hash="hash-downloading",
            title="Downloading External",
            size=2048,
            progress=50,
            state="downloading",
            tags="external",
        )
    ]

    with patch(
        "app.agent.tools.impl.query_download_tasks.DownloadChain",
        return_value=download_chain,
    ), patch.object(
        QueryDownloadTasksTool,
        "_load_history_map",
        return_value={},
    ):
        result = QueryDownloadTasksTool._query_downloads_sync(
            status="downloading",
            include_all_tags=True,
        )

    assert result["downloads"][0].hash == "hash-downloading"
    download_chain.downloading.assert_not_called()
    download_chain.list_torrents.assert_called_once_with(
        downloader=None,
        status="downloading",
        include_all_tags=True,
    )


def test_include_all_tags_false_string_keeps_builtin_tag_scope():
    """
    CLI 字符串 false 不应被 Python 真值规则误判为扩大查询范围。
    """
    download_chain = MagicMock()
    download_chain.list_torrents.return_value = [
        DownloaderTorrent(
            downloader="qb",
            hash="hash-moviepilot",
            title="MoviePilot Task",
            size=1024,
            progress=100,
            state="completed",
            tags="moviepilot",
        )
    ]

    with patch(
        "app.agent.tools.impl.query_download_tasks.DownloadChain",
        return_value=download_chain,
    ), patch.object(
        QueryDownloadTasksTool,
        "_load_history_map",
        return_value={},
    ):
        result = QueryDownloadTasksTool._query_downloads_sync(
            status="completed",
            include_all_tags="false",
        )

    assert result["downloads"][0].hash == "hash-moviepilot"
    download_chain.list_torrents.assert_called_once_with(
        downloader=None,
        status="completed",
        include_all_tags=False,
    )
