"""Tests for KeywordRetrievalHandler base class."""

from unittest.mock import patch

from nlweb_core.retrieved_item import RetrievedItem
from nlweb_platform_handlers.base import KeywordRetrievalHandler


class ConcreteHandler(KeywordRetrievalHandler):
    """Concrete implementation for testing the abstract base."""

    def __init__(self):
        with patch("nlweb_core.handler.set_request_id", return_value="test-req-id"):
            super().__init__()
        self.retrieve_calls: list[tuple[str, str | None, int]] = []

    async def _retrieve_items(self, query, site, num_results):
        self.retrieve_calls.append((query, site, num_results))
        return [
            RetrievedItem(
                url=f"https://example.com/{query.replace(' ', '-')}",
                raw_schema_object={"@type": "Product", "name": query},
                site=site or "",
            )
        ]


class TestRetrieveWithQueries:
    """Tests for _retrieve_with_queries parallel retrieval."""

    def setup_method(self):
        self.handler = ConcreteHandler()

    async def test_single_query_calls_retrieve_directly(self):
        result = await self.handler._retrieve_with_queries(
            queries=["shoes"], site="example.com", num_results=10
        )
        assert len(result) == 1
        assert self.handler.retrieve_calls == [("shoes", "example.com", 10)]

    async def test_multiple_queries_run_in_parallel(self):
        result = await self.handler._retrieve_with_queries(
            queries=["shoes", "sneakers", "boots"],
            site="example.com",
            num_results=9,
        )
        assert len(result) == 3
        assert len(self.handler.retrieve_calls) == 3
        # Each gets 3 results (9 / 3)
        assert all(call[2] == 3 for call in self.handler.retrieve_calls)

    async def test_deduplicates_by_url(self):
        """Test that duplicate URLs are filtered out."""

        class DupeHandler(ConcreteHandler):
            async def _retrieve_items(self, query, site, num_results):
                return [
                    RetrievedItem(
                        url="https://example.com/same",
                        raw_schema_object={"@type": "Product", "name": query},
                        site=site or "",
                    )
                ]

        handler = DupeHandler()
        result = await handler._retrieve_with_queries(
            queries=["q1", "q2", "q3"], site="example.com", num_results=9
        )
        # All three queries return the same URL, so only one result
        assert len(result) == 1

    async def test_handles_retrieval_errors_gracefully(self):
        """Test that errors in individual queries don't break the whole batch."""

        class FailingHandler(ConcreteHandler):
            async def _retrieve_items(self, query, site, num_results):
                if query == "fail":
                    raise RuntimeError("search failed")
                return [
                    RetrievedItem(
                        url=f"https://example.com/{query}",
                        raw_schema_object={"@type": "Product", "name": query},
                        site=site or "",
                    )
                ]

        handler = FailingHandler()
        result = await handler._retrieve_with_queries(
            queries=["good", "fail", "also-good"],
            site="example.com",
            num_results=9,
        )
        assert len(result) == 2

    async def test_limits_to_num_results(self):
        """Test that combined results are limited to num_results."""

        class ManyResultsHandler(ConcreteHandler):
            async def _retrieve_items(self, query, site, num_results):
                return [
                    RetrievedItem(
                        url=f"https://example.com/{query}/{i}",
                        raw_schema_object={"@type": "Product", "name": f"{query} {i}"},
                        site=site or "",
                    )
                    for i in range(5)
                ]

        handler = ManyResultsHandler()
        result = await handler._retrieve_with_queries(
            queries=["a", "b"], site="example.com", num_results=3
        )
        assert len(result) == 3
