"""
Extended tests for IntegratedReportGenerator - Research report generation.

Tests cover:
- Report generator initialization
- Report structure determination
- Section research and generation
- Final report formatting
- Error report generation
- Table of contents generation
- Metadata handling
"""

from datetime import datetime, UTC


class TestReportGeneratorInitialization:
    """Tests for IntegratedReportGenerator initialization."""

    def test_default_searches_per_section(self):
        """Default searches per section should be 2."""
        searches_per_section = 2
        assert searches_per_section == 2

    def test_custom_searches_per_section(self):
        """Should accept custom searches per section."""
        searches_per_section = 5
        assert searches_per_section == 5

    def test_search_system_assignment(self):
        """Should assign search system when provided."""
        search_system = "mock_search_system"
        assigned = search_system
        assert assigned == "mock_search_system"

    def test_llm_from_search_system(self):
        """Should use LLM from search system if provided."""
        search_system_llm = "search_system_llm"
        provided_llm = None

        model = provided_llm or search_system_llm
        assert model == "search_system_llm"

    def test_llm_override(self):
        """Should use provided LLM over search system LLM."""
        search_system_llm = "search_system_llm"
        provided_llm = "custom_llm"

        model = provided_llm or search_system_llm
        assert model == "custom_llm"


class TestGenerateReport:
    """Tests for generate_report method."""

    def test_returns_dict(self):
        """generate_report should return a dict."""
        report = {"content": "Report content", "metadata": {}}
        assert isinstance(report, dict)

    def test_report_has_content_key(self):
        """Report should have content key."""
        report = {"content": "# Report\n\nContent here", "metadata": {}}
        assert "content" in report

    def test_report_has_metadata_key(self):
        """Report should have metadata key."""
        report = {"content": "Content", "metadata": {"query": "test"}}
        assert "metadata" in report

    def test_report_generation_steps(self):
        """Report generation should follow steps."""
        # Step 1: Determine structure
        structure = [{"name": "Section 1", "subsections": []}]

        # Step 2: Research and generate sections
        sections = {"Section 1": "Content"}

        # Step 3: Format final report
        report = {"content": "Formatted", "metadata": {}}

        assert len(structure) == 1
        assert len(sections) == 1
        assert "content" in report


class TestDetermineReportStructure:
    """Tests for _determine_report_structure method."""

    def test_returns_list(self):
        """_determine_report_structure should return a list."""
        structure = []
        assert isinstance(structure, list)

    def test_structure_item_has_name(self):
        """Structure item should have name key."""
        section = {"name": "Introduction", "subsections": []}
        assert "name" in section

    def test_structure_item_has_subsections(self):
        """Structure item should have subsections key."""
        section = {"name": "Introduction", "subsections": []}
        assert "subsections" in section

    def test_subsection_has_name(self):
        """Subsection should have name key."""
        subsection = {"name": "Overview", "purpose": "Provide overview"}
        assert "name" in subsection

    def test_subsection_has_purpose(self):
        """Subsection should have purpose key."""
        subsection = {"name": "Overview", "purpose": "Provide overview"}
        assert "purpose" in subsection

    def test_parse_section_from_line(self):
        """Should parse section from numbered line."""
        line = "1. Introduction"

        if line.strip().startswith(tuple("123456789")):
            section_name = line.split(".")[1].strip()
            current_section = {"name": section_name, "subsections": []}

        assert current_section["name"] == "Introduction"

    def test_parse_subsection_from_line(self):
        """Should parse subsection from bullet line."""
        line = "- Overview | Provide an overview"
        parts = line.strip("- ").split("|")

        if len(parts) == 2:
            subsection = {"name": parts[0].strip(), "purpose": parts[1].strip()}

        assert subsection["name"] == "Overview"
        assert subsection["purpose"] == "Provide an overview"

    def test_parse_subsection_without_purpose(self):
        """Should handle subsection without purpose."""
        line = "- Background"
        parts = line.strip("- ").split("|")

        if len(parts) == 1 and parts[0].strip():
            subsection = {
                "name": parts[0].strip(),
                "purpose": f"Provide detailed information about {parts[0].strip()}",
            }

        assert subsection["name"] == "Background"
        assert "Background" in subsection["purpose"]

    def test_filter_source_sections(self):
        """Should filter out source-related sections."""
        structure = [
            {"name": "Introduction", "subsections": []},
            {"name": "Sources and References", "subsections": []},
        ]

        source_keywords = ["source", "citation", "reference", "bibliography"]
        last_section = structure[-1]
        section_name_lower = last_section["name"].lower()

        if any(keyword in section_name_lower for keyword in source_keywords):
            structure = structure[:-1]

        assert len(structure) == 1
        assert structure[0]["name"] == "Introduction"


class TestResearchAndGenerateSections:
    """Tests for _research_and_generate_sections method."""

    def test_returns_dict(self):
        """_research_and_generate_sections should return a dict."""
        sections = {}
        assert isinstance(sections, dict)

    def test_section_key_is_name(self):
        """Section key should be section name."""
        sections = {"Introduction": "Content"}
        assert "Introduction" in sections

    def test_section_content_includes_header(self):
        """Section content should include header."""
        section_name = "Introduction"
        content = f"# {section_name}\n"

        assert f"# {section_name}" in content

    def test_section_without_subsections_creates_default(self):
        """Section without subsections should create default subsection."""
        section = {"name": "Introduction", "subsections": []}

        if not section["subsections"]:
            section["subsections"] = [
                {
                    "name": section["name"],
                    "purpose": f"Provide comprehensive content for {section['name']}",
                }
            ]

        assert len(section["subsections"]) == 1
        assert section["subsections"][0]["name"] == "Introduction"

    def test_multiple_subsections_get_headers(self):
        """Multiple subsections should get individual headers."""
        section = {
            "name": "Overview",
            "subsections": [
                {"name": "Part A", "purpose": "Purpose A"},
                {"name": "Part B", "purpose": "Purpose B"},
            ],
        }

        content = []
        for subsection in section["subsections"]:
            if len(section["subsections"]) > 1:
                content.append(f"## {subsection['name']}\n")
                content.append(f"_{subsection['purpose']}_\n\n")

        result = "".join(content)
        assert "## Part A" in result
        assert "## Part B" in result

    def test_single_subsection_no_header(self):
        """Single subsection should not add subsection header."""
        section = {
            "name": "Introduction",
            "subsections": [{"name": "Introduction", "purpose": "Purpose"}],
        }

        is_section_level = len(section["subsections"]) == 1
        assert is_section_level is True

    def test_other_subsections_context(self):
        """Should generate other subsections context."""
        subsections = [
            {"name": "Part A", "purpose": "Purpose A"},
            {"name": "Part B", "purpose": "Purpose B"},
            {"name": "Part C", "purpose": "Purpose C"},
        ]
        current = "Part B"

        other_subsections = [
            f"- {s['name']}: {s['purpose']}"
            for s in subsections
            if s["name"] != current
        ]

        other_text = (
            "\n".join(other_subsections) if other_subsections else "None"
        )

        assert "Part A" in other_text
        assert "Part C" in other_text
        assert "Part B" not in other_text

    def test_other_sections_context(self):
        """Should generate other sections context."""
        structure = [
            {"name": "Introduction"},
            {"name": "Analysis"},
            {"name": "Conclusion"},
        ]
        current = "Analysis"

        other_sections = [
            f"- {s['name']}" for s in structure if s["name"] != current
        ]
        other_text = "\n".join(other_sections) if other_sections else "None"

        assert "Introduction" in other_text
        assert "Conclusion" in other_text
        assert "Analysis" not in other_text

    def test_limited_information_fallback(self):
        """Should show fallback when limited information."""
        results = {"current_knowledge": None}

        if results.get("current_knowledge"):
            content = results["current_knowledge"]
        else:
            content = "*Limited information was found for this subsection.*\n"

        assert "Limited information" in content


class TestFormatFinalReport:
    """Tests for _format_final_report method."""

    def test_returns_dict(self):
        """_format_final_report should return a dict."""
        report = {"content": "Content", "metadata": {}}
        assert isinstance(report, dict)

    def test_generates_table_of_contents(self):
        """Should generate table of contents."""
        structure = [
            {"name": "Introduction", "subsections": []},
            {"name": "Analysis", "subsections": []},
        ]

        toc = ["# Table of Contents\n"]
        for i, section in enumerate(structure, 1):
            toc.append(f"{i}. **{section['name']}**")

        result = "\n".join(toc)
        assert "# Table of Contents" in result
        assert "1. **Introduction**" in result
        assert "2. **Analysis**" in result

    def test_toc_includes_subsections(self):
        """TOC should include subsections."""
        section = {
            "name": "Overview",
            "subsections": [
                {"name": "Part A", "purpose": "Purpose A"},
                {"name": "Part B", "purpose": "Purpose B"},
            ],
        }

        toc = [f"1. **{section['name']}**"]
        for j, subsection in enumerate(section["subsections"], 1):
            toc.append(
                f"   1.{j} {subsection['name']} | _{subsection['purpose']}_"
            )

        result = "\n".join(toc)
        assert "1.1 Part A" in result
        assert "1.2 Part B" in result

    def test_includes_research_summary(self):
        """Should include research summary."""
        report_parts = ["# Research Summary"]
        report_parts.append(
            "This report was researched using an advanced search system."
        )

        result = "\n".join(report_parts)
        assert "# Research Summary" in result

    def test_includes_sources_section(self):
        """Should include sources section."""
        formatted_links = "[1] http://example.com"
        final_content = "Report content\n\n## Sources\n\n" + formatted_links

        assert "## Sources" in final_content
        assert "http://example.com" in final_content


class TestMetadataGeneration:
    """Tests for metadata generation."""

    def test_metadata_has_generated_at(self):
        """Metadata should have generated_at timestamp."""
        metadata = {"generated_at": datetime.now(UTC).isoformat()}
        assert "generated_at" in metadata
        assert "T" in metadata["generated_at"]  # ISO format has T separator

    def test_metadata_has_initial_sources(self):
        """Metadata should have initial_sources count."""
        all_links = ["link1", "link2", "link3"]
        metadata = {"initial_sources": len(all_links)}
        assert metadata["initial_sources"] == 3

    def test_metadata_has_sections_researched(self):
        """Metadata should have sections_researched count."""
        structure = [{"name": "Section 1"}, {"name": "Section 2"}]
        metadata = {"sections_researched": len(structure)}
        assert metadata["sections_researched"] == 2

    def test_metadata_has_searches_per_section(self):
        """Metadata should have searches_per_section."""
        metadata = {"searches_per_section": 2}
        assert metadata["searches_per_section"] == 2

    def test_metadata_has_query(self):
        """Metadata should have query."""
        metadata = {"query": "What is machine learning?"}
        assert metadata["query"] == "What is machine learning?"

    def test_complete_metadata_structure(self):
        """Metadata should have complete structure."""
        metadata = {
            "generated_at": datetime.now(UTC).isoformat(),
            "initial_sources": 10,
            "sections_researched": 5,
            "searches_per_section": 2,
            "query": "Test query",
        }

        expected_keys = [
            "generated_at",
            "initial_sources",
            "sections_researched",
            "searches_per_section",
            "query",
        ]

        for key in expected_keys:
            assert key in metadata


class TestGenerateErrorReport:
    """Tests for _generate_error_report method."""

    def test_returns_string(self):
        """_generate_error_report should return a string."""
        query = "Test query"
        error_msg = "Test error"
        error_report = (
            f"=== ERROR REPORT ===\nQuery: {query}\nError: {error_msg}"
        )

        assert isinstance(error_report, str)

    def test_includes_error_header(self):
        """Error report should include header."""
        error_report = "=== ERROR REPORT ==="
        assert "ERROR REPORT" in error_report

    def test_includes_query(self):
        """Error report should include query."""
        query = "What is AI?"
        error_report = f"Query: {query}"
        assert "What is AI?" in error_report

    def test_includes_error_message(self):
        """Error report should include error message."""
        error_msg = "Connection timeout"
        error_report = f"Error: {error_msg}"
        assert "Connection timeout" in error_report


class TestQuestionPreservation:
    """Tests for question preservation from initial research."""

    def test_preserve_existing_questions(self):
        """Should preserve questions from initial research."""
        initial_findings = {
            "questions_by_iteration": {0: ["Q1?", "Q2?"], 1: ["Q3?"]}
        }

        existing_questions = initial_findings.get("questions_by_iteration", {})
        assert len(existing_questions) == 2
        assert 0 in existing_questions

    def test_copy_questions_to_search_system(self):
        """Should copy questions to search system."""
        existing_questions = {0: ["Q1?"], 1: ["Q2?"]}

        # Simulating copy to search system
        search_system_questions = existing_questions.copy()

        assert search_system_questions[0] == ["Q1?"]
        assert search_system_questions[1] == ["Q2?"]

    def test_empty_questions_handled(self):
        """Should handle empty questions gracefully."""
        initial_findings = {}

        existing_questions = initial_findings.get("questions_by_iteration", {})
        if existing_questions:
            has_questions = True
        else:
            has_questions = False

        assert has_questions is False


class TestMaxIterationsControl:
    """Tests for max iterations control during section research."""

    def test_save_original_max_iterations(self):
        """Should save original max iterations."""
        original = 3
        modified = 1

        assert original != modified
        assert modified == 1

    def test_restore_max_iterations(self):
        """Should restore original max iterations after research."""
        original = 3
        current = 1

        # After research, restore
        current = original
        assert current == 3


class TestSectionLevelDetection:
    """Tests for section-level content detection."""

    def test_section_level_with_single_subsection(self):
        """Single subsection indicates section-level content."""
        section = {"subsections": [{"name": "Content"}]}
        is_section_level = len(section["subsections"]) == 1

        assert is_section_level is True

    def test_not_section_level_with_multiple_subsections(self):
        """Multiple subsections indicates not section-level."""
        section = {"subsections": [{"name": "A"}, {"name": "B"}]}
        is_section_level = len(section["subsections"]) == 1

        assert is_section_level is False


class TestPromptGeneration:
    """Tests for prompt generation."""

    def test_section_level_prompt_includes_query(self):
        """Section-level prompt should include query."""
        query = "machine learning"
        section_name = "Introduction"

        prompt = f"Create comprehensive content for the '{section_name}' section in a report about '{query}'."

        assert "machine learning" in prompt
        assert "Introduction" in prompt

    def test_subsection_level_prompt_includes_section(self):
        """Subsection-level prompt should include parent section."""
        section_name = "Analysis"
        subsection_name = "Data Analysis"
        query = "test"

        prompt = f"Create content for subsection '{subsection_name}' in a report about '{query}'. Part of section: '{section_name}'"

        assert "Data Analysis" in prompt
        assert "Analysis" in prompt

    def test_prompt_includes_purpose(self):
        """Prompt should include subsection purpose."""
        purpose = "Provide detailed analysis"
        prompt = f"This subsection's purpose: {purpose}"

        assert "Provide detailed analysis" in prompt


class TestEdgeCases:
    """Tests for edge cases."""

    def test_empty_structure(self):
        """Should handle empty structure."""
        structure = []
        sections = {}

        for section in structure:
            sections[section["name"]] = "Content"

        assert len(sections) == 0

    def test_empty_findings(self):
        """Should handle empty findings."""
        findings = {"current_knowledge": ""}

        combined_content = findings.get("current_knowledge", "")
        assert combined_content == ""

    def test_truncated_content_for_structure(self):
        """Should truncate content for structure determination."""
        content = "x" * 2000
        truncated = content[:1000]

        assert len(truncated) == 1000

    def test_section_with_pipe_in_name(self):
        """Should handle section name with pipe."""
        section_name = "Introduction | Overview"

        if "|" in section_name:
            parts = section_name.split("|", 1)
            name = parts[0].strip()
            purpose = parts[1].strip()

        assert name == "Introduction"
        assert purpose == "Overview"
