import sys
from pathlib import Path
from unittest.mock import Mock

import pytest

# Handle import paths for testing
sys.path.append(str(Path(__file__).parent.parent))
from local_deep_research.report_generator import (
    IntegratedReportGenerator,
)


@pytest.fixture
def mock_llm():
    """Create a mock LLM for testing."""
    mock = Mock()
    mock.invoke.return_value = Mock(content="Mocked LLM response")
    return mock


@pytest.fixture
def mock_search_system():
    """Create a mock search system for testing."""
    mock = Mock()
    mock.analyze_topic.return_value = {
        "findings": [{"content": "Test finding"}],
        "current_knowledge": "Test knowledge",
        "iterations": 1,
        "questions_by_iteration": {1: ["Question 1?", "Question 2?"]},
    }
    mock.all_links_of_system = [
        {"title": "Source 1", "link": "https://example.com/1"},
        {"title": "Source 2", "link": "https://example.com/2"},
    ]
    return mock


@pytest.fixture
def sample_findings():
    """Sample findings for testing."""
    return {
        "findings": [
            {"content": "Finding 1 about AI research"},
            {"content": "Finding 2 about machine learning applications"},
        ],
        "current_knowledge": "AI research has made significant progress in recent years with applications in various fields.",
        "iterations": 2,
        "questions_by_iteration": {
            1: [
                "What are the latest advances in AI?",
                "How is AI applied in healthcare?",
            ],
            2: [
                "What ethical concerns exist in AI development?",
                "What is the future of AI research?",
            ],
        },
    }


@pytest.fixture
def report_generator(mock_llm, mock_search_system, monkeypatch):
    """Create a report generator for testing."""
    monkeypatch.setattr(
        "local_deep_research.report_generator.get_llm", lambda: mock_llm
    )
    generator = IntegratedReportGenerator(search_system=mock_search_system)
    return generator


def test_init(mock_llm, mock_search_system, monkeypatch):
    """Test initialization of report generator."""
    monkeypatch.setattr(
        "local_deep_research.report_generator.get_llm", lambda: mock_llm
    )

    # Test with provided search system
    generator = IntegratedReportGenerator(search_system=mock_search_system)
    # Check that a model was set (might be wrapped)
    assert generator.model is not None
    assert generator.search_system == mock_search_system

    # Test with default search system
    mock_system_class = Mock()
    mock_system_instance = Mock()
    mock_system_class.return_value = mock_system_instance

    monkeypatch.setattr(
        "local_deep_research.report_generator.AdvancedSearchSystem",
        mock_system_class,
    )

    generator = IntegratedReportGenerator()
    # Check that a model was set (might be wrapped)
    assert generator.model is not None
    assert generator.search_system == mock_system_instance


def test_determine_report_structure(report_generator, sample_findings):
    """Test determining report structure from findings."""
    # Mock the LLM response to return a specific structure
    structured_response = """
    STRUCTURE
    1. Introduction
       - Background | Provides historical context of the research topic
       - Significance | Explains why this research matters
    2. Key Findings
       - Recent Advances | Summarizes the latest developments
       - Applications | Describes how the technology is being applied
    3. Discussion
       - Challenges | Identifies current limitations and obstacles
       - Future Directions | Explores potential future developments
    END_STRUCTURE
    """
    report_generator.model.invoke.return_value = Mock(
        content=structured_response
    )

    # Call the method
    structure = report_generator._determine_report_structure(
        sample_findings, "AI research advances"
    )

    # Verify structure was parsed correctly
    assert len(structure) == 3
    assert structure[0]["name"] == "Introduction"
    assert len(structure[0]["subsections"]) == 2
    assert structure[0]["subsections"][0]["name"] == "Background"
    assert (
        structure[0]["subsections"][0]["purpose"]
        == "Provides historical context of the research topic"
    )

    assert structure[1]["name"] == "Key Findings"
    assert structure[2]["name"] == "Discussion"

    # Verify LLM was called (can't check exact args if wrapped)
    assert report_generator.model.invoke.called or hasattr(
        report_generator.model, "invoke"
    )


def test_research_and_generate_sections(report_generator):
    """Test researching and generating sections."""
    # Define sample structure
    structure = [
        {
            "name": "Introduction",
            "subsections": [
                {"name": "Background", "purpose": "Historical context"}
            ],
        },
        {
            "name": "Findings",
            "subsections": [
                {"name": "Key Results", "purpose": "Main research outcomes"}
            ],
        },
    ]

    # Mock the search system to return specific results for each subsection
    report_generator.search_system.analyze_topic.side_effect = [
        {
            "current_knowledge": "Background section content about historical context."
        },
        {
            "current_knowledge": "Key results section content with main findings."
        },
    ]

    # Call the method
    sections = report_generator._research_and_generate_sections(
        {"current_knowledge": "Initial findings"}, structure, "Research query"
    )

    # Verify sections were generated correctly
    assert "Introduction" in sections
    assert "Findings" in sections
    assert "# Introduction" in sections["Introduction"]
    assert "Background section content" in sections["Introduction"]

    assert "# Findings" in sections["Findings"]
    assert "Key results section content" in sections["Findings"]

    # Verify search system was called the correct number of times (once per subsection)
    assert report_generator.search_system.analyze_topic.call_count == 2


def test_format_final_report(report_generator, monkeypatch):
    """Test formatting the final report."""
    # Define sample structure and sections
    structure = [
        {
            "name": "Introduction",
            "subsections": [
                {"name": "Background", "purpose": "Historical context"}
            ],
        },
        {
            "name": "Findings",
            "subsections": [
                {"name": "Key Results", "purpose": "Main research outcomes"}
            ],
        },
    ]

    sections = {
        "Introduction": "# Introduction\n\n## Background\n\nBackground content here.",
        "Findings": "# Findings\n\n## Key Results\n\nKey results content here.",
    }

    # Mock format_links_to_markdown
    def mock_format_links(all_links):
        return "1. [Source 1](https://example.com/1)\n2. [Source 2](https://example.com/2)"

    monkeypatch.setattr(
        "local_deep_research.utilities.search_utilities.format_links_to_markdown",
        mock_format_links,
    )

    # Call the method
    report = report_generator._format_final_report(
        sections, structure, "Test query"
    )

    # Verify report structure
    assert "content" in report
    assert "metadata" in report
    assert "# Table of Contents" in report["content"]
    assert "Introduction" in report["content"]
    assert "Findings" in report["content"]
    assert "Background content here" in report["content"]
    assert "Key results content here" in report["content"]
    assert "## Sources" in report["content"]

    # Verify metadata
    assert report["metadata"]["query"] == "Test query"
    assert "generated_at" in report["metadata"]
    assert "sections_researched" in report["metadata"]
    assert report["metadata"]["sections_researched"] == 2


def test_generate_report(report_generator, sample_findings, monkeypatch):
    """Test the full report generation process."""

    # Mock the component methods with Mock objects
    mock_determine_structure = Mock(
        return_value=[
            {
                "name": "Section",
                "subsections": [{"name": "Subsection", "purpose": "Purpose"}],
            }
        ]
    )

    mock_research = Mock(return_value={"Section": "Section content"})

    mock_format = Mock(
        return_value={
            "content": "Report content",
            "metadata": {"query": "Test query"},
        }
    )

    monkeypatch.setattr(
        report_generator,
        "_determine_report_structure",
        mock_determine_structure,
    )
    monkeypatch.setattr(
        report_generator, "_research_and_generate_sections", mock_research
    )
    monkeypatch.setattr(report_generator, "_format_final_report", mock_format)

    # Call generate_report
    result = report_generator.generate_report(sample_findings, "Test query")

    # Verify component methods were called with correct arguments
    mock_determine_structure.assert_called_once_with(
        sample_findings, "Test query"
    )

    # Get the expected structure result
    structure_result = mock_determine_structure.return_value
    mock_research.assert_called_once_with(
        sample_findings, structure_result, "Test query"
    )

    # Get the expected sections result
    sections_result = mock_research.return_value
    mock_format.assert_called_once_with(
        sections_result, structure_result, "Test query"
    )

    # Verify result is the formatted report
    assert result == mock_format.return_value


def test_generate_error_report(report_generator):
    """Test generating an error report."""
    error_report = report_generator._generate_error_report(
        "Test query", "Error message"
    )

    assert "Test query" in error_report
    assert "Error message" in error_report
