"""
Tests for the FullSearchResults class.

Tests cover:
- Initialization and configuration
- URL quality checking with LLM
- 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"


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 falls back to original results 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 == results

    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 TestRun:
    """Tests for run method."""

    def test_run_returns_results(self):
        """Run returns results with full content via batch_fetch_and_extract."""
        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.validate_url",
                return_value=True,
            ):
                with patch(
                    "local_deep_research.web_search_engines.engines.full_search.batch_fetch_and_extract",
                    return_value={"https://example.com/1": "Fetched content"},
                ):
                    engine = FullSearchResults(
                        llm=mock_llm, web_search=mock_web_search
                    )
                    results = engine.run("test query")

        assert len(results) == 1
        assert results[0]["full_content"] == "Fetched 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.validate_url",
                return_value=True,
            ):
                with patch(
                    "local_deep_research.web_search_engines.engines.full_search.batch_fetch_and_extract",
                    return_value={"https://example.com/1": "Content"},
                ):
                    engine = FullSearchResults(
                        llm=mock_llm, web_search=mock_web_search
                    )
                    results = engine.run("test query")

        # Only one result should pass the LLM 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"}]


class TestJSRenderingForwardingFromSettingsSnapshot:
    """``FullSearchResults`` must read ``web.enable_javascript_rendering``
    from its ``settings_snapshot`` and forward the boolean to every
    ``batch_fetch_and_extract`` call (issue #3826).

    Both code paths into ``batch_fetch_and_extract`` are exercised:
    ``run()`` and ``_get_full_content()``.
    """

    @staticmethod
    def _snapshot(value: bool) -> dict:
        return {
            "web.enable_javascript_rendering": {
                "value": value,
                "ui_element": "checkbox",
            }
        }

    def _patched_run(self, snapshot, mock_llm=None, mock_web_search=None):
        """Run engine.run() with batch_fetch_and_extract patched and
        return the captured kwargs from that call."""
        from local_deep_research.web_search_engines.engines.full_search import (
            FullSearchResults,
        )

        mock_llm = mock_llm or Mock()
        mock_web_search = mock_web_search or 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,
            ),
            patch(
                "local_deep_research.web_search_engines.engines.full_search.validate_url",
                return_value=True,
            ),
            patch(
                "local_deep_research.web_search_engines.engines.full_search.batch_fetch_and_extract",
                return_value={"https://example.com/1": "content"},
            ) as mock_batch,
        ):
            engine = FullSearchResults(
                llm=mock_llm,
                web_search=mock_web_search,
                settings_snapshot=snapshot,
            )
            engine.run("test query")
        assert mock_batch.call_args is not None
        return mock_batch.call_args.kwargs

    def test_init_default_snapshot_is_none(self):
        """Existing callers (no snapshot) keep working — attribute defaults."""
        from local_deep_research.web_search_engines.engines.full_search import (
            FullSearchResults,
        )

        engine = FullSearchResults(llm=Mock(), web_search=Mock())
        assert engine.settings_snapshot is None

    def test_init_stores_snapshot(self):
        from local_deep_research.web_search_engines.engines.full_search import (
            FullSearchResults,
        )

        snap = self._snapshot(True)
        engine = FullSearchResults(
            llm=Mock(), web_search=Mock(), settings_snapshot=snap
        )
        assert engine.settings_snapshot is snap

    def test_run_passes_js_off_when_snapshot_disables(self):
        kwargs = self._patched_run(self._snapshot(False))
        assert kwargs.get("enable_js_rendering") is False

    def test_run_passes_js_on_when_snapshot_enables(self):
        kwargs = self._patched_run(self._snapshot(True))
        assert kwargs.get("enable_js_rendering") is True

    def test_run_defaults_to_js_off_without_snapshot(self):
        """No snapshot, no thread-local context → JS off (safe default)."""
        kwargs = self._patched_run(None)
        assert kwargs.get("enable_js_rendering") is False

    def test_get_full_content_passes_js_off_when_snapshot_disables(self):
        from local_deep_research.web_search_engines.engines.full_search import (
            FullSearchResults,
        )

        with (
            patch(
                "local_deep_research.web_search_engines.engines.full_search.validate_url",
                return_value=True,
            ),
            patch(
                "local_deep_research.web_search_engines.engines.full_search.batch_fetch_and_extract",
                return_value={"https://example.com/1": "content"},
            ) as mock_batch,
        ):
            engine = FullSearchResults(
                llm=Mock(),
                web_search=Mock(),
                settings_snapshot=self._snapshot(False),
            )
            engine._get_full_content(
                [{"link": "https://example.com/1", "title": "T"}]
            )
        assert mock_batch.call_args.kwargs.get("enable_js_rendering") is False

    def test_get_full_content_passes_js_on_when_snapshot_enables(self):
        from local_deep_research.web_search_engines.engines.full_search import (
            FullSearchResults,
        )

        with (
            patch(
                "local_deep_research.web_search_engines.engines.full_search.validate_url",
                return_value=True,
            ),
            patch(
                "local_deep_research.web_search_engines.engines.full_search.batch_fetch_and_extract",
                return_value={"https://example.com/1": "content"},
            ) as mock_batch,
        ):
            engine = FullSearchResults(
                llm=Mock(),
                web_search=Mock(),
                settings_snapshot=self._snapshot(True),
            )
            engine._get_full_content(
                [{"link": "https://example.com/1", "title": "T"}]
            )
        assert mock_batch.call_args.kwargs.get("enable_js_rendering") is True
