"""
Tests for the DuckDuckGoSearchEngine class.

Tests cover:
- Initialization and configuration
- Preview generation
- Full content retrieval
- Rate limit error handling
- Run method
"""

from unittest.mock import Mock, patch
import pytest


class TestDuckDuckGoSearchEngineInit:
    """Tests for DuckDuckGoSearchEngine initialization."""

    def test_init_with_defaults(self):
        """Initialize with default values."""
        from local_deep_research.web_search_engines.engines.search_engine_ddg import (
            DuckDuckGoSearchEngine,
        )

        with patch(
            "local_deep_research.web_search_engines.engines.search_engine_ddg.DuckDuckGoSearchAPIWrapper"
        ) as mock_wrapper:
            engine = DuckDuckGoSearchEngine()

            assert engine.max_results == 10
            assert engine.region == "us"
            assert engine.safe_search is True
            assert engine.language == "English"
            assert engine.include_full_content is False
            mock_wrapper.assert_called_once()

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

        with patch(
            "local_deep_research.web_search_engines.engines.search_engine_ddg.DuckDuckGoSearchAPIWrapper"
        ):
            engine = DuckDuckGoSearchEngine(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_ddg import (
            DuckDuckGoSearchEngine,
        )

        with patch(
            "local_deep_research.web_search_engines.engines.search_engine_ddg.DuckDuckGoSearchAPIWrapper"
        ) as mock_wrapper:
            engine = DuckDuckGoSearchEngine(region="uk")

            assert engine.region == "uk"
            mock_wrapper.assert_called_with(
                region="uk", max_results=10, safesearch="moderate"
            )

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

        with patch(
            "local_deep_research.web_search_engines.engines.search_engine_ddg.DuckDuckGoSearchAPIWrapper"
        ) as mock_wrapper:
            engine = DuckDuckGoSearchEngine(safe_search=False)

            assert engine.safe_search is False
            mock_wrapper.assert_called_with(
                region="us", max_results=10, safesearch="off"
            )

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

        mock_llm = Mock()
        with patch(
            "local_deep_research.web_search_engines.engines.search_engine_ddg.DuckDuckGoSearchAPIWrapper"
        ):
            engine = DuckDuckGoSearchEngine(llm=mock_llm)

            assert engine.llm is mock_llm

    def test_init_with_include_full_content(self):
        """Initialize with include_full_content creates FullSearchResults."""
        from local_deep_research.web_search_engines.engines.search_engine_ddg import (
            DuckDuckGoSearchEngine,
        )

        mock_llm = Mock()
        with patch(
            "local_deep_research.web_search_engines.engines.search_engine_ddg.DuckDuckGoSearchAPIWrapper"
        ):
            with patch(
                "local_deep_research.web_search_engines.engines.full_search.FullSearchResults"
            ) as mock_full_search:
                engine = DuckDuckGoSearchEngine(
                    llm=mock_llm, include_full_content=True
                )

                assert engine.include_full_content is True
                mock_full_search.assert_called_once()

    def test_init_without_full_content_no_full_search(self):
        """Initialize with include_full_content=False doesn't create FullSearchResults."""
        from local_deep_research.web_search_engines.engines.search_engine_ddg import (
            DuckDuckGoSearchEngine,
        )

        with patch(
            "local_deep_research.web_search_engines.engines.search_engine_ddg.DuckDuckGoSearchAPIWrapper"
        ):
            with patch(
                "local_deep_research.web_search_engines.engines.full_search.FullSearchResults"
            ) as mock_full_search:
                DuckDuckGoSearchEngine(include_full_content=False)

                mock_full_search.assert_not_called()


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_ddg import (
            DuckDuckGoSearchEngine,
        )

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

        with patch(
            "local_deep_research.web_search_engines.engines.search_engine_ddg.DuckDuckGoSearchAPIWrapper"
        ) as mock_wrapper_class:
            mock_wrapper = Mock()
            mock_wrapper.results.return_value = mock_results
            mock_wrapper_class.return_value = mock_wrapper

            engine = DuckDuckGoSearchEngine()
            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"] == "https://example1.com"

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

        with patch(
            "local_deep_research.web_search_engines.engines.search_engine_ddg.DuckDuckGoSearchAPIWrapper"
        ) as mock_wrapper_class:
            mock_wrapper = Mock()
            mock_wrapper.results.return_value = []
            mock_wrapper_class.return_value = mock_wrapper

            engine = DuckDuckGoSearchEngine()
            previews = engine._get_previews("test query")

            assert previews == []

    def test_get_previews_non_list_results(self):
        """Get previews handles non-list results."""
        from local_deep_research.web_search_engines.engines.search_engine_ddg import (
            DuckDuckGoSearchEngine,
        )

        with patch(
            "local_deep_research.web_search_engines.engines.search_engine_ddg.DuckDuckGoSearchAPIWrapper"
        ) as mock_wrapper_class:
            mock_wrapper = Mock()
            mock_wrapper.results.return_value = None
            mock_wrapper_class.return_value = mock_wrapper

            engine = DuckDuckGoSearchEngine()
            previews = engine._get_previews("test query")

            assert previews == []

    def test_get_previews_rate_limit_error(self):
        """Get previews raises RateLimitError on rate limit."""
        from local_deep_research.web_search_engines.engines.search_engine_ddg import (
            DuckDuckGoSearchEngine,
        )
        from local_deep_research.web_search_engines.rate_limiting import (
            RateLimitError,
        )

        with patch(
            "local_deep_research.web_search_engines.engines.search_engine_ddg.DuckDuckGoSearchAPIWrapper"
        ) as mock_wrapper_class:
            mock_wrapper = Mock()
            mock_wrapper.results.side_effect = Exception(
                "202 Ratelimit exceeded"
            )
            mock_wrapper_class.return_value = mock_wrapper

            engine = DuckDuckGoSearchEngine()

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

    def test_get_previews_forbidden_error(self):
        """Get previews raises RateLimitError on 403 forbidden."""
        from local_deep_research.web_search_engines.engines.search_engine_ddg import (
            DuckDuckGoSearchEngine,
        )
        from local_deep_research.web_search_engines.rate_limiting import (
            RateLimitError,
        )

        with patch(
            "local_deep_research.web_search_engines.engines.search_engine_ddg.DuckDuckGoSearchAPIWrapper"
        ) as mock_wrapper_class:
            mock_wrapper = Mock()
            mock_wrapper.results.side_effect = Exception("403 Forbidden")
            mock_wrapper_class.return_value = mock_wrapper

            engine = DuckDuckGoSearchEngine()

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

    def test_get_previews_timeout_error(self):
        """Get previews raises RateLimitError on timeout."""
        from local_deep_research.web_search_engines.engines.search_engine_ddg import (
            DuckDuckGoSearchEngine,
        )
        from local_deep_research.web_search_engines.rate_limiting import (
            RateLimitError,
        )

        with patch(
            "local_deep_research.web_search_engines.engines.search_engine_ddg.DuckDuckGoSearchAPIWrapper"
        ) as mock_wrapper_class:
            mock_wrapper = Mock()
            mock_wrapper.results.side_effect = Exception("Connection timed out")
            mock_wrapper_class.return_value = mock_wrapper

            engine = DuckDuckGoSearchEngine()

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

    def test_get_previews_general_error(self):
        """Get previews returns empty on general error."""
        from local_deep_research.web_search_engines.engines.search_engine_ddg import (
            DuckDuckGoSearchEngine,
        )

        with patch(
            "local_deep_research.web_search_engines.engines.search_engine_ddg.DuckDuckGoSearchAPIWrapper"
        ) as mock_wrapper_class:
            mock_wrapper = Mock()
            mock_wrapper.results.side_effect = Exception("Some other error")
            mock_wrapper_class.return_value = mock_wrapper

            engine = DuckDuckGoSearchEngine()
            previews = engine._get_previews("test query")

            assert previews == []


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

    def test_get_full_content_with_full_search(self):
        """Get full content uses FullSearchResults when available."""
        from local_deep_research.web_search_engines.engines.search_engine_ddg import (
            DuckDuckGoSearchEngine,
        )

        mock_llm = Mock()
        with patch(
            "local_deep_research.web_search_engines.engines.search_engine_ddg.DuckDuckGoSearchAPIWrapper"
        ):
            with patch(
                "local_deep_research.web_search_engines.engines.full_search.FullSearchResults"
            ) as mock_full_search_class:
                mock_full_search = Mock()
                mock_full_search._get_full_content.return_value = [
                    {"title": "Result", "full_content": "Full content here"}
                ]
                mock_full_search_class.return_value = mock_full_search

                engine = DuckDuckGoSearchEngine(
                    llm=mock_llm, include_full_content=True
                )

                items = [{"title": "Result", "link": "https://example.com"}]
                results = engine._get_full_content(items)

                mock_full_search._get_full_content.assert_called_once_with(
                    items
                )
                assert results[0]["full_content"] == "Full content here"

    def test_get_full_content_without_full_search(self):
        """Get full content returns items as-is without FullSearchResults."""
        from local_deep_research.web_search_engines.engines.search_engine_ddg import (
            DuckDuckGoSearchEngine,
        )

        with patch(
            "local_deep_research.web_search_engines.engines.search_engine_ddg.DuckDuckGoSearchAPIWrapper"
        ):
            engine = DuckDuckGoSearchEngine()

            items = [{"title": "Result", "link": "https://example.com"}]
            results = engine._get_full_content(items)

            assert results == items


class TestRun:
    """Tests for run method."""

    def test_run_calls_parent(self):
        """Run calls parent class run method."""
        from local_deep_research.web_search_engines.engines.search_engine_ddg import (
            DuckDuckGoSearchEngine,
        )

        with patch(
            "local_deep_research.web_search_engines.engines.search_engine_ddg.DuckDuckGoSearchAPIWrapper"
        ):
            engine = DuckDuckGoSearchEngine()

            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"}],
                ):
                    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_ddg import (
            DuckDuckGoSearchEngine,
        )

        with patch(
            "local_deep_research.web_search_engines.engines.search_engine_ddg.DuckDuckGoSearchAPIWrapper"
        ):
            engine = DuckDuckGoSearchEngine()

            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):
        """DuckDuckGoSearchEngine is marked as public."""
        from local_deep_research.web_search_engines.engines.search_engine_ddg import (
            DuckDuckGoSearchEngine,
        )

        assert DuckDuckGoSearchEngine.is_public is True

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

        assert DuckDuckGoSearchEngine.is_generic is True
