"""
Comprehensive tests for the Google Programmable Search Engine.
Tests initialization, search functionality, error handling, and retry logic.

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

import pytest
from unittest.mock import Mock


class TestGooglePSESearchEngineInit:
    """Tests for Google PSE search engine initialization."""

    @pytest.fixture(autouse=True)
    def mock_validation(self, monkeypatch):
        """Mock validation to avoid HTTP requests during init."""
        # Mock safe_get to return a valid test response
        mock_response = Mock()
        mock_response.status_code = 200
        mock_response.raise_for_status = Mock()
        mock_response.json = Mock(return_value={"items": []})
        monkeypatch.setattr(
            "local_deep_research.web_search_engines.engines.search_engine_google_pse.safe_get",
            Mock(return_value=mock_response),
        )

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

        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
        assert engine.is_public is True
        assert engine.is_generic is True

    def test_init_custom_max_results(self):
        """Test initialization with custom max results."""
        from local_deep_research.web_search_engines.engines.search_engine_google_pse import (
            GooglePSESearchEngine,
        )

        engine = GooglePSESearchEngine(
            api_key="test_key",
            search_engine_id="test_id",
            max_results=25,
        )
        assert engine.max_results >= 25

    def test_init_with_region(self):
        """Test initialization with specific region."""
        from local_deep_research.web_search_engines.engines.search_engine_google_pse import (
            GooglePSESearchEngine,
        )

        engine = GooglePSESearchEngine(
            api_key="test_key",
            search_engine_id="test_id",
            region="de",
        )
        assert engine.region == "de"

    def test_init_with_safe_search(self):
        """Test initialization with safe search options."""
        from local_deep_research.web_search_engines.engines.search_engine_google_pse import (
            GooglePSESearchEngine,
        )

        engine = GooglePSESearchEngine(
            api_key="test_key",
            search_engine_id="test_id",
            safe_search=True,
        )
        assert engine.safe == "active"

        engine_off = GooglePSESearchEngine(
            api_key="test_key",
            search_engine_id="test_id",
            safe_search=False,
        )
        assert engine_off.safe == "off"

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

        # Mock get_setting_from_snapshot to return None
        monkeypatch.setattr(
            "local_deep_research.config.thread_settings.get_setting_from_snapshot",
            lambda *args, **kwargs: None,
        )

        with pytest.raises(ValueError, match="Google API key is required"):
            GooglePSESearchEngine(search_engine_id="test_id")

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

        # Mock get_setting_from_snapshot to return None for engine_id
        def mock_get_setting(key, **kwargs):
            if "api_key" in key:
                return "test_key"
            return None

        monkeypatch.setattr(
            "local_deep_research.config.thread_settings.get_setting_from_snapshot",
            mock_get_setting,
        )

        with pytest.raises(
            ValueError, match="Google Search Engine ID is required"
        ):
            GooglePSESearchEngine()

    def test_init_with_language(self):
        """Test initialization with different languages."""
        from local_deep_research.web_search_engines.engines.search_engine_google_pse import (
            GooglePSESearchEngine,
        )

        engine = GooglePSESearchEngine(
            api_key="test_key",
            search_engine_id="test_id",
            search_language="German",
        )
        assert engine.language == "de"

        engine_jp = GooglePSESearchEngine(
            api_key="test_key",
            search_engine_id="test_id",
            search_language="Japanese",
        )
        assert engine_jp.language == "ja"


class TestGooglePSEEngineType:
    """Tests for Google PSE engine type identification."""

    @pytest.fixture(autouse=True)
    def mock_validation(self, monkeypatch):
        """Mock validation to avoid HTTP requests during init."""
        mock_response = Mock()
        mock_response.status_code = 200
        mock_response.raise_for_status = Mock()
        mock_response.json = Mock(return_value={"items": []})
        monkeypatch.setattr(
            "local_deep_research.web_search_engines.engines.search_engine_google_pse.safe_get",
            Mock(return_value=mock_response),
        )

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

        engine = GooglePSESearchEngine(
            api_key="test_key", search_engine_id="test_id"
        )
        assert "google" 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_google_pse import (
            GooglePSESearchEngine,
        )

        engine = GooglePSESearchEngine(
            api_key="test_key", search_engine_id="test_id"
        )
        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_google_pse import (
            GooglePSESearchEngine,
        )

        engine = GooglePSESearchEngine(
            api_key="test_key", search_engine_id="test_id"
        )
        assert engine.is_generic is True


class TestGooglePSERetryConfig:
    """Tests for Google PSE retry configuration."""

    @pytest.fixture(autouse=True)
    def mock_validation(self, monkeypatch):
        """Mock validation to avoid HTTP requests during init."""
        mock_response = Mock()
        mock_response.status_code = 200
        mock_response.raise_for_status = Mock()
        mock_response.json = Mock(return_value={"items": []})
        monkeypatch.setattr(
            "local_deep_research.web_search_engines.engines.search_engine_google_pse.safe_get",
            Mock(return_value=mock_response),
        )

    def test_default_retry_config(self):
        """Test default retry configuration."""
        from local_deep_research.web_search_engines.engines.search_engine_google_pse import (
            GooglePSESearchEngine,
        )

        engine = GooglePSESearchEngine(
            api_key="test_key", search_engine_id="test_id"
        )
        assert engine.max_retries == 3
        assert engine.retry_delay == 2.0

    def test_custom_retry_config(self):
        """Test custom retry configuration."""
        from local_deep_research.web_search_engines.engines.search_engine_google_pse import (
            GooglePSESearchEngine,
        )

        engine = GooglePSESearchEngine(
            api_key="test_key",
            search_engine_id="test_id",
            max_retries=5,
            retry_delay=1.0,
        )
        assert engine.max_retries == 5
        assert engine.retry_delay == 1.0


class TestGooglePSERateLimiting:
    """Tests for Google PSE rate limiting."""

    @pytest.fixture(autouse=True)
    def mock_validation(self, monkeypatch):
        """Mock validation to avoid HTTP requests during init."""
        mock_response = Mock()
        mock_response.status_code = 200
        mock_response.raise_for_status = Mock()
        mock_response.json = Mock(return_value={"items": []})
        monkeypatch.setattr(
            "local_deep_research.web_search_engines.engines.search_engine_google_pse.safe_get",
            Mock(return_value=mock_response),
        )

    def test_min_request_interval_set(self):
        """Test that minimum request interval is set."""
        from local_deep_research.web_search_engines.engines.search_engine_google_pse import (
            GooglePSESearchEngine,
        )

        engine = GooglePSESearchEngine(
            api_key="test_key", search_engine_id="test_id"
        )
        assert hasattr(engine, "min_request_interval")
        assert engine.min_request_interval == 0.5

    def test_last_request_time_initialized(self):
        """Test that last request time is initialized."""
        from local_deep_research.web_search_engines.engines.search_engine_google_pse import (
            GooglePSESearchEngine,
        )

        engine = GooglePSESearchEngine(
            api_key="test_key", search_engine_id="test_id"
        )
        assert hasattr(engine, "last_request_time")
        assert engine.last_request_time == 0


class TestGooglePSESearchExecution:
    """Tests for Google PSE search execution."""

    @pytest.fixture
    def engine(self, monkeypatch):
        """Create a Google PSE engine with mocked validation."""
        mock_response = Mock()
        mock_response.status_code = 200
        mock_response.raise_for_status = Mock()
        mock_response.json = Mock(return_value={"items": []})
        monkeypatch.setattr(
            "local_deep_research.web_search_engines.engines.search_engine_google_pse.safe_get",
            Mock(return_value=mock_response),
        )

        from local_deep_research.web_search_engines.engines.search_engine_google_pse import (
            GooglePSESearchEngine,
        )

        return GooglePSESearchEngine(
            api_key="test_key", search_engine_id="test_id"
        )

    def test_get_previews_success(self, monkeypatch):
        """Test successful preview retrieval."""
        # First, mock validation to allow engine creation
        validation_response = Mock()
        validation_response.status_code = 200
        validation_response.raise_for_status = Mock()
        validation_response.json = Mock(return_value={"items": []})

        monkeypatch.setattr(
            "local_deep_research.web_search_engines.engines.search_engine_google_pse.safe_get",
            Mock(return_value=validation_response),
        )

        from local_deep_research.web_search_engines.engines.search_engine_google_pse import (
            GooglePSESearchEngine,
        )

        # Create engine with max_results=2 to match expected results
        engine = GooglePSESearchEngine(
            api_key="test_key", search_engine_id="test_id", max_results=2
        )

        # Now mock the search response
        search_response = Mock()
        search_response.status_code = 200
        search_response.raise_for_status = Mock()
        search_response.json = Mock(
            return_value={
                "items": [
                    {
                        "title": "Test Result 1",
                        "link": "https://example1.com",
                        "snippet": "This is a test snippet 1",
                    },
                    {
                        "title": "Test Result 2",
                        "link": "https://example2.com",
                        "snippet": "This is a test snippet 2",
                    },
                ]
            }
        )

        monkeypatch.setattr(
            "local_deep_research.web_search_engines.engines.search_engine_google_pse.safe_get",
            Mock(return_value=search_response),
        )

        previews = engine._get_previews("test query")

        assert len(previews) == 2
        assert previews[0]["title"] == "Test Result 1"
        assert previews[1]["link"] == "https://example2.com"

    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={})

        monkeypatch.setattr(
            "local_deep_research.web_search_engines.engines.search_engine_google_pse.safe_get",
            Mock(return_value=mock_response),
        )

        previews = engine._get_previews("test query")

        assert previews == []

    def test_get_previews_skips_results_without_url(self, monkeypatch):
        """Test that results without URLs are skipped."""
        # First, mock validation to allow engine creation
        validation_response = Mock()
        validation_response.status_code = 200
        validation_response.raise_for_status = Mock()
        validation_response.json = Mock(return_value={"items": []})

        monkeypatch.setattr(
            "local_deep_research.web_search_engines.engines.search_engine_google_pse.safe_get",
            Mock(return_value=validation_response),
        )

        from local_deep_research.web_search_engines.engines.search_engine_google_pse import (
            GooglePSESearchEngine,
        )

        # Create engine with max_results=1 to match expected valid results
        engine = GooglePSESearchEngine(
            api_key="test_key", search_engine_id="test_id", max_results=1
        )

        # Now mock the search response
        search_response = Mock()
        search_response.status_code = 200
        search_response.raise_for_status = Mock()
        search_response.json = Mock(
            return_value={
                "items": [
                    {
                        "title": "Test Without URL",
                        "snippet": "No link here",
                    },
                    {
                        "title": "Test With URL",
                        "link": "https://example.com",
                        "snippet": "Has a link",
                    },
                ]
            }
        )

        monkeypatch.setattr(
            "local_deep_research.web_search_engines.engines.search_engine_google_pse.safe_get",
            Mock(return_value=search_response),
        )

        previews = engine._get_previews("test query")

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


class TestGooglePSEErrorHandling:
    """Tests for Google PSE error handling."""

    @pytest.fixture
    def engine(self, monkeypatch):
        """Create a Google PSE engine with mocked validation."""
        mock_response = Mock()
        mock_response.status_code = 200
        mock_response.raise_for_status = Mock()
        mock_response.json = Mock(return_value={"items": []})
        monkeypatch.setattr(
            "local_deep_research.web_search_engines.engines.search_engine_google_pse.safe_get",
            Mock(return_value=mock_response),
        )

        from local_deep_research.web_search_engines.engines.search_engine_google_pse import (
            GooglePSESearchEngine,
        )

        return GooglePSESearchEngine(
            api_key="test_key", search_engine_id="test_id", max_retries=1
        )

    def test_rate_limit_429_raises_error(self, engine, monkeypatch):
        """Test that 429 errors raise RateLimitError."""
        from requests.exceptions import RequestException

        from local_deep_research.web_search_engines.rate_limiting import (
            RateLimitError,
        )

        monkeypatch.setattr(
            "local_deep_research.web_search_engines.engines.search_engine_google_pse.safe_get",
            Mock(side_effect=RequestException("429 Too Many Requests")),
        )

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

    def test_quota_exceeded_raises_rate_limit_error(self, engine, monkeypatch):
        """Test that quota exceeded errors raise RateLimitError."""
        from requests.exceptions import RequestException

        from local_deep_research.web_search_engines.rate_limiting import (
            RateLimitError,
        )

        monkeypatch.setattr(
            "local_deep_research.web_search_engines.engines.search_engine_google_pse.safe_get",
            Mock(side_effect=RequestException("quotaExceeded")),
        )

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

    def test_general_error_raises_request_exception(self, engine, monkeypatch):
        """Test that general errors raise RequestException after retries."""
        from requests.exceptions import RequestException

        monkeypatch.setattr(
            "local_deep_research.web_search_engines.engines.search_engine_google_pse.safe_get",
            Mock(side_effect=RequestException("Network error")),
        )

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


class TestGooglePSELanguageMapping:
    """Tests for Google PSE language code mapping."""

    @pytest.fixture(autouse=True)
    def mock_validation(self, monkeypatch):
        """Mock validation to avoid HTTP requests during init."""
        mock_response = Mock()
        mock_response.status_code = 200
        mock_response.raise_for_status = Mock()
        mock_response.json = Mock(return_value={"items": []})
        monkeypatch.setattr(
            "local_deep_research.web_search_engines.engines.search_engine_google_pse.safe_get",
            Mock(return_value=mock_response),
        )

    def test_english_language(self):
        """Test English language mapping."""
        from local_deep_research.web_search_engines.engines.search_engine_google_pse import (
            GooglePSESearchEngine,
        )

        engine = GooglePSESearchEngine(
            api_key="test_key",
            search_engine_id="test_id",
            search_language="English",
        )
        assert engine.language == "en"

    def test_spanish_language(self):
        """Test Spanish language mapping."""
        from local_deep_research.web_search_engines.engines.search_engine_google_pse import (
            GooglePSESearchEngine,
        )

        engine = GooglePSESearchEngine(
            api_key="test_key",
            search_engine_id="test_id",
            search_language="Spanish",
        )
        assert engine.language == "es"

    def test_chinese_language(self):
        """Test Chinese language mapping."""
        from local_deep_research.web_search_engines.engines.search_engine_google_pse import (
            GooglePSESearchEngine,
        )

        engine = GooglePSESearchEngine(
            api_key="test_key",
            search_engine_id="test_id",
            search_language="Chinese",
        )
        assert engine.language == "zh-CN"

    def test_unknown_language_defaults_to_english(self):
        """Test that unknown language defaults to English."""
        from local_deep_research.web_search_engines.engines.search_engine_google_pse import (
            GooglePSESearchEngine,
        )

        engine = GooglePSESearchEngine(
            api_key="test_key",
            search_engine_id="test_id",
            search_language="UnknownLanguage",
        )
        assert engine.language == "en"
