"""
Tests for FocusedIterationStrategy.

Tests cover:
- Initialization with default and custom parameters
- Knowledge summary generation
- Early termination logic
- Error response creation
- Parallel search execution
- Full analyze_topic flow
- BrowseComp optimization paths
"""

from unittest.mock import Mock, patch


class TestFocusedIterationStrategyInit:
    """Tests for FocusedIterationStrategy initialization."""

    def test_init_with_defaults(self):
        """Initialize with default parameters."""
        from local_deep_research.advanced_search_system.strategies.focused_iteration_strategy import (
            FocusedIterationStrategy,
        )

        mock_model = Mock()
        mock_search = Mock()

        strategy = FocusedIterationStrategy(
            model=mock_model,
            search=mock_search,
        )

        assert strategy.model is mock_model
        assert strategy.search is mock_search
        assert strategy.max_iterations == 8
        assert strategy.questions_per_iteration == 5
        assert strategy.use_browsecomp_optimization is True
        assert strategy.enable_adaptive_questions is False
        assert strategy.enable_early_termination is False
        assert strategy.knowledge_summary_limit == 10
        assert strategy.knowledge_snippet_truncate == 200
        assert strategy.prompt_knowledge_truncate == 1500
        assert strategy.previous_searches_limit == 10

    def test_init_with_custom_params(self):
        """Initialize with custom parameters."""
        from local_deep_research.advanced_search_system.strategies.focused_iteration_strategy import (
            FocusedIterationStrategy,
        )

        mock_model = Mock()
        mock_search = Mock()

        strategy = FocusedIterationStrategy(
            model=mock_model,
            search=mock_search,
            max_iterations=4,
            questions_per_iteration=3,
            use_browsecomp_optimization=False,
            enable_adaptive_questions=True,
            enable_early_termination=True,
            knowledge_summary_limit=5,
            knowledge_snippet_truncate=100,
            prompt_knowledge_truncate=1000,
            previous_searches_limit=5,
        )

        assert strategy.max_iterations == 4
        assert strategy.questions_per_iteration == 3
        assert strategy.use_browsecomp_optimization is False
        assert strategy.enable_adaptive_questions is True
        assert strategy.enable_early_termination is True
        assert strategy.knowledge_summary_limit == 5
        assert strategy.knowledge_snippet_truncate == 100

    def test_init_none_max_iterations_uses_fallback(self):
        """None max_iterations falls back to 3."""
        from local_deep_research.advanced_search_system.strategies.focused_iteration_strategy import (
            FocusedIterationStrategy,
        )

        strategy = FocusedIterationStrategy(
            model=Mock(),
            search=Mock(),
            max_iterations=None,
        )

        assert strategy.max_iterations == 3

    def test_init_none_questions_per_iteration_uses_fallback(self):
        """None questions_per_iteration falls back to 3."""
        from local_deep_research.advanced_search_system.strategies.focused_iteration_strategy import (
            FocusedIterationStrategy,
        )

        strategy = FocusedIterationStrategy(
            model=Mock(),
            search=Mock(),
            questions_per_iteration=None,
        )

        assert strategy.questions_per_iteration == 3

    def test_init_browsecomp_creates_explorer(self):
        """BrowseComp optimization creates explorer and question generator."""
        from local_deep_research.advanced_search_system.strategies.focused_iteration_strategy import (
            FocusedIterationStrategy,
        )
        from local_deep_research.advanced_search_system.candidate_exploration import (
            ProgressiveExplorer,
        )
        from local_deep_research.advanced_search_system.questions import (
            BrowseCompQuestionGenerator,
        )

        strategy = FocusedIterationStrategy(
            model=Mock(),
            search=Mock(),
            use_browsecomp_optimization=True,
        )

        assert isinstance(
            strategy.question_generator, BrowseCompQuestionGenerator
        )
        assert isinstance(strategy.explorer, ProgressiveExplorer)

    def test_init_no_browsecomp_uses_standard_generator(self):
        """Without BrowseComp, uses StandardQuestionGenerator."""
        from local_deep_research.advanced_search_system.strategies.focused_iteration_strategy import (
            FocusedIterationStrategy,
        )
        from local_deep_research.advanced_search_system.questions import (
            StandardQuestionGenerator,
        )

        strategy = FocusedIterationStrategy(
            model=Mock(),
            search=Mock(),
            use_browsecomp_optimization=False,
        )

        assert isinstance(
            strategy.question_generator, StandardQuestionGenerator
        )
        assert strategy.explorer is None

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

        mock_citation = Mock()
        strategy = FocusedIterationStrategy(
            model=Mock(),
            search=Mock(),
            citation_handler=mock_citation,
        )

        assert strategy.citation_handler is mock_citation

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

        links = [{"url": "https://example.com"}]
        strategy = FocusedIterationStrategy(
            model=Mock(),
            search=Mock(),
            all_links_of_system=links,
        )

        assert strategy.all_links_of_system is links
        assert strategy.questions_by_iteration == {}
        assert strategy.results_by_iteration == {}
        assert strategy.all_search_results == []

    def test_init_passes_truncation_to_question_generator(self):
        """BrowseComp question generator receives truncation settings."""
        from local_deep_research.advanced_search_system.strategies.focused_iteration_strategy import (
            FocusedIterationStrategy,
        )

        strategy = FocusedIterationStrategy(
            model=Mock(),
            search=Mock(),
            use_browsecomp_optimization=True,
            prompt_knowledge_truncate=2000,
            previous_searches_limit=15,
        )

        assert strategy.question_generator.knowledge_truncate_length == 2000
        assert strategy.question_generator.previous_searches_limit == 15


class TestCreateErrorResponse:
    """Tests for _create_error_response method."""

    def test_error_response_structure(self):
        """Error response has correct structure."""
        from local_deep_research.advanced_search_system.strategies.focused_iteration_strategy import (
            FocusedIterationStrategy,
        )

        strategy = FocusedIterationStrategy(model=Mock(), search=Mock())
        result = strategy._create_error_response("Test error")

        assert result["findings"] == []
        assert result["iterations"] == 0
        assert result["questions_by_iteration"] == {}
        assert result["formatted_findings"] == "Error: Test error"
        assert result["current_knowledge"] == ""
        assert result["error"] == "Test error"

    def test_error_response_with_different_message(self):
        """Error response includes the specific error message."""
        from local_deep_research.advanced_search_system.strategies.focused_iteration_strategy import (
            FocusedIterationStrategy,
        )

        strategy = FocusedIterationStrategy(model=Mock(), search=Mock())
        result = strategy._create_error_response("No search engine available")

        assert result["error"] == "No search engine available"
        assert "No search engine available" in result["formatted_findings"]


class TestGetCurrentKnowledgeSummary:
    """Tests for _get_current_knowledge_summary method."""

    def test_empty_results_returns_empty_string(self):
        """Returns empty string when no search results."""
        from local_deep_research.advanced_search_system.strategies.focused_iteration_strategy import (
            FocusedIterationStrategy,
        )

        strategy = FocusedIterationStrategy(model=Mock(), search=Mock())
        assert strategy._get_current_knowledge_summary() == ""

    def test_includes_title_and_snippet(self):
        """Summary includes title and snippet."""
        from local_deep_research.advanced_search_system.strategies.focused_iteration_strategy import (
            FocusedIterationStrategy,
        )

        strategy = FocusedIterationStrategy(model=Mock(), search=Mock())
        strategy.all_search_results = [
            {"title": "Test Title", "snippet": "Test snippet content"},
        ]

        summary = strategy._get_current_knowledge_summary()
        assert "Test Title" in summary
        assert "Test snippet" in summary

    def test_numbering_starts_at_one(self):
        """Summary items are numbered starting from 1."""
        from local_deep_research.advanced_search_system.strategies.focused_iteration_strategy import (
            FocusedIterationStrategy,
        )

        strategy = FocusedIterationStrategy(model=Mock(), search=Mock())
        strategy.all_search_results = [
            {"title": "First", "snippet": "Content 1"},
            {"title": "Second", "snippet": "Content 2"},
        ]

        summary = strategy._get_current_knowledge_summary()
        assert summary.startswith("1. First:")
        assert "2. Second:" in summary

    def test_respects_knowledge_summary_limit(self):
        """Summary respects knowledge_summary_limit parameter."""
        from local_deep_research.advanced_search_system.strategies.focused_iteration_strategy import (
            FocusedIterationStrategy,
        )

        strategy = FocusedIterationStrategy(
            model=Mock(),
            search=Mock(),
            knowledge_summary_limit=2,
        )
        strategy.all_search_results = [
            {"title": f"Title {i}", "snippet": f"Snippet {i}"} for i in range(5)
        ]

        summary = strategy._get_current_knowledge_summary()
        lines = [line for line in summary.split("\n") if line.strip()]
        assert len(lines) == 2

    def test_no_limit_includes_all(self):
        """None knowledge_summary_limit includes all results."""
        from local_deep_research.advanced_search_system.strategies.focused_iteration_strategy import (
            FocusedIterationStrategy,
        )

        strategy = FocusedIterationStrategy(
            model=Mock(),
            search=Mock(),
            knowledge_summary_limit=None,
        )
        strategy.all_search_results = [
            {"title": f"Title {i}", "snippet": f"Snippet {i}"}
            for i in range(20)
        ]

        summary = strategy._get_current_knowledge_summary()
        lines = [line for line in summary.split("\n") if line.strip()]
        assert len(lines) == 20

    def test_truncates_snippets(self):
        """Snippets are truncated to knowledge_snippet_truncate length."""
        from local_deep_research.advanced_search_system.strategies.focused_iteration_strategy import (
            FocusedIterationStrategy,
        )

        strategy = FocusedIterationStrategy(
            model=Mock(),
            search=Mock(),
            knowledge_snippet_truncate=10,
        )
        strategy.all_search_results = [
            {
                "title": "Title",
                "snippet": "This is a very long snippet that should be truncated",
            },
        ]

        summary = strategy._get_current_knowledge_summary()
        # Snippet should be truncated to 10 chars + "..."
        assert "This is a " in summary
        assert "..." in summary

    def test_no_truncation_when_none(self):
        """No snippet truncation when knowledge_snippet_truncate is None."""
        from local_deep_research.advanced_search_system.strategies.focused_iteration_strategy import (
            FocusedIterationStrategy,
        )

        long_snippet = "x" * 500
        strategy = FocusedIterationStrategy(
            model=Mock(),
            search=Mock(),
            knowledge_snippet_truncate=None,
        )
        strategy.all_search_results = [
            {"title": "Title", "snippet": long_snippet},
        ]

        summary = strategy._get_current_knowledge_summary()
        assert long_snippet in summary

    def test_skips_results_without_title_or_snippet(self):
        """Skips results that have neither title nor snippet."""
        from local_deep_research.advanced_search_system.strategies.focused_iteration_strategy import (
            FocusedIterationStrategy,
        )

        strategy = FocusedIterationStrategy(model=Mock(), search=Mock())
        strategy.all_search_results = [
            {"url": "https://example.com"},  # No title or snippet
            {"title": "Has title", "snippet": "Has snippet"},
        ]

        summary = strategy._get_current_knowledge_summary()
        # Should only include the result with title/snippet
        # (but numbering includes skipped, so check content)
        assert "Has title" in summary

    def test_handles_missing_keys(self):
        """Handles results with missing title or snippet keys gracefully."""
        from local_deep_research.advanced_search_system.strategies.focused_iteration_strategy import (
            FocusedIterationStrategy,
        )

        strategy = FocusedIterationStrategy(model=Mock(), search=Mock())
        strategy.all_search_results = [
            {"title": "Only title"},  # No snippet key
            {"snippet": "Only snippet"},  # No title key
        ]

        summary = strategy._get_current_knowledge_summary()
        assert "Only title" in summary
        assert "Only snippet" in summary


class TestShouldTerminateEarly:
    """Tests for _should_terminate_early method."""

    def test_returns_false_without_explorer(self):
        """Returns False when no explorer is available."""
        from local_deep_research.advanced_search_system.strategies.focused_iteration_strategy import (
            FocusedIterationStrategy,
        )

        strategy = FocusedIterationStrategy(
            model=Mock(),
            search=Mock(),
            use_browsecomp_optimization=False,
        )

        assert strategy._should_terminate_early(5) is False

    def test_returns_false_for_early_iterations(self):
        """Returns False for iterations <= 3 even with high confidence."""
        from local_deep_research.advanced_search_system.strategies.focused_iteration_strategy import (
            FocusedIterationStrategy,
        )

        strategy = FocusedIterationStrategy(model=Mock(), search=Mock())
        # Mock explorer with high confidence candidate
        strategy.explorer.progress = Mock()
        strategy.explorer.progress.found_candidates = {"candidate1": 0.95}
        strategy.explorer.progress.entity_coverage = {}

        # Iterations <= 3 should not terminate
        assert strategy._should_terminate_early(1) is False
        assert strategy._should_terminate_early(2) is False
        assert strategy._should_terminate_early(3) is False

    def test_terminates_with_high_confidence(self):
        """Returns True when top candidate confidence > 0.9 after iteration 3."""
        from local_deep_research.advanced_search_system.strategies.focused_iteration_strategy import (
            FocusedIterationStrategy,
        )

        strategy = FocusedIterationStrategy(model=Mock(), search=Mock())
        strategy.explorer.progress = Mock()
        strategy.explorer.progress.found_candidates = {"candidate1": 0.95}
        strategy.explorer.progress.entity_coverage = {}

        assert strategy._should_terminate_early(4) is True

    def test_continues_with_low_coverage(self):
        """Returns False when entity coverage is below 80% before iteration 6."""
        from local_deep_research.advanced_search_system.strategies.focused_iteration_strategy import (
            FocusedIterationStrategy,
        )

        strategy = FocusedIterationStrategy(model=Mock(), search=Mock())
        strategy.explorer.progress = Mock()
        strategy.explorer.progress.found_candidates = {"candidate1": 0.5}
        strategy.explorer.progress.entity_coverage = {"type1": {"entity1"}}

        # Set extracted entities on question generator
        strategy.question_generator.extracted_entities = {
            "type1": ["entity1", "entity2", "entity3", "entity4", "entity5"]
        }

        # Low coverage (1/5 = 20%) before iteration 6 should continue
        assert strategy._should_terminate_early(4) is False

    def test_returns_false_with_no_candidates(self):
        """Returns False when no candidates found."""
        from local_deep_research.advanced_search_system.strategies.focused_iteration_strategy import (
            FocusedIterationStrategy,
        )

        strategy = FocusedIterationStrategy(model=Mock(), search=Mock())
        strategy.explorer.progress = Mock()
        strategy.explorer.progress.found_candidates = {}
        strategy.explorer.progress.entity_coverage = {}

        assert strategy._should_terminate_early(5) is False


class TestExecuteParallelSearches:
    """Tests for _execute_parallel_searches method."""

    def test_empty_queries_returns_empty(self):
        """Returns empty list for empty queries."""
        from local_deep_research.advanced_search_system.strategies.focused_iteration_strategy import (
            FocusedIterationStrategy,
        )

        strategy = FocusedIterationStrategy(model=Mock(), search=Mock())
        result = strategy._execute_parallel_searches([])
        assert result == []

    @patch(
        "local_deep_research.advanced_search_system.strategies.focused_iteration_strategy.get_search_context"
    )
    @patch(
        "local_deep_research.advanced_search_system.strategies.focused_iteration_strategy.preserve_research_context"
    )
    def test_single_query(self, mock_preserve, mock_get_ctx):
        """Executes single query and returns results."""
        from local_deep_research.advanced_search_system.strategies.focused_iteration_strategy import (
            FocusedIterationStrategy,
        )

        mock_search = Mock()
        mock_search.run.return_value = [
            {"title": "Result 1", "url": "https://example.com"}
        ]
        mock_get_ctx.return_value = {}
        # Make preserve_research_context pass through the function
        mock_preserve.side_effect = lambda fn: fn

        strategy = FocusedIterationStrategy(model=Mock(), search=mock_search)
        results = strategy._execute_parallel_searches(["test query"])

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

    @patch(
        "local_deep_research.advanced_search_system.strategies.focused_iteration_strategy.get_search_context"
    )
    @patch(
        "local_deep_research.advanced_search_system.strategies.focused_iteration_strategy.preserve_research_context"
    )
    def test_search_exception_returns_empty_for_that_query(
        self, mock_preserve, mock_get_ctx
    ):
        """Search exceptions are caught, returning empty results for failed query."""
        from local_deep_research.advanced_search_system.strategies.focused_iteration_strategy import (
            FocusedIterationStrategy,
        )

        mock_search = Mock()
        mock_search.run.side_effect = Exception("Search failed")
        mock_get_ctx.return_value = {}
        mock_preserve.side_effect = lambda fn: fn

        strategy = FocusedIterationStrategy(model=Mock(), search=mock_search)
        results = strategy._execute_parallel_searches(["query1"])

        assert results == []

    @patch(
        "local_deep_research.advanced_search_system.strategies.focused_iteration_strategy.get_search_context"
    )
    @patch(
        "local_deep_research.advanced_search_system.strategies.focused_iteration_strategy.preserve_research_context"
    )
    def test_none_search_results_treated_as_empty(
        self, mock_preserve, mock_get_ctx
    ):
        """None search results are treated as empty list."""
        from local_deep_research.advanced_search_system.strategies.focused_iteration_strategy import (
            FocusedIterationStrategy,
        )

        mock_search = Mock()
        mock_search.run.return_value = None
        mock_get_ctx.return_value = {}
        mock_preserve.side_effect = lambda fn: fn

        strategy = FocusedIterationStrategy(model=Mock(), search=mock_search)
        results = strategy._execute_parallel_searches(["query1"])

        assert results == []


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

    def test_no_search_engine_returns_error(self):
        """Returns error when search engine is None."""
        from local_deep_research.advanced_search_system.strategies.focused_iteration_strategy import (
            FocusedIterationStrategy,
        )

        strategy = FocusedIterationStrategy(
            model=Mock(),
            search=None,
        )

        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_returns_expected_keys(self):
        """Result contains all expected keys."""
        from local_deep_research.advanced_search_system.strategies.focused_iteration_strategy import (
            FocusedIterationStrategy,
        )

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

        strategy = FocusedIterationStrategy(
            model=mock_model,
            search=mock_search,
            max_iterations=1,
            use_browsecomp_optimization=False,
        )

        with patch.object(
            strategy.question_generator,
            "generate_questions",
            return_value=["Q1"],
        ):
            with patch.object(
                strategy.citation_handler,
                "analyze_followup",
                return_value={"content": "Synthesized", "documents": []},
            ):
                result = strategy.analyze_topic("test query")

        assert "findings" in result
        assert "iterations" in result
        assert "questions_by_iteration" in result
        assert "formatted_findings" in result
        assert "current_knowledge" in result
        assert "all_links_of_system" in result
        assert "sources" in result

    def test_stores_questions_by_iteration(self):
        """Questions are stored in questions_by_iteration."""
        from local_deep_research.advanced_search_system.strategies.focused_iteration_strategy import (
            FocusedIterationStrategy,
        )

        mock_search = Mock()
        mock_search.run.return_value = []
        mock_model = Mock()

        strategy = FocusedIterationStrategy(
            model=mock_model,
            search=mock_search,
            max_iterations=1,
            use_browsecomp_optimization=False,
        )

        with patch.object(
            strategy.question_generator,
            "generate_questions",
            return_value=["Q1", "Q2"],
        ):
            with patch.object(
                strategy.citation_handler,
                "analyze_followup",
                return_value={"content": "Done", "documents": []},
            ):
                strategy.analyze_topic("test query")

        assert 1 in strategy.questions_by_iteration
        # Original query is prepended if not in questions
        assert "test query" in strategy.questions_by_iteration[1]

    def test_original_query_included_in_first_iteration(self):
        """Original query is included in first iteration questions."""
        from local_deep_research.advanced_search_system.strategies.focused_iteration_strategy import (
            FocusedIterationStrategy,
        )

        mock_search = Mock()
        mock_search.run.return_value = []

        strategy = FocusedIterationStrategy(
            model=Mock(),
            search=mock_search,
            max_iterations=1,
            questions_per_iteration=3,
            use_browsecomp_optimization=False,
        )

        with patch.object(
            strategy.question_generator,
            "generate_questions",
            return_value=["Other Q1", "Other Q2"],
        ):
            with patch.object(
                strategy.citation_handler,
                "analyze_followup",
                return_value={"content": "Done", "documents": []},
            ):
                strategy.analyze_topic("my original query")

        first_iter_questions = strategy.questions_by_iteration[1]
        assert first_iter_questions[0] == "my original query"

    def test_exception_returns_error_response(self):
        """Exception during analysis returns error response."""
        from local_deep_research.advanced_search_system.strategies.focused_iteration_strategy import (
            FocusedIterationStrategy,
        )

        mock_search = Mock()
        mock_model = Mock()

        strategy = FocusedIterationStrategy(
            model=mock_model,
            search=mock_search,
            max_iterations=1,
            use_browsecomp_optimization=False,
        )

        with patch.object(
            strategy.question_generator,
            "generate_questions",
            side_effect=Exception("LLM error"),
        ):
            result = strategy.analyze_topic("test query")

        assert "error" in result
        assert result["findings"] == []

    def test_progress_callback_is_called(self):
        """Progress callback is called during analysis."""
        from local_deep_research.advanced_search_system.strategies.focused_iteration_strategy import (
            FocusedIterationStrategy,
        )

        mock_search = Mock()
        mock_search.run.return_value = []

        strategy = FocusedIterationStrategy(
            model=Mock(),
            search=mock_search,
            max_iterations=1,
            use_browsecomp_optimization=False,
        )

        callback = Mock()
        strategy.set_progress_callback(callback)

        with patch.object(
            strategy.question_generator,
            "generate_questions",
            return_value=["Q1"],
        ):
            with patch.object(
                strategy.citation_handler,
                "analyze_followup",
                return_value={"content": "Done", "documents": []},
            ):
                strategy.analyze_topic("test query")

        assert callback.call_count >= 1

    def test_early_termination_when_enabled(self):
        """Early termination stops iterations when conditions met."""
        from local_deep_research.advanced_search_system.strategies.focused_iteration_strategy import (
            FocusedIterationStrategy,
        )

        mock_search = Mock()
        mock_search.run.return_value = [
            {"title": "Result", "snippet": "Content"}
        ]

        strategy = FocusedIterationStrategy(
            model=Mock(),
            search=mock_search,
            max_iterations=8,
            enable_early_termination=True,
            use_browsecomp_optimization=False,
        )

        # Mock _should_terminate_early to return True on iteration 2
        with patch.object(
            strategy.question_generator,
            "generate_questions",
            return_value=["Q1"],
        ):
            with patch.object(
                strategy.citation_handler,
                "analyze_followup",
                return_value={"content": "Done", "documents": []},
            ):
                with patch.object(
                    strategy,
                    "_should_terminate_early",
                    side_effect=lambda i: i >= 2,
                ):
                    result = strategy.analyze_topic("test query")

        # Should have completed fewer iterations than max
        assert result["iterations"] < 8

    def test_skips_iteration_without_questions(self):
        """Skips search phase when no questions generated (beyond original query)."""
        from local_deep_research.advanced_search_system.strategies.focused_iteration_strategy import (
            FocusedIterationStrategy,
        )

        mock_search = Mock()
        mock_search.run.return_value = []

        strategy = FocusedIterationStrategy(
            model=Mock(),
            search=mock_search,
            max_iterations=2,
            use_browsecomp_optimization=False,
        )

        call_count = [0]

        def generate_questions_side_effect(**kwargs):
            call_count[0] += 1
            if call_count[0] == 1:
                return ["Q1"]  # First iteration has questions
            return []  # Second iteration has no questions

        with patch.object(
            strategy.question_generator,
            "generate_questions",
            side_effect=generate_questions_side_effect,
        ):
            with patch.object(
                strategy.citation_handler,
                "analyze_followup",
                return_value={"content": "Done", "documents": []},
            ):
                strategy.analyze_topic("test query")

        # Search should only be called for the first iteration (with questions)
        # not for the second iteration (no questions, skipped)
        # First iteration: original query is prepended so questions = ["test query"]
        # which gets searched. Second iteration returns [] so search is skipped.
        assert mock_search.run.call_count >= 1

    def test_accumulates_results_across_iterations(self):
        """Results accumulate across iterations."""
        from local_deep_research.advanced_search_system.strategies.focused_iteration_strategy import (
            FocusedIterationStrategy,
        )

        mock_search = Mock()
        call_count = [0]

        def mock_run(q, **kwargs):
            call_count[0] += 1
            return [{"title": f"Result {call_count[0]}", "snippet": "content"}]

        mock_search.run.side_effect = mock_run

        strategy = FocusedIterationStrategy(
            model=Mock(),
            search=mock_search,
            max_iterations=2,
            questions_per_iteration=1,
            use_browsecomp_optimization=False,
        )

        with patch.object(
            strategy.question_generator,
            "generate_questions",
            return_value=["Q1"],
        ):
            with patch.object(
                strategy.citation_handler,
                "analyze_followup",
                return_value={"content": "Done", "documents": []},
            ):
                strategy.analyze_topic("test query")

        # Results from both iterations should be accumulated
        assert len(strategy.all_search_results) >= 2

    def test_questions_per_iteration_limit_enforced(self):
        """Questions list is trimmed to questions_per_iteration limit."""
        from local_deep_research.advanced_search_system.strategies.focused_iteration_strategy import (
            FocusedIterationStrategy,
        )

        mock_search = Mock()
        mock_search.run.return_value = []

        strategy = FocusedIterationStrategy(
            model=Mock(),
            search=mock_search,
            max_iterations=1,
            questions_per_iteration=2,
            use_browsecomp_optimization=False,
        )

        # Return more questions than the limit
        with patch.object(
            strategy.question_generator,
            "generate_questions",
            return_value=["Q1", "Q2", "Q3", "Q4"],
        ):
            with patch.object(
                strategy.citation_handler,
                "analyze_followup",
                return_value={"content": "Done", "documents": []},
            ):
                strategy.analyze_topic("original query")

        # First iteration questions should be limited
        # Original query is prepended, so final list = ["original query", "Q1"] (limited to 2)
        assert len(strategy.questions_by_iteration[1]) <= 2


class TestBrowseCompOptimization:
    """Tests for BrowseComp optimization paths."""

    def test_uses_explorer_when_browsecomp_enabled(self):
        """Uses progressive explorer when BrowseComp is enabled."""
        from local_deep_research.advanced_search_system.strategies.focused_iteration_strategy import (
            FocusedIterationStrategy,
        )

        mock_search = Mock()
        mock_model = Mock()

        strategy = FocusedIterationStrategy(
            model=mock_model,
            search=mock_search,
            max_iterations=1,
            use_browsecomp_optimization=True,
        )

        # Mock explorer.explore to return results
        mock_progress = Mock()
        mock_progress.found_candidates = {}
        mock_progress.entity_coverage = {}
        strategy.explorer.explore = Mock(
            return_value=(
                [{"title": "Result", "snippet": "content"}],
                mock_progress,
            )
        )

        with patch.object(
            strategy.question_generator,
            "generate_questions",
            return_value=["Q1"],
        ):
            with patch.object(
                strategy.citation_handler,
                "analyze_followup",
                return_value={"content": "Done", "documents": []},
            ):
                strategy.analyze_topic("test query")

        strategy.explorer.explore.assert_called()

    def test_browsecomp_result_includes_candidates(self):
        """BrowseComp results include candidates and entity_coverage."""
        from local_deep_research.advanced_search_system.strategies.focused_iteration_strategy import (
            FocusedIterationStrategy,
        )

        mock_search = Mock()
        mock_model = Mock()

        strategy = FocusedIterationStrategy(
            model=mock_model,
            search=mock_search,
            max_iterations=1,
            use_browsecomp_optimization=True,
        )

        # Mock explorer with progress data
        mock_progress = Mock()
        mock_progress.found_candidates = {"candidate1": 0.8}
        mock_progress.entity_coverage = {"type1": {"entity1", "entity2"}}
        strategy.explorer.explore = Mock(
            return_value=(
                [{"title": "Result", "snippet": "content"}],
                mock_progress,
            )
        )
        strategy.explorer.progress = mock_progress

        with patch.object(
            strategy.question_generator,
            "generate_questions",
            return_value=["Q1"],
        ):
            with patch.object(
                strategy.citation_handler,
                "analyze_followup",
                return_value={"content": "Done", "documents": []},
            ):
                result = strategy.analyze_topic("test query")

        assert "candidates" in result
        assert result["candidates"] == {"candidate1": 0.8}
        assert "entity_coverage" in result

    def test_adaptive_questions_passes_results_by_iteration(self):
        """Adaptive questions mode passes results_by_iteration to generator."""
        from local_deep_research.advanced_search_system.strategies.focused_iteration_strategy import (
            FocusedIterationStrategy,
        )

        mock_search = Mock()
        mock_model = Mock()

        strategy = FocusedIterationStrategy(
            model=mock_model,
            search=mock_search,
            max_iterations=1,
            use_browsecomp_optimization=True,
            enable_adaptive_questions=True,
        )

        mock_progress = Mock()
        mock_progress.found_candidates = {}
        mock_progress.entity_coverage = {}
        strategy.explorer.explore = Mock(return_value=([], mock_progress))

        with patch.object(
            strategy.question_generator,
            "generate_questions",
            return_value=["Q1"],
        ) as mock_gen:
            with patch.object(
                strategy.citation_handler,
                "analyze_followup",
                return_value={"content": "Done", "documents": []},
            ):
                strategy.analyze_topic("test query")

        # When adaptive questions enabled, results_by_iteration should be passed
        call_kwargs = mock_gen.call_args[1]
        assert "results_by_iteration" in call_kwargs
        # It should not be None (adaptive mode)
        assert call_kwargs["results_by_iteration"] is not None

    def test_non_adaptive_passes_none_results(self):
        """Non-adaptive mode passes None for results_by_iteration."""
        from local_deep_research.advanced_search_system.strategies.focused_iteration_strategy import (
            FocusedIterationStrategy,
        )

        strategy = FocusedIterationStrategy(
            model=Mock(),
            search=Mock(),
            max_iterations=1,
            use_browsecomp_optimization=True,
            enable_adaptive_questions=False,
        )

        mock_progress = Mock()
        mock_progress.found_candidates = {}
        mock_progress.entity_coverage = {}
        strategy.explorer.explore = Mock(return_value=([], mock_progress))

        with patch.object(
            strategy.question_generator,
            "generate_questions",
            return_value=["Q1"],
        ) as mock_gen:
            with patch.object(
                strategy.citation_handler,
                "analyze_followup",
                return_value={"content": "Done", "documents": []},
            ):
                strategy.analyze_topic("test query")

        call_kwargs = mock_gen.call_args[1]
        assert call_kwargs["results_by_iteration"] is None
