from __future__ import annotations

from concurrent.futures import Future
from pathlib import Path
from unittest.mock import patch

from llama_index.core import Document

from jps_rag_assistant.config import Config
from jps_rag_assistant.ingest import ingest_all


@patch("concurrent.futures.as_completed")
@patch("jps_rag_assistant.msoutlook.reader.retrieve_data")
@patch("jps_rag_assistant.jira.reader.retrieve_data")
@patch("jps_rag_assistant.confluence.reader.retrieve_data")
@patch("jps_rag_assistant.ingest.build_or_update_index")
@patch("jps_rag_assistant.ingest.ThreadPoolExecutor")
def test_ingest_all_calls_all_loaders(
    mock_executor,
    mock_build,
    mock_conf,
    mock_jira,
    mock_outlook,
    mock_as_completed,
    sample_config: Path,
):
    config = Config.load(sample_config)

    # Return real Document objects to avoid MagicMock issues
    mock_outlook.return_value = [Document(text="dummy outlook", doc_id="outlook1")]
    mock_jira.return_value = [Document(text="dummy jira", doc_id="jira1")]
    mock_conf.return_value = [Document(text="dummy conf", doc_id="conf1")]

    class DummyFuture(Future):
        def __init__(self, result):
            super().__init__()
            self.set_result(result)

    # Patch ThreadPoolExecutor to run tasks synchronously
    # class DummyFuture:
    #     def __init__(self, result):
    #         self._result = result
    #     def result(self):
    #         return self._result
    class DummyExecutor:
        def __enter__(self):
            return self

        def __exit__(self, exc_type, exc_val, exc_tb):
            pass

        def submit(self, fn):
            return DummyFuture(fn())

    mock_executor.return_value = DummyExecutor()

    # Patch as_completed to just return the futures as a list
    mock_as_completed.side_effect = lambda fs: list(fs)
    ingest_all(config)

    mock_outlook.assert_called_once_with(
        folder_names=config.outlook.folder_names,
        calendar_days=config.outlook.days,
    )
    mock_jira.assert_called_once()
    mock_conf.assert_called_once()
    mock_build.assert_called_once()
