"""
Tests for the WikipediaSearchEngine class.

Tests cover:
- Initialization and configuration
- Preview generation with disambiguation handling
- Full content retrieval
- Summary and page methods
- Error handling
"""

from unittest.mock import MagicMock, Mock, patch


class TestWikipediaSearchEngineInit:
    """Tests for WikipediaSearchEngine initialization."""

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

        with patch("wikipedia.set_lang") as mock_set_lang:
            engine = WikipediaSearchEngine()

            assert engine.max_results == 10
            assert engine.include_content is True
            assert engine.sentences == 5
            mock_set_lang.assert_called_once_with("en")

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

        with patch("wikipedia.set_lang"):
            engine = WikipediaSearchEngine(max_results=25)

            assert engine.max_results == 25

    def test_init_with_custom_language(self):
        """Initialize with custom language."""
        from local_deep_research.web_search_engines.engines.search_engine_wikipedia import (
            WikipediaSearchEngine,
        )

        with patch("wikipedia.set_lang") as mock_set_lang:
            WikipediaSearchEngine(language="fr")

            mock_set_lang.assert_called_once_with("fr")

    def test_init_with_custom_sentences(self):
        """Initialize with custom sentences."""
        from local_deep_research.web_search_engines.engines.search_engine_wikipedia import (
            WikipediaSearchEngine,
        )

        with patch("wikipedia.set_lang"):
            engine = WikipediaSearchEngine(sentences=10)

            assert engine.sentences == 10

    def test_init_with_include_content_false(self):
        """Initialize with include_content=False."""
        from local_deep_research.web_search_engines.engines.search_engine_wikipedia import (
            WikipediaSearchEngine,
        )

        with patch("wikipedia.set_lang"):
            engine = WikipediaSearchEngine(include_content=False)

            assert engine.include_content is False

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

        mock_llm = Mock()
        with patch("wikipedia.set_lang"):
            engine = WikipediaSearchEngine(llm=mock_llm)

            assert engine.llm is mock_llm

    def test_init_with_max_filtered_results(self):
        """Initialize with max_filtered_results."""
        from local_deep_research.web_search_engines.engines.search_engine_wikipedia import (
            WikipediaSearchEngine,
        )

        with patch("wikipedia.set_lang"):
            engine = WikipediaSearchEngine(max_filtered_results=5)

            assert engine.max_filtered_results == 5


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_wikipedia import (
            WikipediaSearchEngine,
        )

        with patch("wikipedia.set_lang"):
            with patch("wikipedia.search", return_value=["Python", "Java"]):
                with patch(
                    "wikipedia.summary",
                    side_effect=["Python is a language", "Java is a language"],
                ):
                    engine = WikipediaSearchEngine()

                    previews = engine._get_previews("programming")

                    assert len(previews) == 2
                    assert previews[0]["title"] == "Python"
                    assert previews[0]["snippet"] == "Python is a language"
                    assert "wikipedia.org" in previews[0]["link"]

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

        with patch("wikipedia.set_lang"):
            with patch("wikipedia.search", return_value=[]):
                engine = WikipediaSearchEngine()

                previews = engine._get_previews("nonexistent query")

                assert previews == []

    def test_get_previews_handles_disambiguation(self):
        """Get previews handles disambiguation errors."""
        from local_deep_research.web_search_engines.engines.search_engine_wikipedia import (
            WikipediaSearchEngine,
        )
        import wikipedia

        with patch("wikipedia.set_lang"):
            with patch("wikipedia.search", return_value=["Python"]):
                # First call raises disambiguation, second succeeds
                disambig_error = wikipedia.exceptions.DisambiguationError(
                    "Python", ["Python (programming)", "Python (snake)"]
                )
                with patch(
                    "wikipedia.summary",
                    side_effect=[
                        disambig_error,
                        "Python is a programming language",
                    ],
                ):
                    engine = WikipediaSearchEngine()

                    previews = engine._get_previews("python")

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

    def test_get_previews_handles_page_error(self):
        """Get previews handles page errors gracefully."""
        from local_deep_research.web_search_engines.engines.search_engine_wikipedia import (
            WikipediaSearchEngine,
        )
        import wikipedia

        with patch("wikipedia.set_lang"):
            with patch("wikipedia.search", return_value=["Valid", "Invalid"]):
                page_error = wikipedia.exceptions.PageError("Invalid")
                with patch(
                    "wikipedia.summary",
                    side_effect=["Valid summary", page_error],
                ):
                    engine = WikipediaSearchEngine()

                    previews = engine._get_previews("test")

                    # Only valid result should be returned
                    assert len(previews) == 1
                    assert previews[0]["title"] == "Valid"

    def test_get_previews_handles_exception(self):
        """Get previews handles unexpected exceptions."""
        from local_deep_research.web_search_engines.engines.search_engine_wikipedia import (
            WikipediaSearchEngine,
        )

        with patch("wikipedia.set_lang"):
            with patch(
                "wikipedia.search", side_effect=Exception("Connection error")
            ):
                engine = WikipediaSearchEngine()

                previews = engine._get_previews("test")

                assert previews == []

    def test_get_previews_short_circuits_on_rate_limit_decode_error(self):
        """When MediaWiki rate-limits us, the `wikipedia` PyPI library tries
        to ``Response.json()`` on the 429 HTML body and raises
        ``JSONDecodeError``. We must bail out of the per-title loop on the
        first occurrence — every subsequent call in the batch will hit the
        same throttle, and emitting a full traceback per title floods the
        log (we observed 14+ stack traces for a single rate-limited query).
        """
        import json

        from local_deep_research.web_search_engines.engines.search_engine_wikipedia import (
            WikipediaSearchEngine,
        )

        decode_error = json.JSONDecodeError("Expecting value", "<html>", 0)

        with patch("wikipedia.set_lang"):
            with patch(
                "wikipedia.search",
                return_value=["A", "B", "C", "D", "E"],
            ):
                # Title A succeeds, B trips the throttle. Titles C-E must
                # NOT be looked up — we break on first decode error.
                summary_mock = MagicMock(
                    side_effect=["A summary", decode_error, "C", "D", "E"]
                )
                with patch("wikipedia.summary", summary_mock):
                    engine = WikipediaSearchEngine()
                    previews = engine._get_previews("test")

                # Got the title that succeeded before the throttle.
                assert len(previews) == 1
                assert previews[0]["title"] == "A"
                # Loop short-circuited: only A and B were attempted.
                assert summary_mock.call_count == 2

    def test_get_previews_handles_outer_decode_error_on_search(self):
        """Even the initial wikipedia.search() can be rate-limited. Should
        return [] without spamming a traceback at exception level."""
        import json

        from local_deep_research.web_search_engines.engines.search_engine_wikipedia import (
            WikipediaSearchEngine,
        )

        decode_error = json.JSONDecodeError("Expecting value", "<html>", 0)

        with patch("wikipedia.set_lang"):
            with patch("wikipedia.search", side_effect=decode_error):
                engine = WikipediaSearchEngine()
                previews = engine._get_previews("test")

                assert previews == []

    def test_get_previews_creates_correct_link(self):
        """Get previews creates correct Wikipedia link."""
        from local_deep_research.web_search_engines.engines.search_engine_wikipedia import (
            WikipediaSearchEngine,
        )

        with patch("wikipedia.set_lang"):
            with patch("wikipedia.search", return_value=["Hello World"]):
                with patch("wikipedia.summary", return_value="Hello summary"):
                    engine = WikipediaSearchEngine()

                    previews = engine._get_previews("test")

                    assert (
                        previews[0]["link"]
                        == "https://en.wikipedia.org/wiki/Hello_World"
                    )


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

    def test_get_full_content_retrieves_pages(self):
        """Get full content retrieves page data."""
        from local_deep_research.web_search_engines.engines.search_engine_wikipedia import (
            WikipediaSearchEngine,
        )

        mock_page = Mock()
        mock_page.title = "Python"
        mock_page.url = "https://en.wikipedia.org/wiki/Python"
        mock_page.content = "Full content here"
        mock_page.categories = ["Programming"]
        mock_page.references = ["ref1"]
        mock_page.links = ["link1"]
        mock_page.images = ["image1"]
        mock_page.sections = ["section1"]

        with patch("wikipedia.set_lang"):
            with patch("wikipedia.page", return_value=mock_page):
                engine = WikipediaSearchEngine()

                items = [{"id": "Python", "snippet": "Python summary"}]
                results = engine._get_full_content(items)

                assert len(results) == 1
                assert results[0]["title"] == "Python"
                assert results[0]["content"] == "Full content here"
                assert results[0]["full_content"] == "Full content here"
                assert results[0]["categories"] == ["Programming"]

    def test_get_full_content_handles_error(self):
        """Get full content handles errors gracefully."""
        from local_deep_research.web_search_engines.engines.search_engine_wikipedia import (
            WikipediaSearchEngine,
        )
        import wikipedia

        with patch("wikipedia.set_lang"):
            with patch(
                "wikipedia.page",
                side_effect=wikipedia.exceptions.PageError("Not found"),
            ):
                engine = WikipediaSearchEngine()

                items = [{"id": "Invalid", "snippet": "Preview"}]
                results = engine._get_full_content(items)

                # Should return original item on error
                assert len(results) == 1
                assert results[0]["snippet"] == "Preview"

    def test_get_full_content_no_id(self):
        """Get full content handles items without ID."""
        from local_deep_research.web_search_engines.engines.search_engine_wikipedia import (
            WikipediaSearchEngine,
        )

        with patch("wikipedia.set_lang"):
            engine = WikipediaSearchEngine()

            items = [{"title": "Test", "snippet": "No ID here"}]
            results = engine._get_full_content(items)

            assert len(results) == 1
            assert results[0]["snippet"] == "No ID here"


class TestGetSummary:
    """Tests for get_summary method."""

    def test_get_summary_returns_text(self):
        """Get summary returns text."""
        from local_deep_research.web_search_engines.engines.search_engine_wikipedia import (
            WikipediaSearchEngine,
        )

        with patch("wikipedia.set_lang"):
            with patch(
                "wikipedia.summary", return_value="This is a summary"
            ) as mock_summary:
                engine = WikipediaSearchEngine()

                result = engine.get_summary("Python")

                assert result == "This is a summary"
                mock_summary.assert_called_once_with(
                    "Python", sentences=5, auto_suggest=False
                )

    def test_get_summary_with_custom_sentences(self):
        """Get summary with custom sentences."""
        from local_deep_research.web_search_engines.engines.search_engine_wikipedia import (
            WikipediaSearchEngine,
        )

        with patch("wikipedia.set_lang"):
            with patch(
                "wikipedia.summary", return_value="Summary"
            ) as mock_summary:
                engine = WikipediaSearchEngine()

                engine.get_summary("Python", sentences=10)

                mock_summary.assert_called_once_with(
                    "Python", sentences=10, auto_suggest=False
                )

    def test_get_summary_handles_disambiguation(self):
        """Get summary handles disambiguation."""
        from local_deep_research.web_search_engines.engines.search_engine_wikipedia import (
            WikipediaSearchEngine,
        )
        import wikipedia

        with patch("wikipedia.set_lang"):
            disambig_error = wikipedia.exceptions.DisambiguationError(
                "Python", ["Python (language)"]
            )
            with patch(
                "wikipedia.summary",
                side_effect=[disambig_error, "Python language summary"],
            ):
                engine = WikipediaSearchEngine()

                result = engine.get_summary("Python")

                assert result == "Python language summary"


class TestGetPage:
    """Tests for get_page method."""

    def test_get_page_returns_full_info(self):
        """Get page returns full information."""
        from local_deep_research.web_search_engines.engines.search_engine_wikipedia import (
            WikipediaSearchEngine,
        )

        mock_page = Mock()
        mock_page.title = "Python"
        mock_page.url = "https://en.wikipedia.org/wiki/Python"
        mock_page.content = "Full content"
        mock_page.categories = ["Programming"]
        mock_page.references = ["ref1"]
        mock_page.links = ["link1"]
        mock_page.images = ["image1"]
        mock_page.sections = ["section1"]

        with patch("wikipedia.set_lang"):
            with patch("wikipedia.page", return_value=mock_page):
                with patch("wikipedia.summary", return_value="Summary"):
                    engine = WikipediaSearchEngine()

                    result = engine.get_page("Python")

                    assert result["title"] == "Python"
                    assert (
                        result["link"] == "https://en.wikipedia.org/wiki/Python"
                    )
                    assert result["content"] == "Full content"
                    assert result["categories"] == ["Programming"]

    def test_get_page_handles_disambiguation(self):
        """Get page handles disambiguation."""
        from local_deep_research.web_search_engines.engines.search_engine_wikipedia import (
            WikipediaSearchEngine,
        )
        import wikipedia

        mock_page = Mock()
        mock_page.title = "Python (language)"
        mock_page.url = "https://en.wikipedia.org/wiki/Python_(language)"
        mock_page.content = "Content"
        mock_page.categories = []
        mock_page.references = []
        mock_page.links = []
        mock_page.images = []
        mock_page.sections = []

        disambig_error = wikipedia.exceptions.DisambiguationError(
            "Python", ["Python (language)"]
        )

        with patch("wikipedia.set_lang"):
            with patch(
                "wikipedia.page", side_effect=[disambig_error, mock_page]
            ):
                with patch("wikipedia.summary", return_value="Summary"):
                    engine = WikipediaSearchEngine()

                    result = engine.get_page("Python")

                    assert result["title"] == "Python (language)"


class TestSetLanguage:
    """Tests for set_language method."""

    def test_set_language_changes_language(self):
        """Set language changes Wikipedia language."""
        from local_deep_research.web_search_engines.engines.search_engine_wikipedia import (
            WikipediaSearchEngine,
        )

        with patch("wikipedia.set_lang") as mock_set_lang:
            engine = WikipediaSearchEngine()

            engine.set_language("de")

            # Called once in init with 'en', once with 'de'
            assert mock_set_lang.call_count == 2
            mock_set_lang.assert_called_with("de")


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

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

        assert WikipediaSearchEngine.is_public is True


class TestRun:
    """Tests for run method (inherited from BaseSearchEngine)."""

    def test_run_returns_results(self):
        """Run returns search results."""
        from local_deep_research.web_search_engines.engines.search_engine_wikipedia import (
            WikipediaSearchEngine,
        )

        with patch("wikipedia.set_lang"):
            engine = WikipediaSearchEngine()

            with patch.object(
                engine,
                "_get_previews",
                return_value=[
                    {
                        "id": "Python",
                        "title": "Python",
                        "snippet": "Summary",
                        "link": "https://en.wikipedia.org/wiki/Python",
                    }
                ],
            ):
                with patch.object(
                    engine,
                    "_get_full_content",
                    return_value=[
                        {
                            "title": "Python",
                            "snippet": "Summary",
                            "content": "Full",
                        }
                    ],
                ):
                    results = engine.run("python programming")

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

    def test_run_handles_empty_results(self):
        """Run handles empty results."""
        from local_deep_research.web_search_engines.engines.search_engine_wikipedia import (
            WikipediaSearchEngine,
        )

        with patch("wikipedia.set_lang"):
            engine = WikipediaSearchEngine()

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

                assert results == []
