"""
Tests for the SerperSearchEngine class.

Tests cover:
- Initialization and configuration
- API key handling
- Time period mapping
- Preview generation
- Full content retrieval
- Rate limit error handling
- Run method
"""

from unittest.mock import Mock, patch
import pytest


class TestSerperSearchEngineInit:
    """Tests for SerperSearchEngine initialization."""

    def test_init_with_api_key(self):
        """Initialize with API key."""
        from local_deep_research.web_search_engines.engines.search_engine_serper import (
            SerperSearchEngine,
        )

        engine = SerperSearchEngine(api_key="test-api-key")

        assert engine.api_key == "test-api-key"
        assert engine.max_results == 10
        assert engine.region == "us"
        assert engine.search_language == "en"
        assert engine.include_full_content is False

    def test_init_with_custom_max_results(self):
        """Initialize with custom max_results."""
        from local_deep_research.web_search_engines.engines.search_engine_serper import (
            SerperSearchEngine,
        )

        engine = SerperSearchEngine(api_key="test-key", max_results=25)

        assert engine.max_results == 25

    def test_init_with_custom_region(self):
        """Initialize with custom region."""
        from local_deep_research.web_search_engines.engines.search_engine_serper import (
            SerperSearchEngine,
        )

        engine = SerperSearchEngine(api_key="test-key", region="gb")

        assert engine.region == "gb"

    def test_init_with_time_period(self):
        """Initialize with time period."""
        from local_deep_research.web_search_engines.engines.search_engine_serper import (
            SerperSearchEngine,
        )

        engine = SerperSearchEngine(api_key="test-key", time_period="week")

        assert engine.time_period == "week"

    def test_init_with_safe_search(self):
        """Initialize with safe_search settings."""
        from local_deep_research.web_search_engines.engines.search_engine_serper import (
            SerperSearchEngine,
        )

        engine = SerperSearchEngine(api_key="test-key", safe_search=False)

        assert engine.safe_search is False

    def test_init_without_api_key_raises(self):
        """Initialize without API key raises ValueError."""
        from local_deep_research.web_search_engines.engines.search_engine_serper import (
            SerperSearchEngine,
        )

        with pytest.raises(
            ValueError, match="No valid API key found for Serper"
        ):
            SerperSearchEngine()

    def test_init_with_api_key_from_settings(self):
        """Initialize with API key from settings."""
        from local_deep_research.web_search_engines.engines.search_engine_serper import (
            SerperSearchEngine,
        )

        engine = SerperSearchEngine(
            settings_snapshot={
                "search.engine.web.serper.api_key": "settings-api-key"
            }
        )

        assert engine.api_key == "settings-api-key"

    def test_init_with_llm(self):
        """Initialize with LLM."""
        from local_deep_research.web_search_engines.engines.search_engine_serper import (
            SerperSearchEngine,
        )

        mock_llm = Mock()
        engine = SerperSearchEngine(api_key="test-key", llm=mock_llm)

        assert engine.llm is mock_llm

    def test_init_base_url(self):
        """Initialize sets correct base URL."""
        from local_deep_research.web_search_engines.engines.search_engine_serper import (
            SerperSearchEngine,
        )

        engine = SerperSearchEngine(api_key="test-key")

        assert engine.base_url == "https://google.serper.dev/search"


class TestGetPreviews:
    """Tests for _get_previews method."""

    def test_get_previews_returns_results(self):
        """Get previews returns formatted results."""
        from local_deep_research.web_search_engines.engines.search_engine_serper import (
            SerperSearchEngine,
        )

        mock_response = Mock()
        mock_response.status_code = 200
        mock_response.json.return_value = {
            "organic": [
                {
                    "title": "Result 1",
                    "link": "https://example1.com/page",
                    "snippet": "Snippet 1",
                    "position": 1,
                },
                {
                    "title": "Result 2",
                    "link": "https://example2.com/page",
                    "snippet": "Snippet 2",
                    "position": 2,
                },
            ]
        }

        with patch(
            "local_deep_research.web_search_engines.engines.search_engine_serper.safe_post",
            return_value=mock_response,
        ):
            engine = SerperSearchEngine(api_key="test-key")
            previews = engine._get_previews("test query")

            assert len(previews) == 2
            assert previews[0]["title"] == "Result 1"
            assert previews[0]["snippet"] == "Snippet 1"
            assert previews[0]["link"] == "https://example1.com/page"
            assert previews[0]["displayed_link"] == "example1.com"

    def test_get_previews_with_time_period(self):
        """Get previews includes time period in request."""
        from local_deep_research.web_search_engines.engines.search_engine_serper import (
            SerperSearchEngine,
        )

        mock_response = Mock()
        mock_response.status_code = 200
        mock_response.json.return_value = {"organic": []}

        with patch(
            "local_deep_research.web_search_engines.engines.search_engine_serper.safe_post",
            return_value=mock_response,
        ) as mock_post:
            engine = SerperSearchEngine(api_key="test-key", time_period="week")
            engine._get_previews("test query")

            call_kwargs = mock_post.call_args[1]
            assert call_kwargs["json"]["tbs"] == "qdr:w"

    def test_get_previews_rate_limit_error(self):
        """Get previews raises RateLimitError on 429."""
        from local_deep_research.web_search_engines.engines.search_engine_serper import (
            SerperSearchEngine,
        )
        from local_deep_research.web_search_engines.rate_limiting import (
            RateLimitError,
        )

        mock_response = Mock()
        mock_response.status_code = 429
        mock_response.text = "Rate limit exceeded"

        with patch(
            "local_deep_research.web_search_engines.engines.search_engine_serper.safe_post",
            return_value=mock_response,
        ):
            engine = SerperSearchEngine(api_key="test-key")

            with pytest.raises(RateLimitError):
                engine._get_previews("test query")

    def test_get_previews_empty_results(self):
        """Get previews handles empty results."""
        from local_deep_research.web_search_engines.engines.search_engine_serper import (
            SerperSearchEngine,
        )

        mock_response = Mock()
        mock_response.status_code = 200
        mock_response.json.return_value = {"organic": []}

        with patch(
            "local_deep_research.web_search_engines.engines.search_engine_serper.safe_post",
            return_value=mock_response,
        ):
            engine = SerperSearchEngine(api_key="test-key")
            previews = engine._get_previews("test query")

            assert previews == []

    def test_get_previews_stores_knowledge_graph(self):
        """Get previews stores knowledge graph if present."""
        from local_deep_research.web_search_engines.engines.search_engine_serper import (
            SerperSearchEngine,
        )

        mock_response = Mock()
        mock_response.status_code = 200
        mock_response.json.return_value = {
            "organic": [],
            "knowledgeGraph": {"title": "Test Entity", "type": "Organization"},
        }

        with patch(
            "local_deep_research.web_search_engines.engines.search_engine_serper.safe_post",
            return_value=mock_response,
        ):
            engine = SerperSearchEngine(api_key="test-key")
            engine._get_previews("test query")

            assert hasattr(engine, "_knowledge_graph")
            assert engine._knowledge_graph["title"] == "Test Entity"

    def test_get_previews_request_exception(self):
        """Get previews handles request exceptions."""
        from local_deep_research.web_search_engines.engines.search_engine_serper import (
            SerperSearchEngine,
        )
        import requests

        with patch(
            "local_deep_research.web_search_engines.engines.search_engine_serper.safe_post",
            side_effect=requests.exceptions.RequestException(
                "Connection error"
            ),
        ):
            engine = SerperSearchEngine(api_key="test-key")
            previews = engine._get_previews("test query")

            assert previews == []


class TestGetFullContent:
    """Tests for _get_full_content method."""

    def test_get_full_content_returns_items(self):
        """Get full content returns processed items."""
        from local_deep_research.web_search_engines.engines.search_engine_serper import (
            SerperSearchEngine,
        )

        engine = SerperSearchEngine(api_key="test-key")

        items = [
            {
                "title": "Result",
                "link": "https://example.com",
                "_full_result": {"title": "Result", "extra": "data"},
            }
        ]

        results = engine._get_full_content(items)

        assert len(results) == 1
        assert results[0]["extra"] == "data"

    def test_get_full_content_removes_full_result(self):
        """Get full content removes _full_result field."""
        from local_deep_research.web_search_engines.engines.search_engine_serper import (
            SerperSearchEngine,
        )

        engine = SerperSearchEngine(api_key="test-key")

        items = [
            {
                "title": "Result",
                "link": "https://example.com",
                "_full_result": {"title": "Result"},
            }
        ]

        results = engine._get_full_content(items)

        assert "_full_result" not in results[0]

    def test_get_full_content_includes_knowledge_graph(self):
        """Get full content includes knowledge graph in first result."""
        from local_deep_research.web_search_engines.engines.search_engine_serper import (
            SerperSearchEngine,
        )

        engine = SerperSearchEngine(api_key="test-key")
        engine._knowledge_graph = {"title": "Test Entity"}

        items = [{"title": "Result", "link": "https://example.com"}]

        results = engine._get_full_content(items)

        assert "knowledge_graph" in results[0]
        assert results[0]["knowledge_graph"]["title"] == "Test Entity"

    def test_get_full_content_extracts_full_result_with_knowledge_graph(self):
        """Get full content extracts _full_result via super() and adds knowledge graph."""
        from local_deep_research.web_search_engines.engines.search_engine_serper import (
            SerperSearchEngine,
        )

        engine = SerperSearchEngine(api_key="test-key")
        engine._knowledge_graph = {
            "title": "Test Entity",
            "type": "Organization",
        }

        items = [
            {
                "title": "Result",
                "link": "https://example.com",
                "_full_result": {
                    "title": "Result",
                    "link": "https://example.com",
                    "snippet": "Full snippet",
                    "position": 1,
                },
            }
        ]

        results = engine._get_full_content(items)

        assert len(results) == 1
        # Verify _full_result extraction from base class
        assert results[0]["snippet"] == "Full snippet"
        assert results[0]["position"] == 1
        assert "_full_result" not in results[0]
        # Verify knowledge graph added by override
        assert results[0]["knowledge_graph"]["title"] == "Test Entity"
        assert results[0]["knowledge_graph"]["type"] == "Organization"

    def test_get_full_content_no_knowledge_graph_on_second_result(self):
        """Get full content only adds knowledge graph to first result."""
        from local_deep_research.web_search_engines.engines.search_engine_serper import (
            SerperSearchEngine,
        )

        engine = SerperSearchEngine(api_key="test-key")
        engine._knowledge_graph = {"title": "Test Entity"}

        items = [
            {"title": "Result 1", "link": "https://example1.com"},
            {"title": "Result 2", "link": "https://example2.com"},
        ]

        results = engine._get_full_content(items)

        assert "knowledge_graph" in results[0]
        assert "knowledge_graph" not in results[1]


class TestRun:
    """Tests for run method."""

    def test_run_returns_results(self):
        """Run returns search results."""
        from local_deep_research.web_search_engines.engines.search_engine_serper import (
            SerperSearchEngine,
        )

        engine = SerperSearchEngine(api_key="test-key")

        with patch.object(
            engine,
            "_get_previews",
            return_value=[
                {
                    "id": 0,
                    "title": "Result",
                    "snippet": "Snippet",
                    "link": "https://example.com",
                }
            ],
        ):
            with patch.object(
                engine,
                "_get_full_content",
                return_value=[{"title": "Result", "content": "Full"}],
            ):
                results = engine.run("test query")

                assert len(results) == 1

    def test_run_handles_empty_results(self):
        """Run handles empty results."""
        from local_deep_research.web_search_engines.engines.search_engine_serper import (
            SerperSearchEngine,
        )

        engine = SerperSearchEngine(api_key="test-key")

        with patch.object(engine, "_get_previews", return_value=[]):
            results = engine.run("test query")

            assert results == []

    def test_run_cleans_up_attributes(self):
        """Run cleans up temporary attributes."""
        from local_deep_research.web_search_engines.engines.search_engine_serper import (
            SerperSearchEngine,
        )

        engine = SerperSearchEngine(api_key="test-key")
        engine._search_results = [{"test": "data"}]
        engine._knowledge_graph = {"title": "Test"}
        engine._related_searches = []
        engine._people_also_ask = []

        with patch.object(engine, "_get_previews", return_value=[]):
            engine.run("test query")

            assert not hasattr(engine, "_search_results")
            assert not hasattr(engine, "_knowledge_graph")
            assert not hasattr(engine, "_related_searches")
            assert not hasattr(engine, "_people_also_ask")


class TestClassAttributes:
    """Tests for class attributes."""

    def test_is_public(self):
        """SerperSearchEngine is marked as public."""
        from local_deep_research.web_search_engines.engines.search_engine_serper import (
            SerperSearchEngine,
        )

        assert SerperSearchEngine.is_public is True

    def test_is_generic(self):
        """SerperSearchEngine is marked as generic."""
        from local_deep_research.web_search_engines.engines.search_engine_serper import (
            SerperSearchEngine,
        )

        assert SerperSearchEngine.is_generic is True

    def test_class_constants(self):
        """Class constants are set correctly."""
        from local_deep_research.web_search_engines.engines.search_engine_serper import (
            SerperSearchEngine,
        )

        assert SerperSearchEngine.BASE_URL == "https://google.serper.dev/search"
        assert SerperSearchEngine.DEFAULT_TIMEOUT == 30
        assert SerperSearchEngine.DEFAULT_REGION == "us"
        assert SerperSearchEngine.DEFAULT_LANGUAGE == "en"
