"""
Tests for the SerpAPISearchEngine class.

Tests cover:
- Initialization and configuration
- API key handling
- Language code mapping
- Preview generation
- Full content retrieval
"""

from unittest.mock import Mock, patch, MagicMock
import pytest


class TestSerpAPISearchEngineInit:
    """Tests for SerpAPISearchEngine initialization."""

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

        with patch(
            "local_deep_research.web_search_engines.engines.search_engine_serpapi.SerpAPIWrapper"
        ) as mock_wrapper:
            engine = SerpAPISearchEngine(api_key="test-api-key")

            mock_wrapper.assert_called_once()
            assert engine.max_results == 10
            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_serpapi import (
            SerpAPISearchEngine,
        )

        with patch(
            "local_deep_research.web_search_engines.engines.search_engine_serpapi.SerpAPIWrapper"
        ):
            engine = SerpAPISearchEngine(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_serpapi import (
            SerpAPISearchEngine,
        )

        with patch(
            "local_deep_research.web_search_engines.engines.search_engine_serpapi.SerpAPIWrapper"
        ) as mock_wrapper:
            SerpAPISearchEngine(api_key="test-key", region="gb")

            call_kwargs = mock_wrapper.call_args[1]
            assert call_kwargs["params"]["gl"] == "gb"

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

        with patch(
            "local_deep_research.web_search_engines.engines.search_engine_serpapi.SerpAPIWrapper"
        ) as mock_wrapper:
            SerpAPISearchEngine(api_key="test-key", time_period="m")

            call_kwargs = mock_wrapper.call_args[1]
            assert call_kwargs["params"]["tbs"] == "qdr:m"

    def test_init_with_safe_search_disabled(self):
        """Initialize with safe_search disabled."""
        from local_deep_research.web_search_engines.engines.search_engine_serpapi import (
            SerpAPISearchEngine,
        )

        with patch(
            "local_deep_research.web_search_engines.engines.search_engine_serpapi.SerpAPIWrapper"
        ) as mock_wrapper:
            SerpAPISearchEngine(api_key="test-key", safe_search=False)

            call_kwargs = mock_wrapper.call_args[1]
            assert call_kwargs["params"]["safe"] == "off"

    def test_init_with_language(self):
        """Initialize with specific language."""
        from local_deep_research.web_search_engines.engines.search_engine_serpapi import (
            SerpAPISearchEngine,
        )

        with patch(
            "local_deep_research.web_search_engines.engines.search_engine_serpapi.SerpAPIWrapper"
        ) as mock_wrapper:
            SerpAPISearchEngine(api_key="test-key", search_language="Spanish")

            call_kwargs = mock_wrapper.call_args[1]
            assert call_kwargs["params"]["hl"] == "es"

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

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

    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_serpapi import (
            SerpAPISearchEngine,
        )

        with patch(
            "local_deep_research.web_search_engines.engines.search_engine_serpapi.SerpAPIWrapper"
        ) as mock_wrapper:
            SerpAPISearchEngine(
                settings_snapshot={
                    "search.engine.web.serpapi.api_key": "settings-api-key"
                }
            )

            call_kwargs = mock_wrapper.call_args[1]
            assert call_kwargs["serpapi_api_key"] == "settings-api-key"

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

        mock_llm = Mock()
        with patch(
            "local_deep_research.web_search_engines.engines.search_engine_serpapi.SerpAPIWrapper"
        ):
            engine = SerpAPISearchEngine(api_key="test-key", llm=mock_llm)

            assert engine.llm is mock_llm

    def test_init_with_custom_language_mapping(self):
        """Initialize with custom language code mapping."""
        from local_deep_research.web_search_engines.engines.search_engine_serpapi import (
            SerpAPISearchEngine,
        )

        custom_mapping = {"klingon": "tlh"}

        with patch(
            "local_deep_research.web_search_engines.engines.search_engine_serpapi.SerpAPIWrapper"
        ) as mock_wrapper:
            SerpAPISearchEngine(
                api_key="test-key",
                search_language="klingon",
                language_code_mapping=custom_mapping,
            )

            call_kwargs = mock_wrapper.call_args[1]
            assert call_kwargs["params"]["hl"] == "tlh"


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_serpapi import (
            SerpAPISearchEngine,
        )

        mock_engine = MagicMock()
        mock_engine.results.return_value = {
            "organic_results": [
                {
                    "position": 1,
                    "title": "Result 1",
                    "link": "https://example1.com",
                    "snippet": "Snippet 1",
                    "displayed_link": "example1.com",
                },
                {
                    "position": 2,
                    "title": "Result 2",
                    "link": "https://example2.com",
                    "snippet": "Snippet 2",
                    "displayed_link": "example2.com",
                },
            ]
        }

        with patch(
            "local_deep_research.web_search_engines.engines.search_engine_serpapi.SerpAPIWrapper",
            return_value=mock_engine,
        ):
            engine = SerpAPISearchEngine(api_key="test-key")
            previews = engine._get_previews("test query")

            assert len(previews) == 2
            assert previews[0]["title"] == "Result 1"
            assert previews[0]["link"] == "https://example1.com"
            assert previews[0]["position"] == 1
            assert "_full_result" in previews[0]

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

        mock_engine = MagicMock()
        mock_engine.results.return_value = {"organic_results": []}

        with patch(
            "local_deep_research.web_search_engines.engines.search_engine_serpapi.SerpAPIWrapper",
            return_value=mock_engine,
        ):
            engine = SerpAPISearchEngine(api_key="test-key")
            previews = engine._get_previews("test query")

            assert previews == []

    def test_get_previews_exception(self):
        """Get previews handles exceptions gracefully."""
        from local_deep_research.web_search_engines.engines.search_engine_serpapi import (
            SerpAPISearchEngine,
        )

        mock_engine = MagicMock()
        mock_engine.results.side_effect = Exception("API error")

        with patch(
            "local_deep_research.web_search_engines.engines.search_engine_serpapi.SerpAPIWrapper",
            return_value=mock_engine,
        ):
            engine = SerpAPISearchEngine(api_key="test-key")
            previews = engine._get_previews("test query")

            assert previews == []

    def test_get_previews_rate_limit_error(self):
        """Get previews raises RateLimitError for rate limit exceptions."""
        from local_deep_research.web_search_engines.engines.search_engine_serpapi import (
            SerpAPISearchEngine,
        )
        from local_deep_research.web_search_engines.rate_limiting import (
            RateLimitError,
        )

        mock_engine = MagicMock()
        mock_engine.results.side_effect = Exception(
            "429 Too Many Requests: rate limit exceeded"
        )

        with patch(
            "local_deep_research.web_search_engines.engines.search_engine_serpapi.SerpAPIWrapper",
            return_value=mock_engine,
        ):
            engine = SerpAPISearchEngine(api_key="test-key")

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

    def test_get_previews_rate_limit_error_reraise(self):
        """Get previews re-raises RateLimitError directly."""
        from local_deep_research.web_search_engines.engines.search_engine_serpapi import (
            SerpAPISearchEngine,
        )
        from local_deep_research.web_search_engines.rate_limiting import (
            RateLimitError,
        )

        mock_engine = MagicMock()
        mock_engine.results.side_effect = RateLimitError("Rate limit hit")

        with patch(
            "local_deep_research.web_search_engines.engines.search_engine_serpapi.SerpAPIWrapper",
            return_value=mock_engine,
        ):
            engine = SerpAPISearchEngine(api_key="test-key")

            with pytest.raises(RateLimitError, match="Rate limit hit"):
                engine._get_previews("test query")


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_serpapi import (
            SerpAPISearchEngine,
        )

        with patch(
            "local_deep_research.web_search_engines.engines.search_engine_serpapi.SerpAPIWrapper"
        ):
            engine = SerpAPISearchEngine(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"
            assert "_full_result" not in results[0]

    def test_get_full_content_without_full_result(self):
        """Get full content handles items without _full_result."""
        from local_deep_research.web_search_engines.engines.search_engine_serpapi import (
            SerpAPISearchEngine,
        )

        with patch(
            "local_deep_research.web_search_engines.engines.search_engine_serpapi.SerpAPIWrapper"
        ):
            engine = SerpAPISearchEngine(api_key="test-key")

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

            results = engine._get_full_content(items)

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


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_serpapi import (
            SerpAPISearchEngine,
        )

        with patch(
            "local_deep_research.web_search_engines.engines.search_engine_serpapi.SerpAPIWrapper"
        ):
            engine = SerpAPISearchEngine(api_key="test-key")

            with patch.object(
                engine,
                "_get_previews",
                return_value=[
                    {
                        "id": 1,
                        "title": "Result",
                        "link": "https://example.com",
                        "snippet": "Snippet",
                    }
                ],
            ):
                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_cleans_up_attributes(self):
        """Run cleans up temporary attributes."""
        from local_deep_research.web_search_engines.engines.search_engine_serpapi import (
            SerpAPISearchEngine,
        )

        with patch(
            "local_deep_research.web_search_engines.engines.search_engine_serpapi.SerpAPIWrapper"
        ):
            engine = SerpAPISearchEngine(api_key="test-key")
            engine._search_results = [{"test": "data"}]

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

                assert not hasattr(engine, "_search_results")


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

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

        assert SerpAPISearchEngine.is_public is True

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

        assert SerpAPISearchEngine.is_generic is True
