"""
Tests for the CrossEngineFilter.

Tests cover:
- Filter initialization
- Result filtering and ranking
- Reordering behavior
- Error handling
"""

from unittest.mock import Mock, patch


class TestCrossEngineFilter:
    """Tests for the CrossEngineFilter class."""

    def test_initialization_default_values(self):
        """Test CrossEngineFilter initializes with defaults."""
        from local_deep_research.advanced_search_system.filters.cross_engine_filter import (
            CrossEngineFilter,
        )

        mock_model = Mock()

        with patch(
            "local_deep_research.config.thread_settings.get_setting_from_snapshot"
        ) as mock_get_setting:
            mock_get_setting.return_value = 50

            filter_obj = CrossEngineFilter(model=mock_model)

            assert filter_obj.max_results == 50
            assert filter_obj.default_reorder is True
            assert filter_obj.default_reindex is True

    def test_initialization_custom_values(self):
        """Test CrossEngineFilter with custom values."""
        from local_deep_research.advanced_search_system.filters.cross_engine_filter import (
            CrossEngineFilter,
        )

        mock_model = Mock()

        filter_obj = CrossEngineFilter(
            model=mock_model,
            max_results=25,
            default_reorder=False,
            default_reindex=False,
        )

        assert filter_obj.max_results == 25
        assert filter_obj.default_reorder is False
        assert filter_obj.default_reindex is False

    def test_filter_results_few_results_no_llm_call(self):
        """Test that few results don't trigger LLM filtering."""
        from local_deep_research.advanced_search_system.filters.cross_engine_filter import (
            CrossEngineFilter,
        )

        mock_model = Mock()

        filter_obj = CrossEngineFilter(model=mock_model, max_results=100)

        results = [
            {
                "title": f"Result {i}",
                "snippet": f"Snippet {i}",
                "engine": "test",
            }
            for i in range(5)
        ]

        filtered = filter_obj.filter_results(results, "test query")

        # Should not call LLM for <= 10 results
        mock_model.invoke.assert_not_called()
        assert len(filtered) == 5

    def test_filter_results_no_model(self):
        """Test filtering without a model returns original results."""
        from local_deep_research.advanced_search_system.filters.cross_engine_filter import (
            CrossEngineFilter,
        )

        filter_obj = CrossEngineFilter(model=None, max_results=10)

        results = [
            {
                "title": f"Result {i}",
                "snippet": f"Snippet {i}",
                "engine": "test",
            }
            for i in range(15)
        ]

        filtered = filter_obj.filter_results(results, "test query")

        # Should return max_results without filtering
        assert len(filtered) == 10

    def test_filter_results_with_reindex(self):
        """Test that reindexing updates result indices."""
        from local_deep_research.advanced_search_system.filters.cross_engine_filter import (
            CrossEngineFilter,
        )

        filter_obj = CrossEngineFilter(model=None, max_results=100)

        results = [
            {
                "title": f"Result {i}",
                "snippet": f"Snippet {i}",
                "engine": "test",
            }
            for i in range(5)
        ]

        filtered = filter_obj.filter_results(
            results, "test query", reindex=True
        )

        # Check indices are set correctly (1-indexed)
        assert filtered[0]["index"] == "1"
        assert filtered[4]["index"] == "5"

    def test_filter_results_with_start_index(self):
        """Test reindexing with custom start index."""
        from local_deep_research.advanced_search_system.filters.cross_engine_filter import (
            CrossEngineFilter,
        )

        filter_obj = CrossEngineFilter(model=None, max_results=100)

        results = [
            {
                "title": f"Result {i}",
                "snippet": f"Snippet {i}",
                "engine": "test",
            }
            for i in range(3)
        ]

        filtered = filter_obj.filter_results(
            results, "test query", reindex=True, start_index=10
        )

        # Should start from 11 (10 + 1)
        assert filtered[0]["index"] == "11"
        assert filtered[2]["index"] == "13"

    def test_filter_results_with_llm_ranking(self):
        """Test LLM-based ranking of results."""
        from local_deep_research.advanced_search_system.filters.cross_engine_filter import (
            CrossEngineFilter,
        )

        mock_model = Mock()
        mock_model.invoke.return_value = Mock(content="[2, 0, 5]")

        filter_obj = CrossEngineFilter(model=mock_model, max_results=100)

        results = [
            {
                "title": f"Result {i}",
                "snippet": f"Snippet {i}",
                "engine": f"engine{i}",
            }
            for i in range(15)  # More than 10 to trigger LLM
        ]

        filtered = filter_obj.filter_results(
            results, "test query", reorder=True
        )

        # Should reorder based on LLM response [2, 0, 5]
        assert len(filtered) == 3
        assert filtered[0]["title"] == "Result 2"
        assert filtered[1]["title"] == "Result 0"
        assert filtered[2]["title"] == "Result 5"

    def test_filter_results_without_reorder(self):
        """Test filtering without reordering maintains original order."""
        from local_deep_research.advanced_search_system.filters.cross_engine_filter import (
            CrossEngineFilter,
        )

        mock_model = Mock()
        mock_model.invoke.return_value = Mock(content="[5, 2, 0]")

        filter_obj = CrossEngineFilter(model=mock_model, max_results=100)

        results = [
            {
                "title": f"Result {i}",
                "snippet": f"Snippet {i}",
                "engine": f"engine{i}",
            }
            for i in range(15)
        ]

        filtered = filter_obj.filter_results(
            results, "test query", reorder=False
        )

        # Should keep original order but only include [0, 2, 5]
        assert len(filtered) == 3
        assert filtered[0]["title"] == "Result 0"  # Sorted by original index
        assert filtered[1]["title"] == "Result 2"
        assert filtered[2]["title"] == "Result 5"

    def test_filter_results_llm_returns_empty(self):
        """Test fallback when LLM returns empty array."""
        from local_deep_research.advanced_search_system.filters.cross_engine_filter import (
            CrossEngineFilter,
        )

        mock_model = Mock()
        mock_model.invoke.return_value = Mock(content="[]")

        filter_obj = CrossEngineFilter(model=mock_model, max_results=100)

        results = [
            {
                "title": f"Result {i}",
                "snippet": f"Snippet {i}",
                "engine": "test",
            }
            for i in range(15)
        ]

        filtered = filter_obj.filter_results(results, "test query")

        # Should return top 10 original results as fallback
        assert len(filtered) == 10
        assert filtered[0]["title"] == "Result 0"

    def test_filter_results_llm_error(self):
        """Test fallback when LLM raises an error."""
        from local_deep_research.advanced_search_system.filters.cross_engine_filter import (
            CrossEngineFilter,
        )

        mock_model = Mock()
        mock_model.invoke.side_effect = Exception("LLM error")

        filter_obj = CrossEngineFilter(model=mock_model, max_results=100)

        results = [
            {
                "title": f"Result {i}",
                "snippet": f"Snippet {i}",
                "engine": "test",
            }
            for i in range(15)
        ]

        filtered = filter_obj.filter_results(results, "test query")

        # Should return original results as fallback
        assert len(filtered) == 15

    def test_filter_results_invalid_json_response(self):
        """Test handling of invalid JSON in LLM response."""
        from local_deep_research.advanced_search_system.filters.cross_engine_filter import (
            CrossEngineFilter,
        )

        mock_model = Mock()
        mock_model.invoke.return_value = Mock(content="Not valid JSON at all")

        filter_obj = CrossEngineFilter(model=mock_model, max_results=10)

        results = [
            {
                "title": f"Result {i}",
                "snippet": f"Snippet {i}",
                "engine": "test",
            }
            for i in range(15)
        ]

        filtered = filter_obj.filter_results(results, "test query")

        # Should return max_results from original
        assert len(filtered) == 10

    def test_filter_results_respects_max_results(self):
        """Test that max_results limits output."""
        from local_deep_research.advanced_search_system.filters.cross_engine_filter import (
            CrossEngineFilter,
        )

        mock_model = Mock()
        mock_model.invoke.return_value = Mock(
            content="[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11]"
        )

        filter_obj = CrossEngineFilter(model=mock_model, max_results=5)

        results = [
            {
                "title": f"Result {i}",
                "snippet": f"Snippet {i}",
                "engine": "test",
            }
            for i in range(15)
        ]

        filtered = filter_obj.filter_results(results, "test query")

        # Should be limited to max_results
        assert len(filtered) == 5
