"""
Extended tests for RapidSearchStrategy - Optimized rapid search implementation.

Tests cover:
- Strategy initialization
- Search execution flow
- Snippet collection
- Question generation
- Final synthesis
- Progress callback handling
- Error handling
"""


class TestStrategyInitialization:
    """Tests for RapidSearchStrategy initialization."""

    def test_progress_callback_default_none(self):
        """Progress callback should default to None."""
        progress_callback = None
        assert progress_callback is None

    def test_questions_by_iteration_initialized(self):
        """Questions by iteration should be initialized."""
        questions_by_iteration = {}
        assert isinstance(questions_by_iteration, dict)
        assert len(questions_by_iteration) == 0

    def test_all_links_initialized(self):
        """All links list should be initialized."""
        all_links = []
        assert isinstance(all_links, list)

    def test_citation_handler_optional(self):
        """Citation handler should be optional."""
        citation_handler = None
        # Should use provided or create one
        if citation_handler is None:
            handler = "default_handler"
        else:
            handler = citation_handler
        assert handler == "default_handler"


class TestSearchExecution:
    """Tests for search execution flow."""

    def test_initial_search_performed(self):
        """Should perform initial search for main query."""
        query = "What is machine learning?"
        # Simulated search execution
        search_performed = True
        assert search_performed is True
        assert len(query) > 0

    def test_results_collected(self):
        """Should collect search results."""
        results = [
            {"title": "Result 1", "snippet": "Snippet 1"},
            {"title": "Result 2", "snippet": "Snippet 2"},
        ]
        assert len(results) == 2

    def test_empty_results_handled(self):
        """Should handle empty search results."""
        results = []
        if not results:
            results = []
        assert results == []


class TestSnippetCollection:
    """Tests for snippet collection."""

    def test_snippets_extracted_from_results(self):
        """Should extract snippets from search results."""
        results = [
            {
                "snippet": "Snippet text 1",
                "title": "Title 1",
                "link": "http://a.com",
            },
            {
                "snippet": "Snippet text 2",
                "title": "Title 2",
                "link": "http://b.com",
            },
        ]

        collected_snippets = []
        for result in results:
            if "snippet" in result:
                collected_snippets.append(
                    {
                        "text": result["snippet"],
                        "source": result.get("title", "Unknown"),
                        "link": result.get("link", ""),
                    }
                )

        assert len(collected_snippets) == 2
        assert collected_snippets[0]["text"] == "Snippet text 1"

    def test_snippet_structure(self):
        """Snippet should have expected structure."""
        snippet = {
            "text": "Snippet text",
            "source": "Source title",
            "link": "http://example.com",
            "query": "original query",
        }

        assert "text" in snippet
        assert "source" in snippet
        assert "link" in snippet
        assert "query" in snippet

    def test_missing_snippet_skipped(self):
        """Results without snippet should be skipped."""
        results = [
            {"title": "Title 1", "link": "http://a.com"},  # No snippet
            {"snippet": "Snippet 2", "title": "Title 2"},
        ]

        collected = []
        for result in results:
            if "snippet" in result:
                collected.append(result["snippet"])

        assert len(collected) == 1


class TestQuestionGeneration:
    """Tests for follow-up question generation."""

    def test_questions_generated(self):
        """Should generate follow-up questions."""
        questions = ["Q1?", "Q2?", "Q3?"]
        assert len(questions) == 3

    def test_fewer_questions_for_speed(self):
        """Should generate fewer questions for speed."""
        questions_per_iteration = 3  # Fewer than standard
        assert questions_per_iteration == 3

    def test_questions_stored_in_iteration(self):
        """Questions should be stored by iteration."""
        questions_by_iteration = {}
        questions = ["Q1?", "Q2?"]
        questions_by_iteration[0] = questions

        assert 0 in questions_by_iteration
        assert len(questions_by_iteration[0]) == 2


class TestFinalSynthesis:
    """Tests for final synthesis."""

    def test_synthesis_performed_once(self):
        """Synthesis should be performed only once."""
        synthesis_count = 0

        # Simulated synthesis
        synthesis_count += 1

        assert synthesis_count == 1

    def test_synthesis_uses_all_snippets(self):
        """Synthesis should use all collected snippets."""
        collected_snippets = [
            {"text": "Snippet 1"},
            {"text": "Snippet 2"},
            {"text": "Snippet 3"},
        ]

        # All snippets should be available for synthesis
        snippets_for_synthesis = collected_snippets
        assert len(snippets_for_synthesis) == 3

    def test_synthesis_result_structure(self):
        """Synthesis result should have expected structure."""
        result = {
            "content": "Synthesized content",
            "documents": [{"title": "Doc 1"}],
        }

        assert "content" in result
        assert "documents" in result


class TestProgressCallback:
    """Tests for progress callback handling."""

    def test_progress_initialization(self):
        """Should report initialization progress."""
        progress_updates = []

        def callback(msg, pct, data):
            progress_updates.append((msg, pct, data))

        callback("Initializing rapid research system", 5, {"phase": "init"})

        assert len(progress_updates) == 1
        assert progress_updates[0][1] == 5

    def test_progress_search_phase(self):
        """Should report search progress."""
        progress_updates = []

        def callback(msg, pct, data):
            progress_updates.append((msg, pct, data))

        callback("Performing initial search", 10, {"phase": "search"})

        assert progress_updates[0][2]["phase"] == "search"

    def test_progress_question_iteration(self):
        """Should report progress per question."""
        questions = ["Q1", "Q2", "Q3"]
        progress_values = []

        for q_idx, _question in enumerate(questions):
            question_progress = 30 + ((q_idx + 1) / len(questions) * 40)
            progress_values.append(int(question_progress))

        # Progress should increase with each question
        assert progress_values[2] > progress_values[0]

    def test_progress_synthesis_phase(self):
        """Should report synthesis progress."""
        progress_updates = []

        def callback(msg, pct, data):
            progress_updates.append((msg, pct, data))

        callback(
            "Synthesizing all collected information",
            80,
            {"phase": "final_synthesis"},
        )

        assert progress_updates[0][1] == 80

    def test_progress_completion(self):
        """Should report 100% on completion."""
        progress_updates = []

        def callback(msg, pct, data):
            progress_updates.append((msg, pct, data))

        callback("Research complete", 100, {"phase": "complete"})

        assert progress_updates[0][1] == 100


class TestReturnValue:
    """Tests for return value structure."""

    def test_return_has_findings(self):
        """Return should have findings key."""
        result = {
            "findings": [],
            "iterations": 1,
        }
        assert "findings" in result

    def test_return_has_iterations(self):
        """Return should have iterations key."""
        result = {
            "findings": [],
            "iterations": 1,
        }
        assert result["iterations"] == 1

    def test_iterations_always_one(self):
        """Rapid mode always has 1 iteration."""
        iterations = 1
        assert iterations == 1

    def test_return_has_questions(self):
        """Return should have questions key."""
        result = {
            "questions": {0: ["Q1", "Q2"]},
        }
        assert "questions" in result

    def test_return_has_formatted_findings(self):
        """Return should have formatted_findings key."""
        result = {
            "formatted_findings": "Formatted text...",
        }
        assert "formatted_findings" in result

    def test_return_has_current_knowledge(self):
        """Return should have current_knowledge key."""
        result = {
            "current_knowledge": "Synthesized knowledge...",
        }
        assert "current_knowledge" in result


class TestFindingStructure:
    """Tests for finding structure."""

    def test_finding_has_phase(self):
        """Finding should have phase key."""
        finding = {
            "phase": "Final synthesis",
            "content": "Content",
        }
        assert finding["phase"] == "Final synthesis"

    def test_finding_has_content(self):
        """Finding should have content key."""
        finding = {
            "phase": "Final synthesis",
            "content": "Synthesized content here",
        }
        assert "content" in finding

    def test_finding_has_question(self):
        """Finding should have question key."""
        finding = {
            "phase": "Final synthesis",
            "content": "Content",
            "question": "Original query",
        }
        assert "question" in finding

    def test_finding_has_search_results(self):
        """Finding should have search_results key."""
        finding = {
            "phase": "Final synthesis",
            "content": "Content",
            "search_results": [{"title": "Result 1"}],
        }
        assert "search_results" in finding

    def test_finding_has_documents(self):
        """Finding should have documents key."""
        finding = {
            "phase": "Final synthesis",
            "content": "Content",
            "documents": [{"doc": "1"}],
        }
        assert "documents" in finding


class TestSearchEngineValidation:
    """Tests for search engine validation."""

    def test_no_search_engine_error(self):
        """Should return error when no search engine."""
        search = None

        if not search:
            result = {
                "findings": [],
                "iterations": 0,
                "error": "No search engine available",
                "formatted_findings": "Error: Unable to conduct research without a search engine.",
            }
        else:
            result = {"findings": [{"content": "data"}]}

        assert "error" in result
        assert result["iterations"] == 0


class TestLinkExtraction:
    """Tests for link extraction."""

    def test_links_extracted_from_results(self):
        """Should extract links from search results."""
        results = [
            {"title": "Title 1", "link": "http://example1.com"},
            {"title": "Title 2", "link": "http://example2.com"},
        ]

        links = []
        for result in results:
            if "link" in result:
                links.append(result["link"])

        assert len(links) == 2

    def test_links_accumulated(self):
        """Links should be accumulated across searches."""
        all_links = []
        initial_links = ["http://a.com"]
        followup_links = ["http://b.com", "http://c.com"]

        all_links.extend(initial_links)
        all_links.extend(followup_links)

        assert len(all_links) == 3


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

    def test_search_error_handled(self):
        """Should handle search errors gracefully."""
        try:
            raise Exception("Search error")
        except Exception:
            results = []

        assert results == []

    def test_synthesis_error_creates_error_finding(self):
        """Synthesis error should create error finding."""
        try:
            raise Exception("Synthesis error")
        except Exception as e:
            error_msg = f"Error synthesizing final answer: {e!s}"
            finding = {
                "phase": "Error",
                "content": error_msg,
            }

        assert finding["phase"] == "Error"
        assert "Synthesis error" in finding["content"]

    def test_error_progress_reported(self):
        """Errors should be reported via progress callback."""
        progress_updates = []

        def callback(msg, pct, data):
            progress_updates.append((msg, pct, data))

        callback(
            "Error during search",
            15,
            {"phase": "search_error", "error": "Search error"},
        )

        assert progress_updates[0][2]["phase"] == "search_error"


class TestResultCounts:
    """Tests for result count tracking."""

    def test_result_count_in_progress(self):
        """Result count should be in progress data."""
        results = [{"title": "R1"}, {"title": "R2"}, {"title": "R3"}]
        progress_data = {
            "phase": "search_complete",
            "result_count": len(results),
        }

        assert progress_data["result_count"] == 3

    def test_zero_results_reported(self):
        """Zero results should be reported."""
        results = []
        progress_data = {
            "phase": "search_complete",
            "result_count": len(results),
        }

        assert progress_data["result_count"] == 0


class TestStrategyName:
    """Tests for strategy identification."""

    def test_strategy_name_rapid(self):
        """Strategy should be identified as rapid."""
        strategy_name = "rapid"
        progress_data = {"phase": "init", "strategy": strategy_name}

        assert progress_data["strategy"] == "rapid"
