"""
Tests for report_generator.py - Section Generation and State Management

Tests cover the _research_and_generate_sections() method which:
- Initializes questions from previous iterations
- Manages search system state between subsections
- Restores max_iterations after errors
- Creates default subsections when none provided
"""

from unittest.mock import MagicMock, patch

import pytest


class TestSectionGenerationStateManagement:
    """Tests for state management during section generation."""

    @pytest.fixture
    def report_generator(self):
        """Create a report generator with mocked dependencies."""
        with patch("local_deep_research.report_generator.AdvancedSearchSystem"):
            with patch(
                "local_deep_research.report_generator.get_llm"
            ) as mock_get_llm:
                mock_llm = MagicMock()
                mock_get_llm.return_value = mock_llm

                # Create a mock search system with necessary attributes
                mock_search_system = MagicMock()
                mock_search_system.all_links_of_system = []
                mock_search_system.max_iterations = 3
                mock_search_system.questions_by_iteration = {}
                mock_search_system.analyze_topic.return_value = {
                    "current_knowledge": "Generated content for section"
                }

                # Create a mock strategy
                mock_strategy = MagicMock()
                mock_strategy.questions_by_iteration = {}
                mock_strategy.settings_snapshot = {"search.iterations": 3}
                mock_strategy.max_iterations = 3
                mock_search_system.strategy = mock_strategy

                from local_deep_research.report_generator import (
                    IntegratedReportGenerator,
                )

                generator = IntegratedReportGenerator(llm=mock_llm)
                generator.search_system = mock_search_system
                yield generator

    def test_questions_preserved_from_initial_findings(self, report_generator):
        """Questions from initial research should be passed to search system."""
        initial_findings = {
            "current_knowledge": "test content",
            "questions_by_iteration": {
                0: ["Q1: What is the topic?", "Q2: How does it work?"],
                1: ["Q3: What are the applications?"],
            },
        }

        structure = [
            {
                "name": "Introduction",
                "subsections": [
                    {"name": "Overview", "purpose": "Intro purpose"}
                ],
            }
        ]

        report_generator._research_and_generate_sections(
            initial_findings, structure, "test query"
        )

        # Verify questions were copied to strategy
        assert (
            report_generator.search_system.strategy.questions_by_iteration
            == initial_findings["questions_by_iteration"]
        )

    def test_empty_questions_handled_gracefully(self, report_generator):
        """Empty questions_by_iteration should not cause errors."""
        initial_findings = {
            "current_knowledge": "test content",
            "questions_by_iteration": {},
        }

        structure = [
            {
                "name": "Test Section",
                "subsections": [{"name": "Sub", "purpose": "Purpose"}],
            }
        ]

        # Should not raise any exception
        sections = report_generator._research_and_generate_sections(
            initial_findings, structure, "test query"
        )

        assert "Test Section" in sections

    def test_missing_questions_key_handled(self, report_generator):
        """Missing questions_by_iteration key should not cause errors."""
        initial_findings = {"current_knowledge": "test content"}

        structure = [
            {
                "name": "Test Section",
                "subsections": [{"name": "Sub", "purpose": "Purpose"}],
            }
        ]

        # Should not raise any exception
        sections = report_generator._research_and_generate_sections(
            initial_findings, structure, "test query"
        )

        assert "Test Section" in sections


class TestSectionGenerationEmptySubsections:
    """Tests for handling sections without subsections."""

    @pytest.fixture
    def report_generator(self):
        """Create a report generator with mocked dependencies."""
        with patch("local_deep_research.report_generator.AdvancedSearchSystem"):
            with patch(
                "local_deep_research.report_generator.get_llm"
            ) as mock_get_llm:
                mock_llm = MagicMock()
                mock_get_llm.return_value = mock_llm

                mock_search_system = MagicMock()
                mock_search_system.all_links_of_system = []
                mock_search_system.max_iterations = 3
                mock_search_system.analyze_topic.return_value = {
                    "current_knowledge": "Generated content"
                }

                from local_deep_research.report_generator import (
                    IntegratedReportGenerator,
                )

                generator = IntegratedReportGenerator(llm=mock_llm)
                generator.search_system = mock_search_system
                yield generator

    def test_section_with_empty_subsections_creates_default(
        self, report_generator
    ):
        """Section with empty subsections list should get a default subsection."""
        structure = [{"name": "Standalone Section", "subsections": []}]

        initial_findings = {
            "current_knowledge": "test",
            "questions_by_iteration": {},
        }

        sections = report_generator._research_and_generate_sections(
            initial_findings, structure, "test query"
        )

        assert "Standalone Section" in sections
        # analyze_topic should have been called for the auto-created subsection
        report_generator.search_system.analyze_topic.assert_called()

    def test_section_with_pipe_in_name_parsed_for_subsection(
        self, report_generator
    ):
        """Section name with pipe should be parsed into subsection name and purpose."""
        structure = [
            {"name": "Main Topic | Purpose of this section", "subsections": []}
        ]

        initial_findings = {
            "current_knowledge": "test",
            "questions_by_iteration": {},
        }

        sections = report_generator._research_and_generate_sections(
            initial_findings, structure, "test query"
        )

        # The section should be processed with subsections created from the pipe-split name
        assert "Main Topic | Purpose of this section" in sections

    def test_multiple_empty_sections_each_get_default(self, report_generator):
        """Multiple sections without subsections each get their own default."""
        structure = [
            {"name": "Section A", "subsections": []},
            {"name": "Section B", "subsections": []},
            {"name": "Section C", "subsections": []},
        ]

        initial_findings = {
            "current_knowledge": "test",
            "questions_by_iteration": {},
        }

        sections = report_generator._research_and_generate_sections(
            initial_findings, structure, "test query"
        )

        assert len(sections) == 3
        assert "Section A" in sections
        assert "Section B" in sections
        assert "Section C" in sections


class TestMaxIterationsRestoration:
    """Tests for max_iterations preservation and restoration."""

    @pytest.fixture
    def report_generator(self):
        """Create a report generator with mocked dependencies."""
        with patch("local_deep_research.report_generator.AdvancedSearchSystem"):
            with patch(
                "local_deep_research.report_generator.get_llm"
            ) as mock_get_llm:
                mock_llm = MagicMock()
                mock_get_llm.return_value = mock_llm

                mock_search_system = MagicMock()
                mock_search_system.all_links_of_system = []
                mock_search_system.max_iterations = 5  # Original value
                mock_search_system.analyze_topic.return_value = {
                    "current_knowledge": "Content"
                }

                # Create a mock strategy with real settings_snapshot dict
                mock_strategy = MagicMock()
                mock_strategy.settings_snapshot = {"search.iterations": 5}
                mock_strategy.max_iterations = 5
                mock_search_system.strategy = mock_strategy

                from local_deep_research.report_generator import (
                    IntegratedReportGenerator,
                )

                generator = IntegratedReportGenerator(llm=mock_llm)
                generator.search_system = mock_search_system
                yield generator

    def test_max_iterations_restored_after_section(self, report_generator):
        """max_iterations should be restored to original value after each subsection."""
        original_max = report_generator.search_system.max_iterations

        structure = [
            {
                "name": "Test",
                "subsections": [{"name": "Sub", "purpose": "Purpose"}],
            }
        ]

        report_generator._research_and_generate_sections(
            {"current_knowledge": "test", "questions_by_iteration": {}},
            structure,
            "query",
        )

        # After generation, max_iterations should be back to original
        assert report_generator.search_system.max_iterations == original_max

    def test_max_iterations_set_to_one_during_subsection_research(
        self, report_generator
    ):
        """search.iterations should be set to 1 in strategy settings_snapshot during subsection research."""
        iterations_during_search = []

        def capture_iterations(*args, **kwargs):
            iterations_during_search.append(
                report_generator.search_system.strategy.settings_snapshot.get(
                    "search.iterations"
                )
            )
            return {"current_knowledge": "Content"}

        report_generator.search_system.analyze_topic.side_effect = (
            capture_iterations
        )

        structure = [
            {
                "name": "Test",
                "subsections": [
                    {"name": "Sub1", "purpose": "P1"},
                    {"name": "Sub2", "purpose": "P2"},
                ],
            }
        ]

        report_generator._research_and_generate_sections(
            {"current_knowledge": "test", "questions_by_iteration": {}},
            structure,
            "query",
        )

        # Each subsection should have had search.iterations=1
        assert all(i == 1 for i in iterations_during_search)

    def test_max_iterations_restored_after_multiple_sections(
        self, report_generator
    ):
        """max_iterations restoration should work across multiple sections."""
        original_max = report_generator.search_system.max_iterations

        structure = [
            {
                "name": "Section1",
                "subsections": [{"name": "Sub1", "purpose": "P1"}],
            },
            {
                "name": "Section2",
                "subsections": [
                    {"name": "Sub2a", "purpose": "P2a"},
                    {"name": "Sub2b", "purpose": "P2b"},
                ],
            },
        ]

        report_generator._research_and_generate_sections(
            {"current_knowledge": "test", "questions_by_iteration": {}},
            structure,
            "query",
        )

        assert report_generator.search_system.max_iterations == original_max


class TestStateIsolationBetweenSections:
    """Tests for ensuring state doesn't leak between sections."""

    @pytest.fixture
    def report_generator(self):
        """Create a report generator with mocked dependencies."""
        with patch("local_deep_research.report_generator.AdvancedSearchSystem"):
            with patch(
                "local_deep_research.report_generator.get_llm"
            ) as mock_get_llm:
                mock_llm = MagicMock()
                mock_get_llm.return_value = mock_llm

                mock_search_system = MagicMock()
                mock_search_system.all_links_of_system = []
                mock_search_system.max_iterations = 3
                mock_search_system.analyze_topic.return_value = {
                    "current_knowledge": "Content"
                }

                from local_deep_research.report_generator import (
                    IntegratedReportGenerator,
                )

                generator = IntegratedReportGenerator(llm=mock_llm)
                generator.search_system = mock_search_system
                yield generator

    def test_sections_content_independent(self, report_generator):
        """Each section should receive independent content."""
        call_count = [0]

        def unique_content(*args, **kwargs):
            call_count[0] += 1
            return {"current_knowledge": f"Content for call {call_count[0]}"}

        report_generator.search_system.analyze_topic.side_effect = (
            unique_content
        )

        structure = [
            {
                "name": "Section1",
                "subsections": [{"name": "Sub1", "purpose": "P1"}],
            },
            {
                "name": "Section2",
                "subsections": [{"name": "Sub2", "purpose": "P2"}],
            },
        ]

        sections = report_generator._research_and_generate_sections(
            {"current_knowledge": "test", "questions_by_iteration": {}},
            structure,
            "query",
        )

        # Each section should have different content
        assert "Content for call 1" in sections["Section1"]
        assert "Content for call 2" in sections["Section2"]

    def test_context_includes_other_sections(self, report_generator):
        """Each subsection query should include context about other sections."""
        captured_queries = []

        def capture_query(query, *args, **kwargs):
            captured_queries.append(query)
            return {"current_knowledge": "Content"}

        report_generator.search_system.analyze_topic.side_effect = capture_query

        structure = [
            {
                "name": "Introduction",
                "subsections": [{"name": "Overview", "purpose": "Intro"}],
            },
            {
                "name": "Main Content",
                "subsections": [{"name": "Details", "purpose": "Main info"}],
            },
            {
                "name": "Conclusion",
                "subsections": [{"name": "Summary", "purpose": "Wrap up"}],
            },
        ]

        report_generator._research_and_generate_sections(
            {"current_knowledge": "test", "questions_by_iteration": {}},
            structure,
            "test query",
        )

        # First section query should mention other sections
        assert (
            "Main Content" in captured_queries[0]
            or "Conclusion" in captured_queries[0]
        )
        # Middle section should mention Introduction and Conclusion
        assert (
            "Introduction" in captured_queries[1]
            or "Conclusion" in captured_queries[1]
        )


class TestEmptyResultHandling:
    """Tests for handling empty or missing content from search system."""

    @pytest.fixture
    def report_generator(self):
        """Create a report generator with mocked dependencies."""
        with patch("local_deep_research.report_generator.AdvancedSearchSystem"):
            with patch(
                "local_deep_research.report_generator.get_llm"
            ) as mock_get_llm:
                mock_llm = MagicMock()
                mock_get_llm.return_value = mock_llm

                mock_search_system = MagicMock()
                mock_search_system.all_links_of_system = []
                mock_search_system.max_iterations = 3

                from local_deep_research.report_generator import (
                    IntegratedReportGenerator,
                )

                generator = IntegratedReportGenerator(llm=mock_llm)
                generator.search_system = mock_search_system
                yield generator

    def test_empty_current_knowledge_shows_placeholder(self, report_generator):
        """Empty current_knowledge should result in placeholder text."""
        report_generator.search_system.analyze_topic.return_value = {
            "current_knowledge": ""
        }

        structure = [
            {
                "name": "Test",
                "subsections": [{"name": "Sub", "purpose": "Purpose"}],
            }
        ]

        sections = report_generator._research_and_generate_sections(
            {"current_knowledge": "test", "questions_by_iteration": {}},
            structure,
            "query",
        )

        assert "Limited information was found" in sections["Test"]

    def test_none_current_knowledge_shows_placeholder(self, report_generator):
        """None current_knowledge should result in placeholder text."""
        report_generator.search_system.analyze_topic.return_value = {
            "current_knowledge": None
        }

        structure = [
            {
                "name": "Test",
                "subsections": [{"name": "Sub", "purpose": "Purpose"}],
            }
        ]

        sections = report_generator._research_and_generate_sections(
            {"current_knowledge": "test", "questions_by_iteration": {}},
            structure,
            "query",
        )

        assert "Limited information was found" in sections["Test"]

    def test_missing_current_knowledge_key_shows_placeholder(
        self, report_generator
    ):
        """Missing current_knowledge key should result in placeholder text."""
        report_generator.search_system.analyze_topic.return_value = {}

        structure = [
            {
                "name": "Test",
                "subsections": [{"name": "Sub", "purpose": "Purpose"}],
            }
        ]

        sections = report_generator._research_and_generate_sections(
            {"current_knowledge": "test", "questions_by_iteration": {}},
            structure,
            "query",
        )

        assert "Limited information was found" in sections["Test"]
