"""
Tests for the FullSearchResults class.

Tests cover:
- Initialization and configuration
- URL quality checking with LLM
- Boilerplate removal
- Full search workflow
"""

from unittest.mock import Mock, patch
import pytest


class TestFullSearchResultsInit:
    """Tests for FullSearchResults initialization."""

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

        mock_llm = Mock()
        mock_web_search = Mock()

        engine = FullSearchResults(llm=mock_llm, web_search=mock_web_search)

        assert engine.llm is mock_llm
        assert engine.web_search is mock_web_search
        assert engine.output_format == "list"
        assert engine.language == "English"
        assert engine.max_results == 10
        assert engine.region == "wt-wt"
        assert engine.time == "y"
        assert engine.safesearch == "Moderate"

    def test_init_with_custom_values(self):
        """Initialize with custom values."""
        from local_deep_research.web_search_engines.engines.full_search import (
            FullSearchResults,
        )

        mock_llm = Mock()
        mock_web_search = Mock()

        engine = FullSearchResults(
            llm=mock_llm,
            web_search=mock_web_search,
            output_format="json",
            language="German",
            max_results=25,
            region="de-de",
            time="m",
            safesearch="Off",
        )

        assert engine.output_format == "json"
        assert engine.language == "German"
        assert engine.max_results == 25
        assert engine.region == "de-de"
        assert engine.time == "m"
        assert engine.safesearch == "Off"

    def test_init_creates_transformer(self):
        """Initialize creates BeautifulSoup transformer."""
        from local_deep_research.web_search_engines.engines.full_search import (
            FullSearchResults,
        )

        mock_llm = Mock()
        mock_web_search = Mock()

        engine = FullSearchResults(llm=mock_llm, web_search=mock_web_search)

        assert engine.bs_transformer is not None
        assert engine.tags_to_extract == ["p", "div", "span"]


class TestCheckUrls:
    """Tests for check_urls method."""

    def test_check_urls_empty_results(self):
        """Check URLs returns empty for empty results."""
        from local_deep_research.web_search_engines.engines.full_search import (
            FullSearchResults,
        )

        mock_llm = Mock()
        mock_web_search = Mock()

        engine = FullSearchResults(llm=mock_llm, web_search=mock_web_search)

        results = engine.check_urls([], "test query")

        assert results == []

    def test_check_urls_filters_results(self):
        """Check URLs filters results based on LLM response."""
        from local_deep_research.web_search_engines.engines.full_search import (
            FullSearchResults,
        )

        mock_llm = Mock()
        mock_llm.invoke.return_value = Mock(content="[0, 2]")
        mock_web_search = Mock()

        engine = FullSearchResults(llm=mock_llm, web_search=mock_web_search)

        results = [
            {"link": "https://example.com/1", "title": "Result 1"},
            {"link": "https://example.com/2", "title": "Result 2"},
            {"link": "https://example.com/3", "title": "Result 3"},
        ]

        filtered = engine.check_urls(results, "test query")

        assert len(filtered) == 2
        assert filtered[0]["title"] == "Result 1"
        assert filtered[1]["title"] == "Result 3"

    def test_check_urls_handles_think_tags(self):
        """Check URLs handles response with think tags."""
        from local_deep_research.web_search_engines.engines.full_search import (
            FullSearchResults,
        )

        mock_llm = Mock()
        mock_llm.invoke.return_value = Mock(
            content="<think>reasoning</think>[1]"
        )
        mock_web_search = Mock()

        engine = FullSearchResults(llm=mock_llm, web_search=mock_web_search)

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

        filtered = engine.check_urls(results, "test query")

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

    def test_check_urls_exception(self):
        """Check URLs returns empty on exception."""
        from local_deep_research.web_search_engines.engines.full_search import (
            FullSearchResults,
        )

        mock_llm = Mock()
        mock_llm.invoke.side_effect = Exception("LLM error")
        mock_web_search = Mock()

        engine = FullSearchResults(llm=mock_llm, web_search=mock_web_search)

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

        filtered = engine.check_urls(results, "test query")

        assert filtered == []

    def test_check_urls_invalid_json(self):
        """Check URLs returns empty on invalid JSON response."""
        from local_deep_research.web_search_engines.engines.full_search import (
            FullSearchResults,
        )

        mock_llm = Mock()
        mock_llm.invoke.return_value = Mock(content="not valid json")
        mock_web_search = Mock()

        engine = FullSearchResults(llm=mock_llm, web_search=mock_web_search)

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

        filtered = engine.check_urls(results, "test query")

        assert filtered == []


class TestRemoveBoilerplate:
    """Tests for remove_boilerplate method."""

    def test_remove_boilerplate_empty_html(self):
        """Remove boilerplate returns empty for empty HTML."""
        from local_deep_research.web_search_engines.engines.full_search import (
            FullSearchResults,
        )

        mock_llm = Mock()
        mock_web_search = Mock()

        engine = FullSearchResults(llm=mock_llm, web_search=mock_web_search)

        result = engine.remove_boilerplate("")

        assert result == ""

    def test_remove_boilerplate_whitespace_only(self):
        """Remove boilerplate returns empty for whitespace-only HTML."""
        from local_deep_research.web_search_engines.engines.full_search import (
            FullSearchResults,
        )

        mock_llm = Mock()
        mock_web_search = Mock()

        engine = FullSearchResults(llm=mock_llm, web_search=mock_web_search)

        result = engine.remove_boilerplate("   \n\t  ")

        assert result == ""

    def test_remove_boilerplate_extracts_content(self):
        """Remove boilerplate extracts main content."""
        from local_deep_research.web_search_engines.engines.full_search import (
            FullSearchResults,
        )

        mock_llm = Mock()
        mock_web_search = Mock()

        engine = FullSearchResults(llm=mock_llm, web_search=mock_web_search)

        html = """
        <html>
        <body>
        <nav>Navigation menu</nav>
        <article>
        <p>This is the main article content that should be extracted.</p>
        <p>This is another paragraph with important information.</p>
        </article>
        <footer>Footer content</footer>
        </body>
        </html>
        """

        with patch(
            "local_deep_research.web_search_engines.engines.full_search.justext.justext"
        ) as mock_justext:
            # Mock paragraphs
            mock_para1 = Mock()
            mock_para1.text = "Main content"
            mock_para1.is_boilerplate = False

            mock_para2 = Mock()
            mock_para2.text = "Navigation"
            mock_para2.is_boilerplate = True

            mock_justext.return_value = [mock_para1, mock_para2]

            result = engine.remove_boilerplate(html)

            assert "Main content" in result
            assert "Navigation" not in result


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

    def test_run_returns_results(self):
        """Run returns filtered results with full content."""
        from local_deep_research.web_search_engines.engines.full_search import (
            FullSearchResults,
        )

        mock_llm = Mock()
        mock_web_search = Mock()
        mock_web_search.invoke.return_value = [
            {"link": "https://example.com/1", "title": "Result 1"},
        ]

        with patch(
            "local_deep_research.web_search_engines.engines.full_search.QUALITY_CHECK_DDG_URLS",
            False,
        ):
            with patch(
                "local_deep_research.web_search_engines.engines.full_search.AsyncChromiumLoader"
            ) as mock_loader:
                mock_doc = Mock()
                mock_doc.page_content = "<p>Full content</p>"
                mock_doc.metadata = {"source": "https://example.com/1"}
                mock_loader.return_value.load.return_value = [mock_doc]

                with patch.object(
                    FullSearchResults,
                    "remove_boilerplate",
                    return_value="Clean content",
                ):
                    engine = FullSearchResults(
                        llm=mock_llm, web_search=mock_web_search
                    )

                    # Mock the transformer
                    engine.bs_transformer = Mock()
                    engine.bs_transformer.transform_documents.return_value = [
                        mock_doc
                    ]

                    results = engine.run("test query")

                    assert len(results) == 1
                    assert results[0]["full_content"] == "Clean content"

    def test_run_with_url_filtering(self):
        """Run filters URLs when QUALITY_CHECK_DDG_URLS is True."""
        from local_deep_research.web_search_engines.engines.full_search import (
            FullSearchResults,
        )

        mock_llm = Mock()
        mock_llm.invoke.return_value = Mock(content="[0]")
        mock_web_search = Mock()
        mock_web_search.invoke.return_value = [
            {"link": "https://example.com/1", "title": "Result 1"},
            {"link": "https://example.com/2", "title": "Result 2"},
        ]

        with patch(
            "local_deep_research.web_search_engines.engines.full_search.QUALITY_CHECK_DDG_URLS",
            True,
        ):
            with patch(
                "local_deep_research.web_search_engines.engines.full_search.AsyncChromiumLoader"
            ) as mock_loader:
                mock_doc = Mock()
                mock_doc.page_content = "<p>Content</p>"
                mock_doc.metadata = {"source": "https://example.com/1"}
                mock_loader.return_value.load.return_value = [mock_doc]

                with patch.object(
                    FullSearchResults,
                    "remove_boilerplate",
                    return_value="Clean",
                ):
                    engine = FullSearchResults(
                        llm=mock_llm, web_search=mock_web_search
                    )
                    engine.bs_transformer = Mock()
                    engine.bs_transformer.transform_documents.return_value = [
                        mock_doc
                    ]

                    results = engine.run("test query")

                    # Only one result should pass the filter
                    assert len(results) == 1

    def test_run_no_valid_links(self):
        """Run returns empty when no valid links."""
        from local_deep_research.web_search_engines.engines.full_search import (
            FullSearchResults,
        )

        mock_llm = Mock()
        mock_web_search = Mock()
        mock_web_search.invoke.return_value = [
            {"title": "Result without link"},
        ]

        with patch(
            "local_deep_research.web_search_engines.engines.full_search.QUALITY_CHECK_DDG_URLS",
            False,
        ):
            engine = FullSearchResults(llm=mock_llm, web_search=mock_web_search)
            results = engine.run("test query")

            assert results == []

    def test_run_invalid_search_results_format(self):
        """Run raises error for invalid search results format."""
        from local_deep_research.web_search_engines.engines.full_search import (
            FullSearchResults,
        )

        mock_llm = Mock()
        mock_web_search = Mock()
        mock_web_search.invoke.return_value = "not a list"

        engine = FullSearchResults(llm=mock_llm, web_search=mock_web_search)

        with pytest.raises(
            ValueError, match="Expected the search results in list format"
        ):
            engine.run("test query")


class TestInvoke:
    """Tests for invoke method."""

    def test_invoke_delegates_to_run(self):
        """Invoke delegates to run method."""
        from local_deep_research.web_search_engines.engines.full_search import (
            FullSearchResults,
        )

        mock_llm = Mock()
        mock_web_search = Mock()

        engine = FullSearchResults(llm=mock_llm, web_search=mock_web_search)

        with patch.object(
            engine, "run", return_value=[{"result": "test"}]
        ) as mock_run:
            result = engine.invoke("test query")

            mock_run.assert_called_once_with("test query")
            assert result == [{"result": "test"}]


class TestCallable:
    """Tests for __call__ method."""

    def test_callable_delegates_to_invoke(self):
        """Calling instance delegates to invoke method."""
        from local_deep_research.web_search_engines.engines.full_search import (
            FullSearchResults,
        )

        mock_llm = Mock()
        mock_web_search = Mock()

        engine = FullSearchResults(llm=mock_llm, web_search=mock_web_search)

        with patch.object(
            engine, "invoke", return_value=[{"result": "test"}]
        ) as mock_invoke:
            result = engine("test query")

            mock_invoke.assert_called_once_with("test query")
            assert result == [{"result": "test"}]
