"""
Comprehensive tests for the DuckDuckGo search engine.
Tests initialization, search functionality, error handling, and result parsing.

Note: These tests require the 'duckduckgo_search' package to be installed.
"""

import pytest
from unittest.mock import Mock, patch

# Check if DuckDuckGo search is actually importable (requires both
# langchain_community and the ddgs package to be installed)
try:
    from langchain_community.utilities import DuckDuckGoSearchAPIWrapper

    DuckDuckGoSearchAPIWrapper()
    DDGS_AVAILABLE = True
except (ImportError, Exception):
    DDGS_AVAILABLE = False


@pytest.mark.skipif(not DDGS_AVAILABLE, reason="ddgs package not installed")
class TestDuckDuckGoSearchEngineInit:
    """Tests for DuckDuckGo search engine initialization."""

    def test_init_default_parameters(self):
        """Test initialization with default parameters."""
        from local_deep_research.web_search_engines.engines.search_engine_ddg import (
            DuckDuckGoSearchEngine,
        )

        engine = DuckDuckGoSearchEngine()

        assert engine.max_results >= 10
        assert engine.is_public 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_ddg import (
            DuckDuckGoSearchEngine,
        )

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

        engine = DuckDuckGoSearchEngine(region="us-en")
        assert engine.region == "us-en"


@pytest.mark.skipif(not DDGS_AVAILABLE, reason="ddgs package not installed")
class TestDuckDuckGoSearchExecution:
    """Tests for DuckDuckGo search execution."""

    @pytest.fixture
    def ddg_engine(self):
        """Create a DuckDuckGo engine instance."""
        from local_deep_research.web_search_engines.engines.search_engine_ddg import (
            DuckDuckGoSearchEngine,
        )

        return DuckDuckGoSearchEngine(max_results=10)

    def test_engine_initialization(self, ddg_engine):
        """Test that engine is properly initialized."""
        assert ddg_engine is not None
        assert ddg_engine.max_results >= 10


@pytest.mark.skipif(not DDGS_AVAILABLE, reason="ddgs package not installed")
class TestDuckDuckGoRegionSupport:
    """Tests for DuckDuckGo region/locale support."""

    def test_default_region(self):
        """Test default region configuration."""
        from local_deep_research.web_search_engines.engines.search_engine_ddg import (
            DuckDuckGoSearchEngine,
        )

        engine = DuckDuckGoSearchEngine()
        # Default region should be set or None
        assert hasattr(engine, "region")

    def test_custom_region(self):
        """Test custom region configuration."""
        from local_deep_research.web_search_engines.engines.search_engine_ddg import (
            DuckDuckGoSearchEngine,
        )

        engine = DuckDuckGoSearchEngine(region="de-de")
        assert engine.region == "de-de"


@pytest.mark.skipif(not DDGS_AVAILABLE, reason="ddgs package not installed")
class TestDuckDuckGoEngineType:
    """Tests for DuckDuckGo engine type identification."""

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

        engine = DuckDuckGoSearchEngine()
        assert "duckduckgo" 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_ddg import (
            DuckDuckGoSearchEngine,
        )

        engine = DuckDuckGoSearchEngine()
        assert engine.is_public is True

    def test_engine_is_generic(self):
        """Test that engine is marked as generic (not specialized)."""
        from local_deep_research.web_search_engines.engines.search_engine_ddg import (
            DuckDuckGoSearchEngine,
        )

        engine = DuckDuckGoSearchEngine()
        assert engine.is_generic is True


# Tests that can run without the ddgs package (just testing mock structure)
class TestDDGResponseFixtures:
    """Tests for DuckDuckGo response fixture structure."""

    def test_mock_response_structure(self, mock_ddg_response):
        """Test that mock response has correct structure."""
        assert isinstance(mock_ddg_response, list)
        assert len(mock_ddg_response) == 3

    def test_mock_response_has_required_fields(self, mock_ddg_response):
        """Test that mock response items have required fields."""
        for result in mock_ddg_response:
            assert "title" in result
            assert "href" in result
            assert "body" in result

    def test_mock_response_urls_valid(self, mock_ddg_response):
        """Test that URLs in mock response are valid."""
        for result in mock_ddg_response:
            assert result["href"].startswith("http")


@pytest.mark.skipif(not DDGS_AVAILABLE, reason="ddgs package not installed")
class TestDuckDuckGoGetPreviews:
    """Tests for the _get_previews method and error handling."""

    @pytest.fixture
    def ddg_engine(self):
        """Create a DuckDuckGo engine instance."""
        from local_deep_research.web_search_engines.engines.search_engine_ddg import (
            DuckDuckGoSearchEngine,
        )

        return DuckDuckGoSearchEngine(max_results=10)

    def test_get_previews_returns_correct_format(self, ddg_engine):
        """Test that _get_previews returns properly formatted previews."""
        with patch.object(ddg_engine.engine, "results") as mock_results:
            mock_results.return_value = [
                {
                    "title": "Test Title",
                    "link": "https://example.com",
                    "snippet": "Test snippet",
                },
                {
                    "title": "Title 2",
                    "link": "https://example2.com",
                    "snippet": "Snippet 2",
                },
            ]

            previews = ddg_engine._get_previews("test query")

            assert len(previews) == 2
            assert previews[0]["title"] == "Test Title"
            assert previews[0]["link"] == "https://example.com"
            assert previews[0]["snippet"] == "Test snippet"
            assert previews[0]["id"] == "https://example.com"

    def test_get_previews_handles_empty_results(self, ddg_engine):
        """Test that _get_previews handles empty results list."""
        with patch.object(ddg_engine.engine, "results") as mock_results:
            mock_results.return_value = []

            previews = ddg_engine._get_previews("test query")

            assert previews == []

    def test_get_previews_handles_non_list_results(self, ddg_engine):
        """Test that _get_previews handles non-list results."""
        with patch.object(ddg_engine.engine, "results") as mock_results:
            mock_results.return_value = None

            previews = ddg_engine._get_previews("test query")

            assert previews == []

    def test_get_previews_rate_limit_202_raises_error(self, ddg_engine):
        """Test that 202 Ratelimit error raises RateLimitError."""
        from local_deep_research.web_search_engines.rate_limiting import (
            RateLimitError,
        )

        with patch.object(ddg_engine.engine, "results") as mock_results:
            mock_results.side_effect = Exception("202 Ratelimit hit")

            with pytest.raises(RateLimitError) as exc_info:
                ddg_engine._get_previews("test query")

            assert "rate limit" in str(exc_info.value).lower()

    def test_get_previews_403_forbidden_raises_error(self, ddg_engine):
        """Test that 403 forbidden error raises RateLimitError."""
        from local_deep_research.web_search_engines.rate_limiting import (
            RateLimitError,
        )

        with patch.object(ddg_engine.engine, "results") as mock_results:
            mock_results.side_effect = Exception("403 Forbidden response")

            with pytest.raises(RateLimitError) as exc_info:
                ddg_engine._get_previews("test query")

            assert "forbidden" in str(exc_info.value).lower()

    def test_get_previews_timeout_raises_error(self, ddg_engine):
        """Test that timeout error raises RateLimitError."""
        from local_deep_research.web_search_engines.rate_limiting import (
            RateLimitError,
        )

        with patch.object(ddg_engine.engine, "results") as mock_results:
            mock_results.side_effect = Exception("Connection timeout")

            with pytest.raises(RateLimitError) as exc_info:
                ddg_engine._get_previews("test query")

            assert "timeout" in str(exc_info.value).lower()

    def test_get_previews_generic_exception_returns_empty(self, ddg_engine):
        """Test that generic exceptions return empty list."""
        with patch.object(ddg_engine.engine, "results") as mock_results:
            mock_results.side_effect = ValueError("Some generic error")

            previews = ddg_engine._get_previews("test query")

            assert previews == []

    def test_get_previews_handles_missing_fields(self, ddg_engine):
        """Test that _get_previews handles results with missing fields."""
        with patch.object(ddg_engine.engine, "results") as mock_results:
            mock_results.return_value = [
                {"title": "Title Only"},  # Missing link and snippet
                {"link": "https://example.com"},  # Missing title and snippet
            ]

            previews = ddg_engine._get_previews("test query")

            assert len(previews) == 2
            assert previews[0]["title"] == "Title Only"
            assert previews[0]["snippet"] == ""
            assert previews[1]["title"] == ""


@pytest.mark.skipif(not DDGS_AVAILABLE, reason="ddgs package not installed")
class TestDuckDuckGoGetFullContent:
    """Tests for the _get_full_content method."""

    @pytest.fixture
    def ddg_engine_with_full_content(self):
        """Create a DuckDuckGo engine with full content enabled."""
        from local_deep_research.web_search_engines.engines.search_engine_ddg import (
            DuckDuckGoSearchEngine,
        )

        mock_llm = Mock()
        return DuckDuckGoSearchEngine(
            max_results=10,
            llm=mock_llm,
            include_full_content=True,
        )

    @pytest.fixture
    def ddg_engine_snippets_only(self):
        """Create a DuckDuckGo engine without full content."""
        from local_deep_research.web_search_engines.engines.search_engine_ddg import (
            DuckDuckGoSearchEngine,
        )

        return DuckDuckGoSearchEngine(
            max_results=10,
            include_full_content=False,
        )

    def test_get_full_content_without_full_search(
        self, ddg_engine_snippets_only
    ):
        """Test that _get_full_content returns items as-is when no full_search."""
        items = [
            {"title": "Test", "snippet": "Snippet"},
        ]

        result = ddg_engine_snippets_only._get_full_content(items)

        assert result == items

    def test_get_full_content_with_full_search_delegates(
        self, ddg_engine_with_full_content
    ):
        """Test that _get_full_content delegates to full_search when available."""
        items = [{"title": "Test", "snippet": "Snippet"}]

        # Mock the full_search._get_full_content method
        with patch.object(
            ddg_engine_with_full_content.full_search, "_get_full_content"
        ) as mock_full:
            mock_full.return_value = [
                {"title": "Test", "content": "Full content"}
            ]

            result = ddg_engine_with_full_content._get_full_content(items)

            mock_full.assert_called_once_with(items)
            assert result[0]["content"] == "Full content"


@pytest.mark.skipif(not DDGS_AVAILABLE, reason="ddgs package not installed")
class TestDuckDuckGoRunMethod:
    """Tests for the run() method which uses the two-phase approach."""

    @pytest.fixture
    def ddg_engine(self):
        """Create a DuckDuckGo engine instance."""
        from local_deep_research.web_search_engines.engines.search_engine_ddg import (
            DuckDuckGoSearchEngine,
        )

        return DuckDuckGoSearchEngine(max_results=5)

    def test_run_returns_results(self, ddg_engine):
        """Test that run() returns search results."""
        with patch.object(ddg_engine, "_get_previews") as mock_previews:
            mock_previews.return_value = [
                {
                    "id": "1",
                    "title": "Test",
                    "snippet": "Test snippet",
                    "link": "https://example.com",
                },
            ]

            results = ddg_engine.run("test query")

            # Should return filtered results (or all if no filtering)
            assert isinstance(results, list)

    def test_run_accepts_research_context(self, ddg_engine):
        """Test that run() accepts research_context parameter."""
        with patch.object(ddg_engine, "_get_previews") as mock_previews:
            mock_previews.return_value = []

            # Should not raise
            results = ddg_engine.run(
                "test query",
                research_context={"previous_findings": "some info"},
            )

            assert isinstance(results, list)
