"""
Tests for StandardSearchStrategy.

Tests cover:
- Initialization with dependencies
- Settings access
- Analyze topic flow
- Error handling for missing search engine
- Question generation integration
"""

from unittest.mock import Mock, patch


class TestStandardSearchStrategyInit:
    """Tests for StandardSearchStrategy initialization."""

    def test_init_with_required_params(self):
        """Initialize with required parameters."""
        from local_deep_research.advanced_search_system.strategies.standard_strategy import (
            StandardSearchStrategy,
        )

        mock_search = Mock()
        mock_model = Mock()

        settings_snapshot = {
            "search.iterations": {"value": 3},
            "search.questions_per_iteration": {"value": 5},
            "general.knowledge_accumulation_context_limit": {"value": 10000},
        }

        strategy = StandardSearchStrategy(
            search=mock_search,
            model=mock_model,
            settings_snapshot=settings_snapshot,
        )

        assert strategy.search is mock_search
        assert strategy.model is mock_model
        assert strategy.max_iterations == 3
        assert strategy.questions_per_iteration == 5
        assert strategy.context_limit == 10000

    def test_init_creates_components(self):
        """Initialize creates required components."""
        from local_deep_research.advanced_search_system.strategies.standard_strategy import (
            StandardSearchStrategy,
        )

        mock_search = Mock()
        mock_model = Mock()

        settings_snapshot = {
            "search.iterations": {"value": 2},
            "search.questions_per_iteration": {"value": 3},
            "general.knowledge_accumulation_context_limit": {"value": 5000},
        }

        strategy = StandardSearchStrategy(
            search=mock_search,
            model=mock_model,
            settings_snapshot=settings_snapshot,
        )

        assert strategy.question_generator is not None
        assert strategy.knowledge_generator is not None
        assert strategy.findings_repository is not None
        assert strategy.citation_handler is not None

    def test_init_with_custom_citation_handler(self):
        """Initialize with custom citation handler."""
        from local_deep_research.advanced_search_system.strategies.standard_strategy import (
            StandardSearchStrategy,
        )

        mock_search = Mock()
        mock_model = Mock()
        mock_citation_handler = Mock()

        settings_snapshot = {
            "search.iterations": {"value": 2},
            "search.questions_per_iteration": {"value": 3},
            "general.knowledge_accumulation_context_limit": {"value": 5000},
        }

        strategy = StandardSearchStrategy(
            search=mock_search,
            model=mock_model,
            citation_handler=mock_citation_handler,
            settings_snapshot=settings_snapshot,
        )

        assert strategy.citation_handler is mock_citation_handler

    def test_init_inherits_base_attributes(self):
        """Initialize inherits base strategy attributes."""
        from local_deep_research.advanced_search_system.strategies.standard_strategy import (
            StandardSearchStrategy,
        )

        mock_search = Mock()
        mock_model = Mock()
        links = [{"url": "https://example.com"}]

        settings_snapshot = {
            "search.iterations": {"value": 1},
            "search.questions_per_iteration": {"value": 2},
            "general.knowledge_accumulation_context_limit": {"value": 5000},
        }

        strategy = StandardSearchStrategy(
            search=mock_search,
            model=mock_model,
            all_links_of_system=links,
            settings_snapshot=settings_snapshot,
        )

        assert strategy.all_links_of_system is links
        assert strategy.questions_by_iteration == {}


class TestAnalyzeTopic:
    """Tests for analyze_topic method."""

    def test_analyze_topic_no_search_engine(self):
        """Analyze topic returns error when no search engine."""
        from local_deep_research.advanced_search_system.strategies.standard_strategy import (
            StandardSearchStrategy,
        )

        mock_model = Mock()

        settings_snapshot = {
            "search.iterations": {"value": 1},
            "search.questions_per_iteration": {"value": 2},
            "general.knowledge_accumulation_context_limit": {"value": 5000},
        }

        strategy = StandardSearchStrategy(
            search=None,  # No search engine
            model=mock_model,
            settings_snapshot=settings_snapshot,
        )

        result = strategy.analyze_topic("test query")

        assert result["findings"] == []
        assert result["iterations"] == 0
        assert "error" in result
        assert "No search engine available" in result["error"]

    def test_analyze_topic_calls_progress_callback(self):
        """Analyze topic calls progress callback."""
        from local_deep_research.advanced_search_system.strategies.standard_strategy import (
            StandardSearchStrategy,
        )

        mock_search = Mock()
        mock_search.run.return_value = []
        mock_model = Mock()
        mock_model.invoke.return_value = Mock(content="No questions")

        settings_snapshot = {
            "search.iterations": {"value": 1},
            "search.questions_per_iteration": {"value": 2},
            "general.knowledge_accumulation_context_limit": {"value": 5000},
            "general.knowledge_accumulation": {"value": "ITERATION"},
        }

        strategy = StandardSearchStrategy(
            search=mock_search,
            model=mock_model,
            settings_snapshot=settings_snapshot,
        )

        callback = Mock()
        strategy.set_progress_callback(callback)

        with patch.object(
            strategy.question_generator, "generate_questions", return_value=[]
        ):
            strategy.analyze_topic("test query")

        # Progress callback should be called multiple times
        assert callback.call_count >= 1

    def test_analyze_topic_generates_questions(self):
        """Analyze topic generates questions and stores them."""
        from local_deep_research.advanced_search_system.strategies.standard_strategy import (
            StandardSearchStrategy,
        )

        mock_search = Mock()
        mock_search.run.return_value = []  # No search results
        mock_model = Mock()
        # Return proper string content from model invokes
        mock_model.invoke.return_value = Mock(content="Synthesized result text")

        settings_snapshot = {
            "search.iterations": {"value": 1},
            "search.questions_per_iteration": {"value": 2},
            "general.knowledge_accumulation_context_limit": {"value": 5000},
            "general.knowledge_accumulation": {"value": "ITERATION"},
        }

        strategy = StandardSearchStrategy(
            search=mock_search,
            model=mock_model,
            settings_snapshot=settings_snapshot,
        )

        # Mock question generator to return specific questions
        with patch.object(
            strategy.question_generator,
            "generate_questions",
            return_value=["Q1", "Q2"],
        ):
            result = strategy.analyze_topic("test query")

        # Questions should be stored
        assert 0 in strategy.questions_by_iteration
        assert strategy.questions_by_iteration[0] == ["Q1", "Q2"]
        # Result should have questions dictionary
        assert "questions" in result


class TestUpdateProgress:
    """Tests for _update_progress method in StandardSearchStrategy."""

    def test_update_progress_with_callback(self):
        """Update progress calls callback."""
        from local_deep_research.advanced_search_system.strategies.standard_strategy import (
            StandardSearchStrategy,
        )

        mock_search = Mock()
        mock_model = Mock()

        settings_snapshot = {
            "search.iterations": {"value": 1},
            "search.questions_per_iteration": {"value": 2},
            "general.knowledge_accumulation_context_limit": {"value": 5000},
        }

        strategy = StandardSearchStrategy(
            search=mock_search,
            model=mock_model,
            settings_snapshot=settings_snapshot,
        )

        callback = Mock()
        strategy.progress_callback = callback

        strategy._update_progress("Test message", 50, {"key": "value"})

        callback.assert_called_once_with("Test message", 50, {"key": "value"})

    def test_update_progress_without_callback(self):
        """Update progress does nothing without callback."""
        from local_deep_research.advanced_search_system.strategies.standard_strategy import (
            StandardSearchStrategy,
        )

        mock_search = Mock()
        mock_model = Mock()

        settings_snapshot = {
            "search.iterations": {"value": 1},
            "search.questions_per_iteration": {"value": 2},
            "general.knowledge_accumulation_context_limit": {"value": 5000},
        }

        strategy = StandardSearchStrategy(
            search=mock_search,
            model=mock_model,
            settings_snapshot=settings_snapshot,
        )

        # Should not raise
        strategy._update_progress("Test message", 50)


class TestSettingsIntegration:
    """Tests for settings integration."""

    def test_uses_settings_for_iterations(self):
        """Uses settings snapshot for max_iterations."""
        from local_deep_research.advanced_search_system.strategies.standard_strategy import (
            StandardSearchStrategy,
        )

        mock_search = Mock()
        mock_model = Mock()

        settings_snapshot = {
            "search.iterations": {"value": 5},
            "search.questions_per_iteration": {"value": 3},
            "general.knowledge_accumulation_context_limit": {"value": 8000},
        }

        strategy = StandardSearchStrategy(
            search=mock_search,
            model=mock_model,
            settings_snapshot=settings_snapshot,
        )

        assert strategy.max_iterations == 5
        assert strategy.questions_per_iteration == 3
        assert strategy.context_limit == 8000

    def test_settings_value_extraction(self):
        """Extracts values from settings with 'value' key."""
        from local_deep_research.advanced_search_system.strategies.standard_strategy import (
            StandardSearchStrategy,
        )

        mock_search = Mock()
        mock_model = Mock()

        # Settings with nested 'value' key
        settings_snapshot = {
            "search.iterations": {"value": 4, "ui_element": "number"},
            "search.questions_per_iteration": {
                "value": 6,
                "ui_element": "number",
            },
            "general.knowledge_accumulation_context_limit": {
                "value": 15000,
                "ui_element": "number",
            },
        }

        strategy = StandardSearchStrategy(
            search=mock_search,
            model=mock_model,
            settings_snapshot=settings_snapshot,
        )

        assert strategy.max_iterations == 4
        assert strategy.questions_per_iteration == 6
        assert strategy.context_limit == 15000


class TestErrorHandling:
    """Tests for error handling in StandardSearchStrategy."""

    def test_handles_empty_search_results_gracefully(self):
        """Handles empty search results gracefully."""
        from local_deep_research.advanced_search_system.strategies.standard_strategy import (
            StandardSearchStrategy,
        )

        mock_search = Mock()
        mock_search.run.return_value = []  # Empty results
        mock_model = Mock()
        mock_model.invoke.return_value = Mock(content="Synthesized result")

        settings_snapshot = {
            "search.iterations": {"value": 1},
            "search.questions_per_iteration": {"value": 2},
            "general.knowledge_accumulation_context_limit": {"value": 5000},
            "general.knowledge_accumulation": {"value": "ITERATION"},
        }

        strategy = StandardSearchStrategy(
            search=mock_search,
            model=mock_model,
            settings_snapshot=settings_snapshot,
        )

        with patch.object(
            strategy.question_generator,
            "generate_questions",
            return_value=["Q1", "Q2"],
        ):
            result = strategy.analyze_topic("test query")

        # Should return a result even with empty search results
        assert isinstance(result, dict)
        assert "findings" in result

    def test_handles_none_search_results_gracefully(self):
        """Handles None search results gracefully."""
        from local_deep_research.advanced_search_system.strategies.standard_strategy import (
            StandardSearchStrategy,
        )

        mock_search = Mock()
        mock_search.run.return_value = None  # None results
        mock_model = Mock()
        mock_model.invoke.return_value = Mock(content="Synthesized result")

        settings_snapshot = {
            "search.iterations": {"value": 1},
            "search.questions_per_iteration": {"value": 2},
            "general.knowledge_accumulation_context_limit": {"value": 5000},
            "general.knowledge_accumulation": {"value": "ITERATION"},
        }

        strategy = StandardSearchStrategy(
            search=mock_search,
            model=mock_model,
            settings_snapshot=settings_snapshot,
        )

        with patch.object(
            strategy.question_generator,
            "generate_questions",
            return_value=["Q1"],
        ):
            result = strategy.analyze_topic("test query")

        # Should return a result even with None search results
        assert isinstance(result, dict)


class TestComponentIntegration:
    """Tests for component integration."""

    def test_question_generator_receives_correct_params(self):
        """Question generator receives correct parameters."""
        from local_deep_research.advanced_search_system.strategies.standard_strategy import (
            StandardSearchStrategy,
        )

        mock_search = Mock()
        mock_search.run.return_value = []
        mock_model = Mock()
        mock_model.invoke.return_value = Mock(content="Synthesized result")

        settings_snapshot = {
            "search.iterations": {"value": 1},
            "search.questions_per_iteration": {"value": 3},
            "general.knowledge_accumulation_context_limit": {"value": 5000},
            "general.knowledge_accumulation": {"value": "ITERATION"},
        }

        strategy = StandardSearchStrategy(
            search=mock_search,
            model=mock_model,
            settings_snapshot=settings_snapshot,
        )

        with patch.object(
            strategy.question_generator, "generate_questions", return_value=[]
        ) as mock_gen:
            with patch.object(
                strategy.knowledge_generator,
                "compress_knowledge",
                return_value="Compressed",
            ):
                strategy.analyze_topic("research query")

            # Should be called with correct parameters
            mock_gen.assert_called_once()
            call_kwargs = mock_gen.call_args[1]
            assert call_kwargs["query"] == "research query"
            assert call_kwargs["questions_per_iteration"] == 3

    def test_search_called_for_each_question(self):
        """Search is called for each generated question."""
        from local_deep_research.advanced_search_system.strategies.standard_strategy import (
            StandardSearchStrategy,
        )

        mock_search = Mock()
        mock_search.run.return_value = []
        mock_model = Mock()
        mock_model.invoke.return_value = Mock(content="Analysis result text")

        settings_snapshot = {
            "search.iterations": {"value": 1},
            "search.questions_per_iteration": {"value": 2},
            "general.knowledge_accumulation_context_limit": {"value": 5000},
            "general.knowledge_accumulation": {"value": "ITERATION"},
        }

        strategy = StandardSearchStrategy(
            search=mock_search,
            model=mock_model,
            settings_snapshot=settings_snapshot,
        )

        with patch.object(
            strategy.question_generator,
            "generate_questions",
            return_value=["Q1", "Q2"],
        ):
            with patch.object(
                strategy.knowledge_generator,
                "compress_knowledge",
                return_value="Compressed knowledge",
            ):
                strategy.analyze_topic("test query")

        # Search should be called for each question
        assert mock_search.run.call_count == 2
