"""Tests for automatic RAG indexing functionality."""

import threading
import time
from unittest.mock import MagicMock, patch


class TestAutoIndexingSetting:
    """Test the auto-indexing setting."""

    def test_auto_index_setting_exists_in_defaults(self):
        """Test that the auto_index_enabled setting is defined in defaults."""
        import json
        from pathlib import Path

        defaults_path = Path(
            "src/local_deep_research/defaults/default_settings.json"
        )
        with open(defaults_path) as f:
            defaults = json.load(f)

        assert "research_library.auto_index_enabled" in defaults
        setting = defaults["research_library.auto_index_enabled"]
        assert setting["ui_element"] == "checkbox"
        assert setting["value"] is False  # Default is disabled (opt-in)
        assert setting["category"] == "research_library"


class TestTriggerAutoIndex:
    """Test the trigger_auto_index function."""

    def test_trigger_auto_index_skips_when_disabled(self):
        """Test that auto-indexing is skipped when disabled in settings."""
        from local_deep_research.research_library.routes.rag_routes import (
            trigger_auto_index,
        )

        with patch(
            "local_deep_research.database.session_context.get_user_db_session"
        ) as mock_session:
            mock_db = MagicMock()
            mock_session.return_value.__enter__.return_value = mock_db

            mock_settings = MagicMock()
            mock_settings.get_bool_setting.return_value = False

            with patch(
                "local_deep_research.research_library.routes.rag_routes.SettingsManager",
                return_value=mock_settings,
            ):
                with patch(
                    "local_deep_research.research_library.routes.rag_routes._get_auto_index_executor"
                ) as mock_get_executor:
                    trigger_auto_index(
                        document_ids=["doc1"],
                        collection_id="coll1",
                        username="testuser",
                        db_password="testpass",
                    )

                    # Executor should NOT be called when disabled
                    mock_get_executor.assert_not_called()

    def test_trigger_auto_index_submits_to_executor_when_enabled(self):
        """Test that auto-indexing submits to executor when enabled."""
        from local_deep_research.research_library.routes.rag_routes import (
            trigger_auto_index,
        )

        with patch(
            "local_deep_research.database.session_context.get_user_db_session"
        ) as mock_session:
            mock_db = MagicMock()
            mock_session.return_value.__enter__.return_value = mock_db

            mock_settings = MagicMock()
            mock_settings.get_bool_setting.return_value = True

            with patch(
                "local_deep_research.research_library.routes.rag_routes.SettingsManager",
                return_value=mock_settings,
            ):
                with patch(
                    "local_deep_research.research_library.routes.rag_routes._get_auto_index_executor"
                ) as mock_get_executor:
                    mock_executor = MagicMock()
                    mock_get_executor.return_value = mock_executor

                    trigger_auto_index(
                        document_ids=["doc1", "doc2"],
                        collection_id="coll1",
                        username="testuser",
                        db_password="testpass",
                    )

                    # Executor should be obtained and submit called
                    mock_get_executor.assert_called_once()
                    mock_executor.submit.assert_called_once()

    def test_trigger_auto_index_skips_empty_document_list(self):
        """Test that auto-indexing is skipped when no documents provided."""
        from local_deep_research.research_library.routes.rag_routes import (
            trigger_auto_index,
        )

        with patch(
            "local_deep_research.database.session_context.get_user_db_session"
        ) as mock_session:
            # Should not even try to access the database
            trigger_auto_index(
                document_ids=[],
                collection_id="coll1",
                username="testuser",
                db_password="testpass",
            )

            mock_session.assert_not_called()

    def test_trigger_auto_index_skips_on_settings_exception(self):
        """Test that auto-indexing is skipped when settings check raises exception."""
        from local_deep_research.research_library.routes.rag_routes import (
            trigger_auto_index,
        )

        with patch(
            "local_deep_research.database.session_context.get_user_db_session"
        ) as mock_session:
            # Simulate database error
            mock_session.side_effect = Exception("Database connection failed")

            with patch(
                "local_deep_research.research_library.routes.rag_routes._get_auto_index_executor"
            ) as mock_get_executor:
                # Should not raise, just skip
                trigger_auto_index(
                    document_ids=["doc1"],
                    collection_id="coll1",
                    username="testuser",
                    db_password="testpass",
                )

                # Executor should NOT be called when settings check fails
                mock_get_executor.assert_not_called()

    def test_trigger_auto_index_passes_correct_arguments(self):
        """Test that trigger_auto_index passes correct arguments to the worker."""
        from local_deep_research.research_library.routes.rag_routes import (
            trigger_auto_index,
            _auto_index_documents_worker,
        )

        with patch(
            "local_deep_research.database.session_context.get_user_db_session"
        ) as mock_session:
            mock_db = MagicMock()
            mock_session.return_value.__enter__.return_value = mock_db

            mock_settings = MagicMock()
            mock_settings.get_bool_setting.return_value = True

            with patch(
                "local_deep_research.research_library.routes.rag_routes.SettingsManager",
                return_value=mock_settings,
            ):
                with patch(
                    "local_deep_research.research_library.routes.rag_routes._get_auto_index_executor"
                ) as mock_get_executor:
                    mock_executor = MagicMock()
                    mock_get_executor.return_value = mock_executor

                    trigger_auto_index(
                        document_ids=["doc1", "doc2", "doc3"],
                        collection_id="my_collection",
                        username="alice",
                        db_password="secret123",
                    )

                    # Verify correct arguments passed to submit
                    mock_executor.submit.assert_called_once()
                    call_args = mock_executor.submit.call_args
                    assert call_args[0][0] == _auto_index_documents_worker
                    assert call_args[0][1] == ["doc1", "doc2", "doc3"]
                    assert call_args[0][2] == "my_collection"
                    assert call_args[0][3] == "alice"
                    assert call_args[0][4] == "secret123"

    def test_trigger_auto_index_with_single_document(self):
        """Test that auto-indexing works with a single document."""
        from local_deep_research.research_library.routes.rag_routes import (
            trigger_auto_index,
        )

        with patch(
            "local_deep_research.database.session_context.get_user_db_session"
        ) as mock_session:
            mock_db = MagicMock()
            mock_session.return_value.__enter__.return_value = mock_db

            mock_settings = MagicMock()
            mock_settings.get_bool_setting.return_value = True

            with patch(
                "local_deep_research.research_library.routes.rag_routes.SettingsManager",
                return_value=mock_settings,
            ):
                with patch(
                    "local_deep_research.research_library.routes.rag_routes._get_auto_index_executor"
                ) as mock_get_executor:
                    mock_executor = MagicMock()
                    mock_get_executor.return_value = mock_executor

                    trigger_auto_index(
                        document_ids=["single_doc"],
                        collection_id="coll1",
                        username="testuser",
                        db_password="testpass",
                    )

                    mock_executor.submit.assert_called_once()

    def test_trigger_auto_index_with_many_documents(self):
        """Test that auto-indexing works with many documents."""
        from local_deep_research.research_library.routes.rag_routes import (
            trigger_auto_index,
        )

        with patch(
            "local_deep_research.database.session_context.get_user_db_session"
        ) as mock_session:
            mock_db = MagicMock()
            mock_session.return_value.__enter__.return_value = mock_db

            mock_settings = MagicMock()
            mock_settings.get_bool_setting.return_value = True

            with patch(
                "local_deep_research.research_library.routes.rag_routes.SettingsManager",
                return_value=mock_settings,
            ):
                with patch(
                    "local_deep_research.research_library.routes.rag_routes._get_auto_index_executor"
                ) as mock_get_executor:
                    mock_executor = MagicMock()
                    mock_get_executor.return_value = mock_executor

                    # Submit 100 documents
                    doc_ids = [f"doc_{i}" for i in range(100)]
                    trigger_auto_index(
                        document_ids=doc_ids,
                        collection_id="coll1",
                        username="testuser",
                        db_password="testpass",
                    )

                    mock_executor.submit.assert_called_once()
                    call_args = mock_executor.submit.call_args
                    assert len(call_args[0][1]) == 100


class TestAutoIndexExecutor:
    """Test the ThreadPoolExecutor infrastructure for auto-indexing."""

    def test_get_auto_index_executor_returns_executor(self):
        """Test that _get_auto_index_executor returns a ThreadPoolExecutor."""
        from concurrent.futures import ThreadPoolExecutor

        from local_deep_research.research_library.routes.rag_routes import (
            _get_auto_index_executor,
        )

        executor = _get_auto_index_executor()
        assert isinstance(executor, ThreadPoolExecutor)

    def test_get_auto_index_executor_returns_same_instance(self):
        """Test that _get_auto_index_executor returns the same singleton instance."""
        from local_deep_research.research_library.routes.rag_routes import (
            _get_auto_index_executor,
        )

        executor1 = _get_auto_index_executor()
        executor2 = _get_auto_index_executor()
        assert executor1 is executor2

    def test_shutdown_auto_index_executor(self):
        """Test that _shutdown_auto_index_executor properly shuts down the executor."""
        import local_deep_research.research_library.routes.rag_routes as rag_module
        from local_deep_research.research_library.routes.rag_routes import (
            _get_auto_index_executor,
            _shutdown_auto_index_executor,
        )

        # Ensure executor exists
        executor = _get_auto_index_executor()
        assert executor is not None

        # Shutdown
        _shutdown_auto_index_executor()

        # Global should be None after shutdown
        assert rag_module._auto_index_executor is None

        # Getting executor again should create a new one
        new_executor = _get_auto_index_executor()
        assert new_executor is not None
        assert new_executor is not executor

    def test_executor_has_bounded_workers(self):
        """Test that the executor has bounded max_workers to prevent thread proliferation."""
        from local_deep_research.research_library.routes.rag_routes import (
            _get_auto_index_executor,
        )

        executor = _get_auto_index_executor()
        # The executor should have max_workers=4 as configured
        assert executor._max_workers == 4

    def test_executor_submits_work_successfully(self):
        """Test that work can be submitted to the executor."""

        from local_deep_research.research_library.routes.rag_routes import (
            _get_auto_index_executor,
        )

        executor = _get_auto_index_executor()
        result = []

        def worker(value):
            result.append(value)

        future = executor.submit(worker, "test_value")
        future.result(timeout=5)  # Wait for completion

        assert result == ["test_value"]

    def test_executor_limits_concurrent_tasks(self):
        """Test that the executor limits concurrent tasks to max_workers."""
        from local_deep_research.research_library.routes.rag_routes import (
            _get_auto_index_executor,
        )

        executor = _get_auto_index_executor()
        max_workers = executor._max_workers

        running_count = []
        count_lock = threading.Lock()
        barrier = threading.Event()

        def slow_worker():
            with count_lock:
                running_count.append(1)
            barrier.wait(timeout=5)  # Wait until released
            with count_lock:
                running_count.pop()

        # Submit more tasks than max_workers
        futures = [executor.submit(slow_worker) for _ in range(max_workers + 2)]

        # Give threads time to start
        time.sleep(0.2)

        # Only max_workers should be running
        with count_lock:
            concurrent_count = len(running_count)
        assert concurrent_count <= max_workers

        # Release all workers
        barrier.set()

        # Wait for all to complete
        for f in futures:
            f.result(timeout=5)

    def test_executor_thread_name_prefix(self):
        """Test that executor threads have the correct name prefix."""
        from local_deep_research.research_library.routes.rag_routes import (
            _get_auto_index_executor,
        )

        executor = _get_auto_index_executor()
        thread_name_captured = []

        def capture_thread_name():
            thread_name_captured.append(threading.current_thread().name)

        future = executor.submit(capture_thread_name)
        future.result(timeout=5)

        assert len(thread_name_captured) == 1
        assert thread_name_captured[0].startswith("auto_index_")

    def test_executor_thread_safe_initialization(self):
        """Test that executor initialization is thread-safe under concurrent access."""
        from local_deep_research.research_library.routes.rag_routes import (
            _get_auto_index_executor,
            _shutdown_auto_index_executor,
        )

        # Reset executor to test initialization
        _shutdown_auto_index_executor()

        executors = []
        errors = []
        barrier = threading.Barrier(10)

        def get_executor_concurrently():
            try:
                barrier.wait(timeout=5)  # Synchronize all threads
                executor = _get_auto_index_executor()
                executors.append(executor)
            except Exception as e:
                errors.append(e)

        threads = [
            threading.Thread(target=get_executor_concurrently)
            for _ in range(10)
        ]
        for t in threads:
            t.start()
        for t in threads:
            t.join(timeout=10)

        assert len(errors) == 0, f"Errors occurred: {errors}"
        assert len(executors) == 10
        # All should be the same instance
        assert all(e is executors[0] for e in executors)

    def test_executor_handles_worker_exceptions(self):
        """Test that worker exceptions don't break the executor."""
        from local_deep_research.research_library.routes.rag_routes import (
            _get_auto_index_executor,
        )

        executor = _get_auto_index_executor()

        def failing_worker():
            raise ValueError("Intentional test error")

        def succeeding_worker():
            return "success"

        # Submit a failing task
        future1 = executor.submit(failing_worker)

        # Submit a succeeding task after
        future2 = executor.submit(succeeding_worker)

        # First should raise
        try:
            future1.result(timeout=5)
            assert False, "Should have raised ValueError"
        except ValueError as e:
            assert "Intentional test error" in str(e)

        # Second should still succeed
        assert future2.result(timeout=5) == "success"

    def test_shutdown_is_idempotent(self):
        """Test that calling shutdown multiple times is safe."""
        import local_deep_research.research_library.routes.rag_routes as rag_module
        from local_deep_research.research_library.routes.rag_routes import (
            _get_auto_index_executor,
            _shutdown_auto_index_executor,
        )

        # Ensure executor exists
        _get_auto_index_executor()

        # Shutdown multiple times should not raise
        _shutdown_auto_index_executor()
        _shutdown_auto_index_executor()
        _shutdown_auto_index_executor()

        assert rag_module._auto_index_executor is None

    def test_shutdown_when_no_executor_exists(self):
        """Test that shutdown is safe when no executor has been created."""
        import local_deep_research.research_library.routes.rag_routes as rag_module
        from local_deep_research.research_library.routes.rag_routes import (
            _shutdown_auto_index_executor,
        )

        # Force executor to None
        rag_module._auto_index_executor = None

        # Should not raise
        _shutdown_auto_index_executor()

        assert rag_module._auto_index_executor is None

    def test_executor_queues_excess_tasks(self):
        """Test that tasks beyond max_workers are queued and eventually executed."""
        from local_deep_research.research_library.routes.rag_routes import (
            _get_auto_index_executor,
        )

        executor = _get_auto_index_executor()
        max_workers = executor._max_workers
        total_tasks = max_workers * 3  # Submit 3x more tasks than workers

        results = []
        results_lock = threading.Lock()

        def worker(task_id):
            time.sleep(0.05)  # Small delay
            with results_lock:
                results.append(task_id)
            return task_id

        # Submit many tasks
        futures = [executor.submit(worker, i) for i in range(total_tasks)]

        # Wait for all to complete
        for f in futures:
            f.result(timeout=30)

        # All tasks should have been executed
        assert len(results) == total_tasks
        assert set(results) == set(range(total_tasks))

    def test_executor_preserves_task_order_within_worker(self):
        """Test that a single worker processes its tasks in order."""
        from local_deep_research.research_library.routes.rag_routes import (
            _get_auto_index_executor,
        )

        executor = _get_auto_index_executor()
        results = []

        def worker(value):
            results.append(value)
            return value

        # Submit tasks sequentially and wait for each
        for i in range(5):
            future = executor.submit(worker, i)
            future.result(timeout=5)

        # Results should be in order since we waited for each
        assert results == [0, 1, 2, 3, 4]


class TestAutoIndexDocumentsWorker:
    """Test the _auto_index_documents_worker function."""

    def test_worker_indexes_documents(self):
        """Test that the worker indexes documents via RAG service."""
        from local_deep_research.research_library.routes.rag_routes import (
            _auto_index_documents_worker,
        )

        with patch(
            "local_deep_research.research_library.routes.rag_routes._get_rag_service_for_thread"
        ) as mock_get_service:
            mock_rag_service = MagicMock()
            mock_rag_service.index_document.return_value = {"status": "success"}
            # Configure context manager behavior
            mock_get_service.return_value.__enter__.return_value = (
                mock_rag_service
            )
            mock_get_service.return_value.__exit__.return_value = None

            _auto_index_documents_worker(
                document_ids=["doc1", "doc2", "doc3"],
                collection_id="coll1",
                username="testuser",
                db_password="testpass",
            )

            # Should have called index_document for each document
            assert mock_rag_service.index_document.call_count == 3
            mock_rag_service.index_document.assert_any_call(
                "doc1", "coll1", force_reindex=False
            )
            mock_rag_service.index_document.assert_any_call(
                "doc2", "coll1", force_reindex=False
            )
            mock_rag_service.index_document.assert_any_call(
                "doc3", "coll1", force_reindex=False
            )

    def test_worker_handles_skipped_documents(self):
        """Test that the worker handles already indexed documents."""
        from local_deep_research.research_library.routes.rag_routes import (
            _auto_index_documents_worker,
        )

        with patch(
            "local_deep_research.research_library.routes.rag_routes._get_rag_service_for_thread"
        ) as mock_get_service:
            mock_rag_service = MagicMock()
            # First succeeds, second skipped, third succeeds
            mock_rag_service.index_document.side_effect = [
                {"status": "success"},
                {"status": "skipped"},
                {"status": "success"},
            ]
            # Configure context manager behavior
            mock_get_service.return_value.__enter__.return_value = (
                mock_rag_service
            )
            mock_get_service.return_value.__exit__.return_value = None

            # Should not raise
            _auto_index_documents_worker(
                document_ids=["doc1", "doc2", "doc3"],
                collection_id="coll1",
                username="testuser",
                db_password="testpass",
            )

            # All documents should have been attempted
            assert mock_rag_service.index_document.call_count == 3

    def test_worker_handles_indexing_exception(self):
        """Test that the worker handles exceptions during indexing."""
        from local_deep_research.research_library.routes.rag_routes import (
            _auto_index_documents_worker,
        )

        with patch(
            "local_deep_research.research_library.routes.rag_routes._get_rag_service_for_thread"
        ) as mock_get_service:
            mock_rag_service = MagicMock()
            mock_rag_service.index_document.side_effect = Exception(
                "Index failed"
            )
            # Configure context manager behavior
            mock_get_service.return_value.__enter__.return_value = (
                mock_rag_service
            )
            mock_get_service.return_value.__exit__.return_value = None

            # Should not raise, even with exception
            _auto_index_documents_worker(
                document_ids=["doc1"],
                collection_id="coll1",
                username="testuser",
                db_password="testpass",
            )

    def test_worker_continues_after_exception(self):
        """Test that the worker continues indexing after an exception."""
        from local_deep_research.research_library.routes.rag_routes import (
            _auto_index_documents_worker,
        )

        with patch(
            "local_deep_research.research_library.routes.rag_routes._get_rag_service_for_thread"
        ) as mock_get_service:
            mock_rag_service = MagicMock()
            # First succeeds, second raises, third succeeds
            mock_rag_service.index_document.side_effect = [
                {"status": "success"},
                Exception("Index failed"),
                {"status": "success"},
            ]
            # Configure context manager behavior
            mock_get_service.return_value.__enter__.return_value = (
                mock_rag_service
            )
            mock_get_service.return_value.__exit__.return_value = None

            # Should not raise
            _auto_index_documents_worker(
                document_ids=["doc1", "doc2", "doc3"],
                collection_id="coll1",
                username="testuser",
                db_password="testpass",
            )

            # All documents should have been attempted
            assert mock_rag_service.index_document.call_count == 3

    def test_worker_creates_rag_service_with_correct_params(self):
        """Test that the worker creates RAG service with correct parameters."""
        from local_deep_research.research_library.routes.rag_routes import (
            _auto_index_documents_worker,
        )

        with patch(
            "local_deep_research.research_library.routes.rag_routes._get_rag_service_for_thread"
        ) as mock_get_service:
            mock_rag_service = MagicMock()
            mock_rag_service.index_document.return_value = {"status": "success"}
            # Configure context manager behavior
            mock_get_service.return_value.__enter__.return_value = (
                mock_rag_service
            )
            mock_get_service.return_value.__exit__.return_value = None

            _auto_index_documents_worker(
                document_ids=["doc1"],
                collection_id="my_collection",
                username="alice",
                db_password="secret",
            )

            mock_get_service.assert_called_once_with(
                "my_collection", "alice", "secret"
            )

    def test_worker_with_empty_document_list(self):
        """Test that the worker handles empty document list."""
        from local_deep_research.research_library.routes.rag_routes import (
            _auto_index_documents_worker,
        )

        with patch(
            "local_deep_research.research_library.routes.rag_routes._get_rag_service_for_thread"
        ) as mock_get_service:
            mock_rag_service = MagicMock()
            # Configure context manager behavior
            mock_get_service.return_value.__enter__.return_value = (
                mock_rag_service
            )
            mock_get_service.return_value.__exit__.return_value = None

            # Should not raise with empty list
            _auto_index_documents_worker(
                document_ids=[],
                collection_id="coll1",
                username="testuser",
                db_password="testpass",
            )

            # index_document should not be called
            mock_rag_service.index_document.assert_not_called()

    def test_worker_handles_rag_service_creation_failure(self):
        """Test that the worker handles RAG service creation failure."""
        from local_deep_research.research_library.routes.rag_routes import (
            _auto_index_documents_worker,
        )

        with patch(
            "local_deep_research.research_library.routes.rag_routes._get_rag_service_for_thread"
        ) as mock_get_service:
            mock_get_service.side_effect = Exception(
                "Failed to create RAG service"
            )

            # Should not raise, even with exception
            _auto_index_documents_worker(
                document_ids=["doc1"],
                collection_id="coll1",
                username="testuser",
                db_password="testpass",
            )


class TestAutoIndexIntegration:
    """Integration tests for the auto-indexing system."""

    def test_full_flow_with_real_executor(self):
        """Test the full flow using the real executor."""
        from local_deep_research.research_library.routes.rag_routes import (
            trigger_auto_index,
            _get_auto_index_executor,
        )

        # Ensure executor is initialized
        _get_auto_index_executor()
        task_executed = threading.Event()

        mock_rag_service = MagicMock()

        def index_and_signal(doc_id, collection_id, force_reindex=False):
            task_executed.set()
            return {"status": "success"}

        mock_rag_service.index_document.side_effect = index_and_signal

        with patch(
            "local_deep_research.database.session_context.get_user_db_session"
        ) as mock_session:
            mock_db = MagicMock()
            mock_session.return_value.__enter__.return_value = mock_db

            mock_settings = MagicMock()
            mock_settings.get_bool_setting.return_value = True

            with patch(
                "local_deep_research.research_library.routes.rag_routes.SettingsManager",
                return_value=mock_settings,
            ):
                with patch(
                    "local_deep_research.research_library.routes.rag_routes._get_rag_service_for_thread"
                ) as mock_get_service:
                    # Configure context manager behavior
                    mock_get_service.return_value.__enter__.return_value = (
                        mock_rag_service
                    )
                    mock_get_service.return_value.__exit__.return_value = None

                    trigger_auto_index(
                        document_ids=["doc1"],
                        collection_id="coll1",
                        username="testuser",
                        db_password="testpass",
                    )

                    # Wait for task to execute
                    assert task_executed.wait(timeout=5), (
                        "Task was not executed"
                    )

    def test_multiple_concurrent_triggers(self):
        """Test multiple concurrent trigger_auto_index calls."""
        from local_deep_research.research_library.routes.rag_routes import (
            trigger_auto_index,
            _get_auto_index_executor,
        )

        # Ensure executor is initialized
        _get_auto_index_executor()
        indexed_docs = []
        docs_lock = threading.Lock()
        all_done = threading.Event()
        expected_count = 5

        mock_rag_service = MagicMock()

        def track_indexing(doc_id, collection_id, force_reindex=False):
            with docs_lock:
                indexed_docs.append(doc_id)
                if len(indexed_docs) >= expected_count:
                    all_done.set()
            return {"status": "success"}

        mock_rag_service.index_document.side_effect = track_indexing

        with patch(
            "local_deep_research.database.session_context.get_user_db_session"
        ) as mock_session:
            mock_db = MagicMock()
            mock_session.return_value.__enter__.return_value = mock_db

            mock_settings = MagicMock()
            mock_settings.get_bool_setting.return_value = True

            with patch(
                "local_deep_research.research_library.routes.rag_routes.SettingsManager",
                return_value=mock_settings,
            ):
                with patch(
                    "local_deep_research.research_library.routes.rag_routes._get_rag_service_for_thread"
                ) as mock_get_service:
                    # Configure context manager behavior
                    mock_get_service.return_value.__enter__.return_value = (
                        mock_rag_service
                    )
                    mock_get_service.return_value.__exit__.return_value = None

                    # Trigger multiple times concurrently
                    for i in range(expected_count):
                        trigger_auto_index(
                            document_ids=[f"doc_{i}"],
                            collection_id="coll1",
                            username="testuser",
                            db_password="testpass",
                        )

                    # Wait for all to complete
                    assert all_done.wait(timeout=10), "Not all tasks completed"

                    with docs_lock:
                        assert len(indexed_docs) == expected_count

    def test_executor_reused_across_triggers(self):
        """Test that the same executor is reused across multiple triggers."""
        from local_deep_research.research_library.routes.rag_routes import (
            trigger_auto_index,
        )

        with patch(
            "local_deep_research.database.session_context.get_user_db_session"
        ) as mock_session:
            mock_db = MagicMock()
            mock_session.return_value.__enter__.return_value = mock_db

            mock_settings = MagicMock()
            mock_settings.get_bool_setting.return_value = True

            with patch(
                "local_deep_research.research_library.routes.rag_routes.SettingsManager",
                return_value=mock_settings,
            ):
                with patch(
                    "local_deep_research.research_library.routes.rag_routes._get_auto_index_executor"
                ) as mock_get_executor:
                    mock_executor = MagicMock()
                    mock_get_executor.return_value = mock_executor

                    # Trigger multiple times
                    for i in range(3):
                        trigger_auto_index(
                            document_ids=[f"doc_{i}"],
                            collection_id="coll1",
                            username="testuser",
                            db_password="testpass",
                        )

                    # Same executor should be fetched each time
                    assert mock_get_executor.call_count == 3
                    assert mock_executor.submit.call_count == 3
