# #region Test.SmokePlugins [C:3] [TYPE Module] [SEMANTICS test,plugin,smoke,loader,integration]
# @BRIEF Smoke tests verifying plugin discovery, instantiation, and TaskManager integration.
# @RELATION BINDS_TO -> [PluginLoader]
# @RELATION BINDS_TO -> [TaskManager]
# @TEST_EDGE: missing_plugin -> Missing expected plugins detected
# @TEST_EDGE: plugin_import_error -> Plugin load fails gracefully
# @TEST_EDGE: task_manager_init_failure -> TaskManager initialization error handled
# @RATIONALE Smoke tests exercise the full plugin loading chain without external deps.
import os
from pathlib import Path
import pytest
import sys
from unittest.mock import MagicMock, patch

import tests.conftest  # noqa: F401 — ensure conftest runs first

sys.path.insert(0, str(Path(__file__).parent.parent))

# ── Save original module before it gets mocked ──
_ORIG_DATABASE_MODULE = sys.modules.get('src.core.database')


@pytest.fixture(autouse=True)
def isolate_database():
    """Isolate this test module from the real database.

    Saves the real src.core.database, replaces it with a MagicMock,
    and restores it after the test completes. This prevents PluginLoader
    imports from triggering real database initialization.
    """
    # Remove real module if loaded
    sys.modules.pop('src.core.database', None)
    # Insert mock
    mock_db = MagicMock()
    sys.modules['src.core.database'] = mock_db
    yield
    # Restore real module
    sys.modules.pop('src.core.database', None)
    if _ORIG_DATABASE_MODULE is not None:
        sys.modules['src.core.database'] = _ORIG_DATABASE_MODULE


class TestPluginSmoke:
    """Smoke tests for plugin loading and initialization."""

    def test_plugins_load_successfully(self):
        """
        Verify that all standard plugins can be discovered and instantiated
        by the PluginLoader without throwing errors (e.g., missing imports,
        syntax errors, missing class declarations).
        """
        from src.core.plugin_loader import PluginLoader

        plugin_dir = os.path.join(str(Path(__file__).parent.parent), "src", "plugins")

        # This will discover and instantiate plugins
        loader = PluginLoader(plugin_dir)

        plugins = loader.get_all_plugin_configs()
        plugin_ids = {p.id for p in plugins}

        # We expect at least the migration and git plugins to be present
        expected_plugins = {"superset-migration", "git-integration"}

        missing_plugins = expected_plugins - plugin_ids
        assert not missing_plugins, f"Missing expected plugins: {missing_plugins}"

    @pytest.mark.anyio
    async def test_task_manager_initializes_with_plugins(self):
        """
        Verify that the TaskManager can initialize with the real PluginLoader.
        """
        from src.core.plugin_loader import PluginLoader
        from src.core.task_manager.manager import TaskManager

        plugin_dir = os.path.join(str(Path(__file__).parent.parent), "src", "plugins")
        loader = PluginLoader(plugin_dir)

        # Initialize TaskManager with real loader
        with patch("src.core.task_manager.manager.TaskPersistenceService") as MockPersistence, \
             patch("src.core.task_manager.manager.TaskLogPersistenceService"):

            MockPersistence.return_value.load_tasks.return_value = []

            with patch("src.dependencies.config_manager"):
                manager = TaskManager(loader)

            # Stop the flusher thread to prevent hanging
            if hasattr(manager, '_flusher_stop_event'):
                manager._flusher_stop_event.set()
            elif hasattr(manager, 'event_bus') and hasattr(manager.event_bus, '_flusher_stop_event'):
                manager.event_bus._flusher_stop_event.set()
            if hasattr(manager, '_flusher_thread'):
                manager._flusher_thread.join(timeout=2)

            assert manager is not None
# #endregion Test.SmokePlugins
