import asyncio
from types import SimpleNamespace
from unittest.mock import AsyncMock, patch

from app.chain.search import SearchChain
from app.modules.indexer import IndexerModule
from app.runtime.config import settings


def make_chain() -> SearchChain:
    """构造不触发完整启动流程的搜索链。"""
    chain = object.__new__(SearchChain)
    chain.get_search_page_size = IndexerModule.get_search_page_size
    return chain


def test_search_returns_plugin_results_without_indexer_sites():
    """未配置 PT 站点时，插件资源仍应进入原生资源搜索。"""
    chain = make_chain()
    plugin_item = SimpleNamespace(title="Plugin Result", description="")
    calls = []
    chain.search_plugin_torrents = lambda **kwargs: calls.append(kwargs) or [plugin_item]

    with (
        patch("app.chain.search.get_configured_system_config") as system_config_oper,
        patch("app.chain.search.SitesHelper") as sites_helper,
    ):
        system_config_oper.return_value.get.return_value = []
        sites_helper.return_value.get_indexers.return_value = []
        results = chain._SearchChain__search_all_sites(keyword="keyword")

    assert results == [plugin_item]
    assert len(calls) == 1
    assert calls[0]["keyword"] == "keyword"


def test_search_invokes_plugin_once_with_multiple_indexers():
    """多个 PT 站点不应导致插件资源源被重复搜索。"""
    chain = make_chain()
    plugin_calls = []
    site_calls = []
    chain.search_plugin_torrents = lambda **kwargs: plugin_calls.append(kwargs) or [
        SimpleNamespace(title="Plugin Result", description="")
    ]
    chain.search_site_torrents = lambda **kwargs: site_calls.append(kwargs) or [
        SimpleNamespace(title=f"Site {kwargs['site']['id']}", description="")
    ]

    with (
        patch.object(settings, "SEARCH_RESOURCE_PAGES", 1, create=True),
        patch("app.chain.search.get_configured_system_config") as system_config_oper,
        patch("app.chain.search.SitesHelper") as sites_helper,
        patch("app.chain.search.ProgressHelper") as progress_helper,
    ):
        system_config_oper.return_value.get.return_value = [1, 2]
        sites_helper.return_value.get_indexers.return_value = [
            {"id": 1, "name": "站点一"},
            {"id": 2, "name": "站点二"},
        ]
        progress_helper.return_value = SimpleNamespace(
            start=lambda: None, update=lambda **_kwargs: None, end=lambda: None
        )
        results = chain._SearchChain__search_all_sites(keyword="keyword")

    assert len(plugin_calls) == 1
    assert sorted(call["site"]["id"] for call in site_calls) == [1, 2]
    assert len(results) == 3


def test_async_search_returns_plugin_results_without_indexers():
    """异步搜索应支持只有插件资源源的部署方式。"""
    chain = make_chain()
    plugin_item = SimpleNamespace(title="Plugin Result", description="")
    calls = []

    async def plugin_search(**kwargs):
        calls.append(kwargs)
        return [plugin_item]

    chain.async_search_plugin_torrents = plugin_search
    async def run_search():
        with (
            patch("app.chain.search.get_configured_system_config") as system_config_oper,
            patch("app.chain.search.SitesHelper") as sites_helper,
        ):
            system_config_oper.return_value.get.return_value = []
            sites_helper.return_value.async_get_indexers = AsyncMock(return_value=[])
            return await chain._SearchChain__async_search_all_sites(keyword="keyword")

    results = asyncio.run(run_search())

    assert results == [plugin_item]
    assert len(calls) == 1


def test_async_search_stream_emits_plugin_results_once_without_indexers():
    """流式搜索完成事件不应重复发送插件资源。"""
    chain = make_chain()
    plugin_item = SimpleNamespace(title="Plugin Result", description="")

    async def plugin_search(**_kwargs):
        return [plugin_item]

    chain.async_search_plugin_torrents = plugin_search
    async def collect_events():
        with (
            patch("app.chain.search.get_configured_system_config") as system_config_oper,
            patch("app.chain.search.SitesHelper") as sites_helper,
        ):
            system_config_oper.return_value.get.return_value = []
            sites_helper.return_value.async_get_indexers = AsyncMock(return_value=[])
            return [
                event
                async for event in chain._SearchChain__async_search_all_sites_stream(
                    keyword="keyword"
                )
            ]

    events = asyncio.run(collect_events())

    assert [event["type"] for event in events] == ["append", "done"]
    assert events[0]["items"] == [plugin_item]
    assert events[1]["items"] == []
    assert events[1]["total_items"] == 1
