"""
Tests for the BraveSearchEngine class.

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

from unittest.mock import Mock, patch
import pytest


class TestBraveSearchEngineInit:
    """Tests for BraveSearchEngine initialization."""

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

        with patch(
            "local_deep_research.web_search_engines.engines.search_engine_brave.BraveSearch"
        ) as mock_brave:
            mock_brave.from_api_key.return_value = Mock()
            engine = BraveSearchEngine(api_key="test-api-key")

            mock_brave.from_api_key.assert_called_once()
            call_kwargs = mock_brave.from_api_key.call_args
            assert call_kwargs[1]["api_key"] == "test-api-key"
            assert engine.max_results == 10
            assert engine.include_full_content is True

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

        with patch(
            "local_deep_research.web_search_engines.engines.search_engine_brave.BraveSearch"
        ) as mock_brave:
            mock_brave.from_api_key.return_value = Mock()
            engine = BraveSearchEngine(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_brave import (
            BraveSearchEngine,
        )

        with patch(
            "local_deep_research.web_search_engines.engines.search_engine_brave.BraveSearch"
        ) as mock_brave:
            mock_brave.from_api_key.return_value = Mock()
            BraveSearchEngine(api_key="test-key", region="UK")

            call_kwargs = mock_brave.from_api_key.call_args[1]
            assert call_kwargs["search_kwargs"]["country"] == "UK"

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

        with patch(
            "local_deep_research.web_search_engines.engines.search_engine_brave.BraveSearch"
        ) as mock_brave:
            mock_brave.from_api_key.return_value = Mock()
            BraveSearchEngine(api_key="test-key", safe_search=False)

            call_kwargs = mock_brave.from_api_key.call_args[1]
            assert call_kwargs["search_kwargs"]["safesearch"] == "off"

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

        with patch(
            "local_deep_research.web_search_engines.engines.search_engine_brave.BraveSearch"
        ) as mock_brave:
            mock_brave.from_api_key.return_value = Mock()
            BraveSearchEngine(api_key="test-key", safe_search=True)

            call_kwargs = mock_brave.from_api_key.call_args[1]
            assert call_kwargs["search_kwargs"]["safesearch"] == "moderate"

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

        with patch(
            "local_deep_research.web_search_engines.engines.search_engine_brave.BraveSearch"
        ) as mock_brave:
            mock_brave.from_api_key.return_value = Mock()
            BraveSearchEngine(api_key="test-key", search_language="Spanish")

            call_kwargs = mock_brave.from_api_key.call_args[1]
            assert call_kwargs["search_kwargs"]["search_lang"] == "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_brave import (
            BraveSearchEngine,
        )

        with patch(
            "local_deep_research.web_search_engines.engines.search_engine_brave.BraveSearch"
        ) as mock_brave:
            mock_brave.from_api_key.return_value = Mock()
            BraveSearchEngine(api_key="test-key", search_language="Klingon")

            call_kwargs = mock_brave.from_api_key.call_args[1]
            assert call_kwargs["search_kwargs"]["search_lang"] == "en"

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

        with patch(
            "local_deep_research.web_search_engines.engines.search_engine_brave.BraveSearch"
        ) as mock_brave:
            mock_brave.from_api_key.return_value = Mock()
            BraveSearchEngine(api_key="test-key", time_period="d")

            call_kwargs = mock_brave.from_api_key.call_args[1]
            assert call_kwargs["search_kwargs"]["freshness"] == "pd"

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

        with patch(
            "local_deep_research.config.search_config.get_setting_from_snapshot",
            return_value=None,
        ):
            with pytest.raises(ValueError) as exc_info:
                BraveSearchEngine()

            assert "Brave API key not found" in str(exc_info.value)

    def test_init_with_api_key_from_settings(self):
        """Initialize with API key from settings snapshot."""
        from local_deep_research.web_search_engines.engines.search_engine_brave import (
            BraveSearchEngine,
        )

        with patch(
            "local_deep_research.config.search_config.get_setting_from_snapshot",
            return_value="settings-api-key",
        ):
            with patch(
                "local_deep_research.web_search_engines.engines.search_engine_brave.BraveSearch"
            ) as mock_brave:
                mock_brave.from_api_key.return_value = Mock()
                BraveSearchEngine()

                call_kwargs = mock_brave.from_api_key.call_args[1]
                assert call_kwargs["api_key"] == "settings-api-key"

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

        mock_llm = Mock()
        with patch(
            "local_deep_research.web_search_engines.engines.search_engine_brave.BraveSearch"
        ) as mock_brave:
            mock_brave.from_api_key.return_value = Mock()
            engine = BraveSearchEngine(api_key="test-key", llm=mock_llm)

            assert engine.llm is mock_llm

    def test_init_with_include_full_content_false(self):
        """Initialize with include_full_content=False."""
        from local_deep_research.web_search_engines.engines.search_engine_brave import (
            BraveSearchEngine,
        )

        with patch(
            "local_deep_research.web_search_engines.engines.search_engine_brave.BraveSearch"
        ) as mock_brave:
            mock_brave.from_api_key.return_value = Mock()
            engine = BraveSearchEngine(
                api_key="test-key", include_full_content=False
            )

            assert engine.include_full_content is False
            assert not hasattr(engine, "full_search")

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

        custom_mapping = {"custom": "cu"}
        with patch(
            "local_deep_research.web_search_engines.engines.search_engine_brave.BraveSearch"
        ) as mock_brave:
            mock_brave.from_api_key.return_value = Mock()
            BraveSearchEngine(
                api_key="test-key",
                language_code_mapping=custom_mapping,
                search_language="custom",
            )

            call_kwargs = mock_brave.from_api_key.call_args[1]
            assert call_kwargs["search_kwargs"]["search_lang"] == "cu"


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_brave import (
            BraveSearchEngine,
        )

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

        with patch(
            "local_deep_research.web_search_engines.engines.search_engine_brave.BraveSearch"
        ) as mock_brave:
            mock_engine = Mock()
            mock_engine.run.return_value = mock_raw_results
            mock_brave.from_api_key.return_value = mock_engine

            engine = BraveSearchEngine(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"
            assert previews[0]["id"] == 0
            assert previews[0]["position"] == 0

    def test_get_previews_parses_json_string_results(self):
        """Get previews parses JSON string results."""
        from local_deep_research.web_search_engines.engines.search_engine_brave import (
            BraveSearchEngine,
        )
        import json

        mock_raw_results = [
            {
                "title": "Result 1",
                "link": "https://example.com",
                "snippet": "Snippet",
            }
        ]

        with patch(
            "local_deep_research.web_search_engines.engines.search_engine_brave.BraveSearch"
        ) as mock_brave:
            mock_engine = Mock()
            # Return results as JSON string
            mock_engine.run.return_value = json.dumps(mock_raw_results)
            mock_brave.from_api_key.return_value = mock_engine

            engine = BraveSearchEngine(api_key="test-key")
            previews = engine._get_previews("test query")

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

    def test_get_previews_handles_invalid_json(self):
        """Get previews handles invalid JSON string."""
        from local_deep_research.web_search_engines.engines.search_engine_brave import (
            BraveSearchEngine,
        )

        with patch(
            "local_deep_research.web_search_engines.engines.search_engine_brave.BraveSearch"
        ) as mock_brave:
            mock_engine = Mock()
            mock_engine.run.return_value = "invalid json {"
            mock_brave.from_api_key.return_value = mock_engine

            engine = BraveSearchEngine(api_key="test-key")
            previews = engine._get_previews("test query")

            assert previews == []

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

        with patch(
            "local_deep_research.web_search_engines.engines.search_engine_brave.BraveSearch"
        ) as mock_brave:
            mock_engine = Mock()
            mock_engine.run.return_value = []
            mock_brave.from_api_key.return_value = mock_engine

            engine = BraveSearchEngine(api_key="test-key")
            previews = engine._get_previews("test query")

            assert previews == []

    def test_get_previews_rate_limit_429_error(self):
        """Get previews raises RateLimitError on 429."""
        from local_deep_research.web_search_engines.engines.search_engine_brave import (
            BraveSearchEngine,
        )
        from local_deep_research.web_search_engines.rate_limiting import (
            RateLimitError,
        )

        with patch(
            "local_deep_research.web_search_engines.engines.search_engine_brave.BraveSearch"
        ) as mock_brave:
            mock_engine = Mock()
            mock_engine.run.side_effect = Exception(
                "Error 429: Too many requests"
            )
            mock_brave.from_api_key.return_value = mock_engine

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

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

    def test_get_previews_rate_limit_too_many_requests(self):
        """Get previews raises RateLimitError on 'too many requests'."""
        from local_deep_research.web_search_engines.engines.search_engine_brave import (
            BraveSearchEngine,
        )
        from local_deep_research.web_search_engines.rate_limiting import (
            RateLimitError,
        )

        with patch(
            "local_deep_research.web_search_engines.engines.search_engine_brave.BraveSearch"
        ) as mock_brave:
            mock_engine = Mock()
            mock_engine.run.side_effect = Exception(
                "Too many requests from your IP"
            )
            mock_brave.from_api_key.return_value = mock_engine

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

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

    def test_get_previews_rate_limit_quota_error(self):
        """Get previews raises RateLimitError on quota exceeded."""
        from local_deep_research.web_search_engines.engines.search_engine_brave import (
            BraveSearchEngine,
        )
        from local_deep_research.web_search_engines.rate_limiting import (
            RateLimitError,
        )

        with patch(
            "local_deep_research.web_search_engines.engines.search_engine_brave.BraveSearch"
        ) as mock_brave:
            mock_engine = Mock()
            mock_engine.run.side_effect = Exception("Quota exceeded")
            mock_brave.from_api_key.return_value = mock_engine

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

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

    def test_get_previews_general_error_returns_empty(self):
        """Get previews returns empty list on general error."""
        from local_deep_research.web_search_engines.engines.search_engine_brave import (
            BraveSearchEngine,
        )

        with patch(
            "local_deep_research.web_search_engines.engines.search_engine_brave.BraveSearch"
        ) as mock_brave:
            mock_engine = Mock()
            mock_engine.run.side_effect = Exception("Some other error")
            mock_brave.from_api_key.return_value = mock_engine

            engine = BraveSearchEngine(api_key="test-key")
            previews = engine._get_previews("test query")

            assert previews == []

    def test_get_previews_truncates_long_queries(self):
        """Get previews truncates queries longer than 400 chars."""
        from local_deep_research.web_search_engines.engines.search_engine_brave import (
            BraveSearchEngine,
        )

        with patch(
            "local_deep_research.web_search_engines.engines.search_engine_brave.BraveSearch"
        ) as mock_brave:
            mock_engine = Mock()
            mock_engine.run.return_value = []
            mock_brave.from_api_key.return_value = mock_engine

            engine = BraveSearchEngine(api_key="test-key")
            long_query = "x" * 500
            engine._get_previews(long_query)

            # Check the query was truncated
            call_args = mock_engine.run.call_args[0][0]
            assert len(call_args) == 400

    def test_get_previews_stores_full_result(self):
        """Get previews stores _full_result in preview."""
        from local_deep_research.web_search_engines.engines.search_engine_brave import (
            BraveSearchEngine,
        )

        mock_raw_results = [
            {
                "title": "Result",
                "link": "https://example.com",
                "snippet": "Snippet",
                "extra": "data",
            }
        ]

        with patch(
            "local_deep_research.web_search_engines.engines.search_engine_brave.BraveSearch"
        ) as mock_brave:
            mock_engine = Mock()
            mock_engine.run.return_value = mock_raw_results
            mock_brave.from_api_key.return_value = mock_engine

            engine = BraveSearchEngine(api_key="test-key")
            previews = engine._get_previews("test query")

            assert "_full_result" in previews[0]
            assert previews[0]["_full_result"]["extra"] == "data"


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

    def test_get_full_content_without_full_search(self):
        """Get full content returns items without full_search."""
        from local_deep_research.web_search_engines.engines.search_engine_brave import (
            BraveSearchEngine,
        )

        with patch(
            "local_deep_research.web_search_engines.engines.search_engine_brave.BraveSearch"
        ) as mock_brave:
            mock_brave.from_api_key.return_value = Mock()
            engine = BraveSearchEngine(
                api_key="test-key", include_full_content=False
            )

            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_field(self):
        """Get full content removes _full_result field."""
        from local_deep_research.web_search_engines.engines.search_engine_brave import (
            BraveSearchEngine,
        )

        with patch(
            "local_deep_research.web_search_engines.engines.search_engine_brave.BraveSearch"
        ) as mock_brave:
            mock_brave.from_api_key.return_value = Mock()
            engine = BraveSearchEngine(
                api_key="test-key", include_full_content=False
            )

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

            results = engine._get_full_content(items)

            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_brave import (
            BraveSearchEngine,
        )

        with patch(
            "local_deep_research.web_search_engines.engines.search_engine_brave.BraveSearch"
        ) as mock_brave:
            mock_brave.from_api_key.return_value = Mock()
            engine = BraveSearchEngine(
                api_key="test-key", include_full_content=False
            )

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

            results = engine._get_full_content(items)

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

    def test_get_full_content_snippets_only_mode(self):
        """Get full content in snippets only mode."""
        from local_deep_research.web_search_engines.engines.search_engine_brave import (
            BraveSearchEngine,
        )
        from local_deep_research.web_search_engines.engines import (
            search_engine_brave,
        )

        with patch(
            "local_deep_research.web_search_engines.engines.search_engine_brave.BraveSearch"
        ) as mock_brave:
            mock_brave.from_api_key.return_value = Mock()
            engine = BraveSearchEngine(
                api_key="test-key", include_full_content=False
            )

            # Mock SEARCH_SNIPPETS_ONLY
            with patch.object(
                search_engine_brave.search_config,
                "SEARCH_SNIPPETS_ONLY",
                True,
                create=True,
            ):
                items = [
                    {
                        "title": "Result",
                        "_full_result": {
                            "title": "Result",
                            "snippet": "Full snippet",
                        },
                    }
                ]

                results = engine._get_full_content(items)

                assert len(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_brave import (
            BraveSearchEngine,
        )

        with patch(
            "local_deep_research.web_search_engines.engines.search_engine_brave.BraveSearch"
        ) as mock_brave:
            mock_brave.from_api_key.return_value = Mock()
            engine = BraveSearchEngine(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_brave import (
            BraveSearchEngine,
        )

        with patch(
            "local_deep_research.web_search_engines.engines.search_engine_brave.BraveSearch"
        ) as mock_brave:
            mock_brave.from_api_key.return_value = Mock()
            engine = BraveSearchEngine(api_key="test-key")

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

                assert results == []

    def test_run_cleans_up_search_results(self):
        """Run cleans up _search_results after execution."""
        from local_deep_research.web_search_engines.engines.search_engine_brave import (
            BraveSearchEngine,
        )

        with patch(
            "local_deep_research.web_search_engines.engines.search_engine_brave.BraveSearch"
        ) as mock_brave:
            mock_brave.from_api_key.return_value = Mock()
            engine = BraveSearchEngine(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):
        """BraveSearchEngine is marked as public."""
        from local_deep_research.web_search_engines.engines.search_engine_brave import (
            BraveSearchEngine,
        )

        assert BraveSearchEngine.is_public is True

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

        assert BraveSearchEngine.is_generic is True
