"""Tests for the Web Search plugin."""

from __future__ import annotations

from agent_scout.plugins.base import PluginConfig
from agent_scout.plugins.web_search import (
    RateLimiter,
    SearchProvider,
    SearchResult,
    WebSearchPlugin,
    _deduplicate,
)

# --- Mock providers ---


class MockProvider(SearchProvider):
    """Provider that returns canned results."""

    name = "mock"

    def __init__(self, results: list[SearchResult] | None = None, fail: bool = False) -> None:
        super().__init__()
        self._results = results or []
        self._fail = fail

    async def search(self, query: str, max_results: int = 5) -> list[SearchResult]:
        if self._fail:
            raise ConnectionError("Mock provider failure")
        return self._results[:max_results]


class RateLimitedProvider(MockProvider):
    """Provider that is always rate-limited."""

    name = "rate_limited"

    def is_available(self) -> bool:
        return False


# --- Tests ---


async def test_search_returns_results():
    plugin = WebSearchPlugin()
    await plugin.load()
    await plugin.enable()

    mock = MockProvider(
        results=[
            SearchResult("Flight Deal", "https://example.com/flights", "Cheap flights", "mock"),
            SearchResult("Hotel Deal", "https://example.com/hotels", "Cheap hotels", "mock"),
        ]
    )
    plugin.set_providers([mock])

    result = await plugin.execute("web_search", {"query": "cheap flights"})
    assert "Flight Deal" in result
    assert "Hotel Deal" in result
    assert "example.com/flights" in result


async def test_empty_query():
    plugin = WebSearchPlugin()
    await plugin.load()
    await plugin.enable()
    plugin.set_providers([MockProvider()])

    result = await plugin.execute("web_search", {"query": ""})
    assert "Error" in result


async def test_no_results():
    plugin = WebSearchPlugin()
    await plugin.load()
    await plugin.enable()
    plugin.set_providers([MockProvider(results=[])])

    result = await plugin.execute("web_search", {"query": "obscure query"})
    assert "No search results found" in result


async def test_fallback_on_failure():
    """When primary fails, fallback provider is used."""
    plugin = WebSearchPlugin()
    await plugin.load()
    await plugin.enable()

    failing = MockProvider(fail=True)
    backup = MockProvider(
        results=[SearchResult("Backup Result", "https://backup.com", "Found it", "backup")]
    )
    plugin.set_providers([failing, backup])

    result = await plugin.execute("web_search", {"query": "test"})
    assert "Backup Result" in result


async def test_skip_rate_limited_provider():
    """Rate-limited providers are skipped."""
    plugin = WebSearchPlugin()
    await plugin.load()
    await plugin.enable()

    rate_limited = RateLimitedProvider(
        results=[SearchResult("Blocked", "https://blocked.com", "Nope", "blocked")]
    )
    backup = MockProvider(
        results=[SearchResult("Available", "https://available.com", "Yes", "available")]
    )
    plugin.set_providers([rate_limited, backup])

    result = await plugin.execute("web_search", {"query": "test"})
    assert "Available" in result
    assert "Blocked" not in result


async def test_deduplication():
    results = [
        SearchResult("A", "https://example.com/page", "First", "p1"),
        SearchResult("B", "https://example.com/page/", "Duplicate", "p2"),
        SearchResult("C", "https://other.com", "Different", "p1"),
    ]
    deduped = _deduplicate(results)
    assert len(deduped) == 2
    assert deduped[0].title == "A"
    assert deduped[1].title == "C"


async def test_max_results_respected():
    plugin = WebSearchPlugin()
    await plugin.load()
    await plugin.enable()

    many_results = [
        SearchResult(f"Result {i}", f"https://example.com/{i}", f"Snippet {i}", "mock")
        for i in range(20)
    ]
    plugin.set_providers([MockProvider(results=many_results)])

    result = await plugin.execute("web_search", {"query": "test", "max_results": 3})
    # Should only have 3 numbered results
    assert "4." not in result
    assert "1." in result


async def test_unknown_tool():
    plugin = WebSearchPlugin()
    result = await plugin.execute("unknown", {})
    assert "Unknown tool" in result


async def test_get_tools():
    plugin = WebSearchPlugin()
    tools = plugin.get_tools()
    assert len(tools) == 1
    assert tools[0].name == "web_search"
    assert any(p.name == "query" and p.required for p in tools[0].parameters)


def test_rate_limiter():
    limiter = RateLimiter(max_calls=3, period_seconds=60)
    assert limiter.allow() is True
    assert limiter.allow() is True
    assert limiter.allow() is True
    # 4th call should be blocked
    assert limiter.allow() is False
    assert limiter.calls_remaining == 0


def test_rate_limiter_remaining():
    limiter = RateLimiter(max_calls=5, period_seconds=60)
    assert limiter.calls_remaining == 5
    limiter.allow()
    assert limiter.calls_remaining == 4


async def test_on_load_builds_providers():
    config = PluginConfig(
        settings={
            "tavily_api_key": "tvly-test",
            "serpapi_api_key": "serp-test",
            "brave_api_key": "brave-test",
        }
    )
    plugin = WebSearchPlugin(config=config)
    await plugin.load()

    # Should have 4 providers: DDG + 3 API-based
    assert len(plugin._providers) == 4
