"""
Comprehensive tests for the Serper search engine (Google via Serper API).
Tests initialization, search functionality, and result formatting.

Note: These tests mock HTTP requests to avoid requiring an API key.
"""

import pytest
from unittest.mock import Mock


class TestSerperSearchEngineInit:
    """Tests for Serper search engine initialization."""

    def test_init_default_values(self):
        """Test initialization with default values."""
        from local_deep_research.web_search_engines.engines.search_engine_serper import (
            SerperSearchEngine,
        )

        engine = SerperSearchEngine(api_key="test_key")

        assert engine.max_results == 10
        assert engine.region == "us"
        assert engine.search_language == "en"
        assert engine.safe_search is True
        assert engine.is_public is True
        assert engine.is_generic is True

    def test_init_with_custom_max_results(self):
        """Test initialization 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_api_key(self):
        """Test initialization with API key."""
        from local_deep_research.web_search_engines.engines.search_engine_serper import (
            SerperSearchEngine,
        )

        engine = SerperSearchEngine(api_key="my_api_key")
        assert engine.api_key == "my_api_key"

    def test_init_without_api_key_raises_error(self, monkeypatch):
        """Test that initialization without API key raises ValueError."""
        from local_deep_research.web_search_engines.engines.search_engine_serper import (
            SerperSearchEngine,
        )

        monkeypatch.setattr(
            "local_deep_research.config.search_config.get_setting_from_snapshot",
            Mock(return_value=None),
        )

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

    def test_init_with_region(self):
        """Test initialization 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):
        """Test initialization with time period filter."""
        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_disabled(self):
        """Test initialization with safe search disabled."""
        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_with_language(self):
        """Test initialization with different language."""
        from local_deep_research.web_search_engines.engines.search_engine_serper import (
            SerperSearchEngine,
        )

        engine = SerperSearchEngine(api_key="test_key", search_language="es")
        assert engine.search_language == "es"

    def test_base_url_set(self):
        """Test that base URL is correctly set."""
        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 TestSerperEngineType:
    """Tests for Serper engine type identification."""

    def test_engine_type_set(self):
        """Test that engine type is properly set."""
        from local_deep_research.web_search_engines.engines.search_engine_serper import (
            SerperSearchEngine,
        )

        engine = SerperSearchEngine(api_key="test_key")
        assert "serper" in engine.engine_type.lower()

    def test_engine_is_public(self):
        """Test that engine is marked as public."""
        from local_deep_research.web_search_engines.engines.search_engine_serper import (
            SerperSearchEngine,
        )

        engine = SerperSearchEngine(api_key="test_key")
        assert engine.is_public is True

    def test_engine_is_generic(self):
        """Test that engine is marked as generic."""
        from local_deep_research.web_search_engines.engines.search_engine_serper import (
            SerperSearchEngine,
        )

        engine = SerperSearchEngine(api_key="test_key")
        assert engine.is_generic is True


class TestSerperSearchExecution:
    """Tests for Serper search execution."""

    @pytest.fixture
    def engine(self):
        """Create a Serper engine."""
        from local_deep_research.web_search_engines.engines.search_engine_serper import (
            SerperSearchEngine,
        )

        return SerperSearchEngine(api_key="test_key", max_results=10)

    def test_get_previews_success(self, engine, monkeypatch):
        """Test successful preview retrieval."""
        mock_response = Mock()
        mock_response.status_code = 200
        mock_response.raise_for_status = Mock()
        mock_response.json = Mock(
            return_value={
                "organic": [
                    {
                        "title": "Test Result",
                        "link": "https://example.com/page",
                        "snippet": "This is a test snippet.",
                        "position": 1,
                    },
                    {
                        "title": "Second Result",
                        "link": "https://test.org/article",
                        "snippet": "Another snippet.",
                        "position": 2,
                    },
                ]
            }
        )

        monkeypatch.setattr(
            "local_deep_research.web_search_engines.engines.search_engine_serper.safe_post",
            Mock(return_value=mock_response),
        )

        previews = engine._get_previews("test query")

        assert len(previews) == 2
        assert previews[0]["title"] == "Test Result"
        assert previews[0]["link"] == "https://example.com/page"
        assert previews[0]["snippet"] == "This is a test snippet."

    def test_get_previews_with_knowledge_graph(self, engine, monkeypatch):
        """Test preview retrieval with knowledge graph."""
        mock_response = Mock()
        mock_response.status_code = 200
        mock_response.raise_for_status = Mock()
        mock_response.json = Mock(
            return_value={
                "organic": [
                    {
                        "title": "Test Result",
                        "link": "https://example.com",
                        "snippet": "Test",
                    }
                ],
                "knowledgeGraph": {
                    "title": "Test Entity",
                    "type": "Organization",
                    "description": "A test entity",
                },
            }
        )

        monkeypatch.setattr(
            "local_deep_research.web_search_engines.engines.search_engine_serper.safe_post",
            Mock(return_value=mock_response),
        )

        engine._get_previews("test query")

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

    def test_get_previews_empty_results(self, engine, monkeypatch):
        """Test preview retrieval with no results."""
        mock_response = Mock()
        mock_response.status_code = 200
        mock_response.raise_for_status = Mock()
        mock_response.json = Mock(return_value={"organic": []})

        monkeypatch.setattr(
            "local_deep_research.web_search_engines.engines.search_engine_serper.safe_post",
            Mock(return_value=mock_response),
        )

        previews = engine._get_previews("nonexistent topic xyz123")

        assert previews == []

    def test_get_previews_rate_limit_error(self, engine, monkeypatch):
        """Test that 429 errors raise RateLimitError."""
        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"

        monkeypatch.setattr(
            "local_deep_research.web_search_engines.engines.search_engine_serper.safe_post",
            Mock(return_value=mock_response),
        )

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

    def test_get_previews_handles_request_exception(self, engine, monkeypatch):
        """Test that request exceptions are handled gracefully."""
        import requests

        monkeypatch.setattr(
            "local_deep_research.web_search_engines.engines.search_engine_serper.safe_post",
            Mock(
                side_effect=requests.exceptions.RequestException(
                    "Network error"
                )
            ),
        )

        previews = engine._get_previews("test query")
        assert previews == []

    def test_get_previews_handles_general_exception(self, engine, monkeypatch):
        """Test that general exceptions are handled gracefully."""
        monkeypatch.setattr(
            "local_deep_research.web_search_engines.engines.search_engine_serper.safe_post",
            Mock(side_effect=Exception("Unexpected error")),
        )

        previews = engine._get_previews("test query")
        assert previews == []


class TestSerperTimePeriodMapping:
    """Tests for Serper time period mapping."""

    @pytest.fixture
    def engine(self):
        """Create a Serper engine."""
        from local_deep_research.web_search_engines.engines.search_engine_serper import (
            SerperSearchEngine,
        )

        return SerperSearchEngine(api_key="test_key", time_period="day")

    def test_time_period_day_mapping(self, engine, monkeypatch):
        """Test time period 'day' is mapped correctly."""
        captured_payload = {}

        def capture_post(url, headers=None, json=None, timeout=None):
            captured_payload.update(json)
            mock_response = Mock()
            mock_response.status_code = 200
            mock_response.raise_for_status = Mock()
            mock_response.json = Mock(return_value={"organic": []})
            return mock_response

        monkeypatch.setattr(
            "local_deep_research.web_search_engines.engines.search_engine_serper.safe_post",
            capture_post,
        )

        engine._get_previews("test")

        assert "tbs" in captured_payload
        assert captured_payload["tbs"] == "qdr:d"


class TestSerperFullContent:
    """Tests for Serper full content retrieval."""

    @pytest.fixture
    def engine(self):
        """Create a Serper engine."""
        from local_deep_research.web_search_engines.engines.search_engine_serper import (
            SerperSearchEngine,
        )

        return SerperSearchEngine(api_key="test_key")

    def test_get_full_content_snippets_only_mode(self, engine, monkeypatch):
        """Test full content retrieval in snippets only mode."""
        # Mock the search_config module that gets imported inside the method
        mock_config = Mock()
        mock_config.SEARCH_SNIPPETS_ONLY = True

        monkeypatch.setattr(
            "local_deep_research.config.search_config.SEARCH_SNIPPETS_ONLY",
            True,
            raising=False,
        )

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

        results = engine._get_full_content(items)

        assert len(results) == 1
        assert results[0]["title"] == "Test Result"
        assert "_full_result" not in results[0]

    def test_get_full_content_without_full_result(self, engine, monkeypatch):
        """Test full content retrieval when _full_result is not available."""
        # Mock to simulate SEARCH_SNIPPETS_ONLY not existing
        import local_deep_research.config.search_config as search_config_module

        # Remove the attribute if it exists
        if hasattr(search_config_module, "SEARCH_SNIPPETS_ONLY"):
            monkeypatch.delattr(
                "local_deep_research.config.search_config.SEARCH_SNIPPETS_ONLY",
                raising=False,
            )

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

        results = engine._get_full_content(items)

        assert len(results) == 1
        assert results[0]["title"] == "Test Result"


class TestSerperURLParsing:
    """Tests for Serper URL parsing in results."""

    @pytest.fixture
    def engine(self):
        """Create a Serper engine."""
        from local_deep_research.web_search_engines.engines.search_engine_serper import (
            SerperSearchEngine,
        )

        return SerperSearchEngine(api_key="test_key")

    def test_display_link_extraction(self, engine, monkeypatch):
        """Test that display link is extracted from URL."""
        mock_response = Mock()
        mock_response.status_code = 200
        mock_response.raise_for_status = Mock()
        mock_response.json = Mock(
            return_value={
                "organic": [
                    {
                        "title": "Test",
                        "link": "https://www.example.com/path/to/page",
                        "snippet": "Test",
                    }
                ]
            }
        )

        monkeypatch.setattr(
            "local_deep_research.web_search_engines.engines.search_engine_serper.safe_post",
            Mock(return_value=mock_response),
        )

        previews = engine._get_previews("test")

        assert previews[0]["displayed_link"] == "www.example.com"

    def test_display_link_empty_for_invalid_url(self, engine, monkeypatch):
        """Test that display link is empty for results without link."""
        mock_response = Mock()
        mock_response.status_code = 200
        mock_response.raise_for_status = Mock()
        mock_response.json = Mock(
            return_value={"organic": [{"title": "Test", "snippet": "Test"}]}
        )

        monkeypatch.setattr(
            "local_deep_research.web_search_engines.engines.search_engine_serper.safe_post",
            Mock(return_value=mock_response),
        )

        previews = engine._get_previews("test")

        assert previews[0]["displayed_link"] == ""


class TestSerperAdditionalFeatures:
    """Tests for Serper additional features like sitelinks and dates."""

    @pytest.fixture
    def engine(self):
        """Create a Serper engine."""
        from local_deep_research.web_search_engines.engines.search_engine_serper import (
            SerperSearchEngine,
        )

        return SerperSearchEngine(api_key="test_key")

    def test_sitelinks_included(self, engine, monkeypatch):
        """Test that sitelinks are included in results."""
        mock_response = Mock()
        mock_response.status_code = 200
        mock_response.raise_for_status = Mock()
        mock_response.json = Mock(
            return_value={
                "organic": [
                    {
                        "title": "Test",
                        "link": "https://example.com",
                        "snippet": "Test",
                        "sitelinks": [
                            {
                                "title": "About",
                                "link": "https://example.com/about",
                            }
                        ],
                    }
                ]
            }
        )

        monkeypatch.setattr(
            "local_deep_research.web_search_engines.engines.search_engine_serper.safe_post",
            Mock(return_value=mock_response),
        )

        previews = engine._get_previews("test")

        assert "sitelinks" in previews[0]
        assert previews[0]["sitelinks"][0]["title"] == "About"

    def test_date_included(self, engine, monkeypatch):
        """Test that date is included in results."""
        mock_response = Mock()
        mock_response.status_code = 200
        mock_response.raise_for_status = Mock()
        mock_response.json = Mock(
            return_value={
                "organic": [
                    {
                        "title": "Test",
                        "link": "https://example.com",
                        "snippet": "Test",
                        "date": "2024-01-15",
                    }
                ]
            }
        )

        monkeypatch.setattr(
            "local_deep_research.web_search_engines.engines.search_engine_serper.safe_post",
            Mock(return_value=mock_response),
        )

        previews = engine._get_previews("test")

        assert "date" in previews[0]
        assert previews[0]["date"] == "2024-01-15"

    def test_related_searches_stored(self, engine, monkeypatch):
        """Test that related searches are stored."""
        mock_response = Mock()
        mock_response.status_code = 200
        mock_response.raise_for_status = Mock()
        mock_response.json = Mock(
            return_value={
                "organic": [
                    {
                        "title": "Test",
                        "link": "https://example.com",
                        "snippet": "Test",
                    }
                ],
                "relatedSearches": [{"query": "related query"}],
            }
        )

        monkeypatch.setattr(
            "local_deep_research.web_search_engines.engines.search_engine_serper.safe_post",
            Mock(return_value=mock_response),
        )

        engine._get_previews("test")

        assert hasattr(engine, "_related_searches")
        assert engine._related_searches[0]["query"] == "related query"
