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.search_system import AdvancedSearchSystem  # noqa: E402


@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():
    """Create a mock search engine for testing."""
    mock = Mock()
    mock.run.return_value = [
        {
            "title": "Mocked Search Result",
            "link": "https://example.com/mocked",
            "snippet": "This is a mocked search result snippet.",
        }
    ]
    return mock


@pytest.fixture
def mock_strategy():
    """Create a mock search strategy for testing."""
    mock = Mock()
    mock.analyze_topic.return_value = {
        "findings": [{"content": "Test finding"}],
        "current_knowledge": "Test knowledge summary",
        "iterations": 1,
        "questions_by_iteration": {1: ["Question 1?", "Question 2?"]},
    }
    return mock


def test_progress_callback_forwarding(monkeypatch, mock_search, mock_llm):
    """Test that progress callbacks are properly forwarded to the strategy."""
    # Create a mock strategy class and instance
    mock_strategy_class = Mock()
    mock_strategy_instance = Mock()
    mock_strategy_class.return_value = mock_strategy_instance

    monkeypatch.setattr(
        "local_deep_research.search_system.StandardSearchStrategy",
        mock_strategy_class,
    )
    monkeypatch.setattr(
        "local_deep_research.search_system.get_llm", lambda: mock_llm
    )
    monkeypatch.setattr(
        "local_deep_research.search_system.get_search",
        lambda llm_instance=None: mock_search,
    )

    # Create the search system
    system = AdvancedSearchSystem()

    # Create a mock progress callback
    mock_callback = Mock()

    # Set the callback
    system.set_progress_callback(mock_callback)

    # Internal _progress_callback should call the user-provided callback
    system._progress_callback("Test message", 50, {"test": "metadata"})

    # Verify callback was called with correct parameters
    mock_callback.assert_called_once_with(
        "Test message", 50, {"test": "metadata"}
    )


def test_init_standard_strategy(monkeypatch):
    """Test initialization with standard strategy."""
    # Set up mock LLM and search
    mock_llm_instance = Mock()
    mock_search_instance = Mock()

    monkeypatch.setattr(
        "local_deep_research.search_system.get_llm", lambda: mock_llm_instance
    )
    monkeypatch.setattr(
        "local_deep_research.search_system.get_search",
        lambda llm_instance=None: mock_search_instance,
    )

    # Create with default strategy (now source-based)
    system = AdvancedSearchSystem()

    # Check if the correct strategy type was created (default is now source-based)
    assert "SourceBasedSearchStrategy" in system.strategy.__class__.__name__

    # Also test explicit standard strategy
    system_standard = AdvancedSearchSystem(strategy_name="standard")
    assert (
        "StandardSearchStrategy" in system_standard.strategy.__class__.__name__
    )


def test_init_iterdrag_strategy(monkeypatch):
    """Test initialization with IterDRAG strategy."""
    # Set up mock LLM and search
    mock_llm_instance = Mock()
    mock_search_instance = Mock()

    monkeypatch.setattr(
        "local_deep_research.search_system.get_llm", lambda: mock_llm_instance
    )
    monkeypatch.setattr(
        "local_deep_research.search_system.get_search",
        lambda llm_instance=None: mock_search_instance,
    )

    # Create with IterDRAG strategy
    system = AdvancedSearchSystem(strategy_name="iterdrag")

    # Check if the correct strategy type was created
    assert "IterDRAGStrategy" in system.strategy.__class__.__name__


def test_init_parallel_strategy(monkeypatch):
    """Test initialization with parallel strategy."""
    # Set up mock LLM and search
    mock_llm_instance = Mock()
    mock_search_instance = Mock()

    monkeypatch.setattr(
        "local_deep_research.search_system.get_llm", lambda: mock_llm_instance
    )
    monkeypatch.setattr(
        "local_deep_research.search_system.get_search",
        lambda llm_instance=None: mock_search_instance,
    )

    # Create with parallel strategy
    system = AdvancedSearchSystem(strategy_name="parallel")

    # Check if the correct strategy type was created
    assert "ParallelSearchStrategy" in system.strategy.__class__.__name__


def test_init_rapid_strategy(monkeypatch):
    """Test initialization with rapid strategy."""
    # Set up mock LLM and search
    mock_llm_instance = Mock()
    mock_search_instance = Mock()

    monkeypatch.setattr(
        "local_deep_research.search_system.get_llm", lambda: mock_llm_instance
    )
    monkeypatch.setattr(
        "local_deep_research.search_system.get_search",
        lambda llm_instance=None: mock_search_instance,
    )

    # Create with rapid strategy
    system = AdvancedSearchSystem(strategy_name="rapid")

    # Check if the correct strategy type was created
    assert "RapidSearchStrategy" in system.strategy.__class__.__name__


def test_init_invalid_strategy(monkeypatch):
    """Test initialization with invalid strategy (should default to standard)."""
    # Set up mock LLM and search
    mock_llm_instance = Mock()
    mock_search_instance = Mock()

    monkeypatch.setattr(
        "local_deep_research.search_system.get_llm", lambda: mock_llm_instance
    )
    monkeypatch.setattr(
        "local_deep_research.search_system.get_search",
        lambda llm_instance=None: mock_search_instance,
    )

    # Create with invalid strategy name
    system = AdvancedSearchSystem(strategy_name="invalid_strategy_name")

    # Check if it defaulted to standard strategy
    assert "StandardSearchStrategy" in system.strategy.__class__.__name__


def test_set_progress_callback(monkeypatch):
    """Test setting progress callback."""
    # Set up mock LLM and search
    mock_llm_instance = Mock()
    mock_search_instance = Mock()

    monkeypatch.setattr(
        "local_deep_research.search_system.get_llm", lambda: mock_llm_instance
    )
    monkeypatch.setattr(
        "local_deep_research.search_system.get_search",
        lambda llm_instance=None: mock_search_instance,
    )

    system = AdvancedSearchSystem()

    # Create a mock callback
    mock_callback = Mock()

    # Set the callback
    system.set_progress_callback(mock_callback)

    # Verify callback was set on the search system
    assert system.progress_callback == mock_callback

    # Verify callback was passed to the strategy
    assert system.strategy.progress_callback == mock_callback


def test_analyze_topic(monkeypatch):
    """Test analyzing a topic."""
    # Set up mock LLM and search
    mock_llm_instance = Mock()
    mock_search_instance = Mock()

    # Create a mock strategy class and instance
    mock_strategy_class = Mock()
    mock_strategy_instance = Mock()
    mock_strategy_instance.analyze_topic.return_value = {
        "findings": [{"content": "Test finding"}],
        "current_knowledge": "Test knowledge",
        "iterations": 2,
        "questions_by_iteration": {
            1: ["Question 1?", "Question 2?"],
            2: ["Follow-up 1?", "Follow-up 2?"],
        },
        "all_links_of_system": [
            "https://example.com/1",
            "https://example.com/2",
        ],
    }
    # Set the questions_by_iteration attribute on the mock strategy
    mock_strategy_instance.questions_by_iteration = {
        1: ["Question 1?", "Question 2?"],
        2: ["Follow-up 1?", "Follow-up 2?"],
    }
    # Set all_links_of_system attribute on the mock strategy
    mock_strategy_instance.all_links_of_system = [
        "https://example.com/1",
        "https://example.com/2",
    ]
    mock_strategy_class.return_value = mock_strategy_instance

    monkeypatch.setattr(
        "local_deep_research.search_system.get_llm", lambda: mock_llm_instance
    )
    monkeypatch.setattr(
        "local_deep_research.search_system.get_search",
        lambda llm_instance=None: mock_search_instance,
    )
    # Mock SourceBasedSearchStrategy which is now the default
    monkeypatch.setattr(
        "local_deep_research.search_system.SourceBasedSearchStrategy",
        mock_strategy_class,
    )
    # Mock get_db_setting for progress callback
    monkeypatch.setattr(
        "local_deep_research.search_system.get_db_setting",
        lambda key, default=None: {
            "llm.provider": "test_provider",
            "llm.model": "test_model",
            "search.tool": "test_search",
        }.get(key, default),
    )

    # Create the search system (uses source-based strategy by default)
    system = AdvancedSearchSystem()

    # Set mock all_links_of_system attribute on the strategy
    system.strategy.all_links_of_system = [
        "https://example.com/1",
        "https://example.com/2",
    ]

    # Analyze a topic
    result = system.analyze_topic("test query")

    # Verify strategy's analyze_topic was called
    mock_strategy_instance.analyze_topic.assert_called_once_with("test query")

    # Verify result contents
    assert "findings" in result
    assert "current_knowledge" in result
    assert "iterations" in result
    assert "questions_by_iteration" in result
    assert "search_system" in result

    # Verify search_system reference is correct
    assert result["search_system"] == system

    # Verify questions and links were stored on the system
    assert system.questions_by_iteration == {
        1: ["Question 1?", "Question 2?"],
        2: ["Follow-up 1?", "Follow-up 2?"],
    }
    assert system.all_links_of_system == [
        "https://example.com/1",
        "https://example.com/2",
    ]
