"""
Tests for the GooglePSESearchEngine class.

Tests cover:
- Initialization and configuration
- API key and engine ID handling
- Language and region settings
- Rate limiting
- Retry logic
- Preview generation
- Connection validation
"""

from unittest.mock import Mock, patch
import pytest


class TestGooglePSESearchEngineInit:
    """Tests for GooglePSESearchEngine initialization."""

    def test_init_with_credentials(self):
        """Initialize with API key and engine ID."""
        from local_deep_research.web_search_engines.engines.search_engine_google_pse import (
            GooglePSESearchEngine,
        )

        with patch.object(GooglePSESearchEngine, "_validate_connection"):
            engine = GooglePSESearchEngine(
                api_key="test-api-key", search_engine_id="test-engine-id"
            )

            assert engine.api_key == "test-api-key"
            assert engine.search_engine_id == "test-engine-id"
            assert engine.max_results == 10

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

        with patch.object(GooglePSESearchEngine, "_validate_connection"):
            engine = GooglePSESearchEngine(
                api_key="test-key",
                search_engine_id="test-id",
                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_google_pse import (
            GooglePSESearchEngine,
        )

        with patch.object(GooglePSESearchEngine, "_validate_connection"):
            engine = GooglePSESearchEngine(
                api_key="test-key",
                search_engine_id="test-id",
                region="uk",
            )

            assert engine.region == "uk"

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

        with patch.object(GooglePSESearchEngine, "_validate_connection"):
            engine = GooglePSESearchEngine(
                api_key="test-key",
                search_engine_id="test-id",
                safe_search=False,
            )

            assert engine.safe == "off"

    def test_init_with_safe_search_enabled(self):
        """Initialize with safe_search enabled."""
        from local_deep_research.web_search_engines.engines.search_engine_google_pse import (
            GooglePSESearchEngine,
        )

        with patch.object(GooglePSESearchEngine, "_validate_connection"):
            engine = GooglePSESearchEngine(
                api_key="test-key",
                search_engine_id="test-id",
                safe_search=True,
            )

            assert engine.safe == "active"

    def test_init_with_language(self):
        """Initialize with custom language."""
        from local_deep_research.web_search_engines.engines.search_engine_google_pse import (
            GooglePSESearchEngine,
        )

        with patch.object(GooglePSESearchEngine, "_validate_connection"):
            engine = GooglePSESearchEngine(
                api_key="test-key",
                search_engine_id="test-id",
                search_language="Spanish",
            )

            assert engine.language == "es"

    def test_init_with_unknown_language_defaults_to_english(self):
        """Initialize with unknown language defaults to English."""
        from local_deep_research.web_search_engines.engines.search_engine_google_pse import (
            GooglePSESearchEngine,
        )

        with patch.object(GooglePSESearchEngine, "_validate_connection"):
            engine = GooglePSESearchEngine(
                api_key="test-key",
                search_engine_id="test-id",
                search_language="Klingon",
            )

            assert engine.language == "en"

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

        with patch(
            "local_deep_research.config.thread_settings.get_setting_from_snapshot",
            return_value=None,
        ):
            with pytest.raises(ValueError) as exc_info:
                GooglePSESearchEngine(search_engine_id="test-id")

            assert "Google API key is required" in str(exc_info.value)

    def test_init_without_engine_id_raises(self):
        """Initialize without engine ID raises ValueError."""
        from local_deep_research.web_search_engines.engines.search_engine_google_pse import (
            GooglePSESearchEngine,
        )

        with patch(
            "local_deep_research.config.thread_settings.get_setting_from_snapshot",
            return_value=None,
        ):
            with pytest.raises(ValueError) as exc_info:
                GooglePSESearchEngine(api_key="test-key")

            assert "Google Search Engine ID is required" in str(exc_info.value)

    def test_init_with_retry_settings(self):
        """Initialize with custom retry settings."""
        from local_deep_research.web_search_engines.engines.search_engine_google_pse import (
            GooglePSESearchEngine,
        )

        with patch.object(GooglePSESearchEngine, "_validate_connection"):
            engine = GooglePSESearchEngine(
                api_key="test-key",
                search_engine_id="test-id",
                max_retries=5,
                retry_delay=3.0,
            )

            assert engine.max_retries == 5
            assert engine.retry_delay == 3.0

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

        mock_llm = Mock()
        with patch.object(GooglePSESearchEngine, "_validate_connection"):
            engine = GooglePSESearchEngine(
                api_key="test-key",
                search_engine_id="test-id",
                llm=mock_llm,
            )

            assert engine.llm is mock_llm


class TestMakeRequest:
    """Tests for _make_request method."""

    def test_make_request_returns_response(self):
        """Make request returns JSON response."""
        from local_deep_research.web_search_engines.engines.search_engine_google_pse import (
            GooglePSESearchEngine,
        )

        mock_response = Mock()
        mock_response.json.return_value = {"items": []}
        mock_response.raise_for_status = Mock()

        with patch.object(GooglePSESearchEngine, "_validate_connection"):
            with patch(
                "local_deep_research.web_search_engines.engines.search_engine_google_pse.safe_get",
                return_value=mock_response,
            ):
                engine = GooglePSESearchEngine(
                    api_key="test-key", search_engine_id="test-id"
                )
                result = engine._make_request("test query")

                assert result == {"items": []}

    def test_make_request_rate_limit_error(self):
        """Make request raises RateLimitError on quota exceeded."""
        from local_deep_research.web_search_engines.engines.search_engine_google_pse import (
            GooglePSESearchEngine,
        )
        from local_deep_research.web_search_engines.rate_limiting import (
            RateLimitError,
        )
        from requests.exceptions import RequestException

        with patch.object(GooglePSESearchEngine, "_validate_connection"):
            with patch(
                "local_deep_research.web_search_engines.engines.search_engine_google_pse.safe_get",
                side_effect=RequestException("quotaExceeded"),
            ):
                engine = GooglePSESearchEngine(
                    api_key="test-key",
                    search_engine_id="test-id",
                    max_retries=1,
                )

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

    def test_make_request_429_error(self):
        """Make request raises RateLimitError on 429."""
        from local_deep_research.web_search_engines.engines.search_engine_google_pse import (
            GooglePSESearchEngine,
        )
        from local_deep_research.web_search_engines.rate_limiting import (
            RateLimitError,
        )
        from requests.exceptions import RequestException

        with patch.object(GooglePSESearchEngine, "_validate_connection"):
            with patch(
                "local_deep_research.web_search_engines.engines.search_engine_google_pse.safe_get",
                side_effect=RequestException("Error 429: Too many requests"),
            ):
                engine = GooglePSESearchEngine(
                    api_key="test-key",
                    search_engine_id="test-id",
                    max_retries=1,
                )

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


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_google_pse import (
            GooglePSESearchEngine,
        )

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

        with patch.object(GooglePSESearchEngine, "_validate_connection"):
            engine = GooglePSESearchEngine(
                api_key="test-key", search_engine_id="test-id", max_results=2
            )
            # Return items once, then empty to stop pagination loop
            with patch.object(
                engine, "_make_request", side_effect=[mock_response, {}]
            ):
                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"
                assert previews[0]["source"] == "Google Programmable Search"

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

        mock_response = {}  # No items

        with patch.object(GooglePSESearchEngine, "_validate_connection"):
            with patch.object(
                GooglePSESearchEngine,
                "_make_request",
                return_value=mock_response,
            ):
                engine = GooglePSESearchEngine(
                    api_key="test-key", search_engine_id="test-id"
                )
                previews = engine._get_previews("test query")

                assert previews == []

    def test_get_previews_skips_results_without_url(self):
        """Get previews skips results without URL."""
        from local_deep_research.web_search_engines.engines.search_engine_google_pse import (
            GooglePSESearchEngine,
        )

        mock_response = {
            "items": [
                {"title": "No URL Result", "snippet": "Snippet"},
                {
                    "title": "With URL",
                    "snippet": "Snippet",
                    "link": "https://example.com",
                },
            ]
        }

        with patch.object(GooglePSESearchEngine, "_validate_connection"):
            engine = GooglePSESearchEngine(
                api_key="test-key", search_engine_id="test-id", max_results=1
            )
            # Return items once, then empty to stop pagination loop
            with patch.object(
                engine, "_make_request", side_effect=[mock_response, {}]
            ):
                previews = engine._get_previews("test query")

                assert len(previews) == 1
                assert previews[0]["title"] == "With URL"

    def test_get_previews_handles_exception(self):
        """Get previews handles exceptions gracefully."""
        from local_deep_research.web_search_engines.engines.search_engine_google_pse import (
            GooglePSESearchEngine,
        )

        with patch.object(GooglePSESearchEngine, "_validate_connection"):
            with patch.object(
                GooglePSESearchEngine,
                "_make_request",
                side_effect=Exception("API error"),
            ):
                engine = GooglePSESearchEngine(
                    api_key="test-key", search_engine_id="test-id"
                )
                previews = engine._get_previews("test query")

                assert previews == []


class TestValidateConnection:
    """Tests for _validate_connection method."""

    def test_validate_connection_success(self):
        """Validate connection succeeds."""
        from local_deep_research.web_search_engines.engines.search_engine_google_pse import (
            GooglePSESearchEngine,
        )

        mock_response = Mock()
        mock_response.json.return_value = {}
        mock_response.raise_for_status = Mock()

        with patch(
            "local_deep_research.web_search_engines.engines.search_engine_google_pse.safe_get",
            return_value=mock_response,
        ):
            engine = GooglePSESearchEngine(
                api_key="test-key", search_engine_id="test-id"
            )

            # If we get here, validation succeeded
            assert engine.api_key == "test-key"

    def test_validate_connection_api_error(self):
        """Validate connection raises on API error."""
        from local_deep_research.web_search_engines.engines.search_engine_google_pse import (
            GooglePSESearchEngine,
        )

        mock_response = Mock()
        mock_response.json.return_value = {
            "error": {"message": "Invalid API key"}
        }
        mock_response.raise_for_status = Mock()

        with patch(
            "local_deep_research.web_search_engines.engines.search_engine_google_pse.safe_get",
            return_value=mock_response,
        ):
            with pytest.raises(ValueError) as exc_info:
                GooglePSESearchEngine(
                    api_key="test-key", search_engine_id="test-id"
                )

            assert "Invalid API key" in str(exc_info.value)


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_google_pse import (
            GooglePSESearchEngine,
        )

        with patch.object(GooglePSESearchEngine, "_validate_connection"):
            engine = GooglePSESearchEngine(
                api_key="test-key", search_engine_id="test-id"
            )

            with patch.object(
                engine,
                "_get_previews",
                return_value=[
                    {
                        "id": "https://example.com",
                        "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_google_pse import (
            GooglePSESearchEngine,
        )

        with patch.object(GooglePSESearchEngine, "_validate_connection"):
            engine = GooglePSESearchEngine(
                api_key="test-key", search_engine_id="test-id"
            )

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

                assert results == []


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

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

        assert GooglePSESearchEngine.is_public is True

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

        assert GooglePSESearchEngine.is_generic is True
