"""Plugin Manager

Provides a simple interface for consumers to access TTS engines via the
new Plugin Architecture. Discovers, loads, and manages plugins from the
plugins directory.

Usage:
    from abogen.tts_plugin.plugin_manager import get_plugin_manager

    manager = get_plugin_manager()
    engine = manager.create_engine("kokoro", lang_code="a", device="cpu")
    session = engine.create_session()
    try:
        result = session.synthesize("Hello world")
    finally:
        session.dispose()
"""

from typing import Any, Dict, List, Optional, Type

from abogen.tts_plugin.engine import Engine, EngineSession
from abogen.tts_plugin.manifest import PluginManifest
from abogen.tts_plugin.types import AudioFormat


class PluginManager:
    """Manages TTS plugins and provides a simple interface for consumers."""

    def __init__(self) -> None:
        self._plugins: Dict[str, dict] = {}
        self._engines: Dict[str, Engine] = {}
        self._loaded = False

    def discover(self, plugins_dir: str = "plugins") -> None:
        """Discover and load all plugins from the given directory."""
        import os
        from pathlib import Path
        from abogen.tts_plugin.loader import load_plugin_from_dir

        self._plugins.clear()
        self._engines.clear()

        plugins_path = Path(plugins_dir)
        if not plugins_path.exists():
            self._loaded = True
            return

        for entry in plugins_path.iterdir():
            if entry.is_dir() and (entry / "__init__.py").exists():
                try:
                    result = load_plugin_from_dir(entry)
                    if result.success and result.manifest is not None:
                        self._plugins[result.manifest.id] = {
                            "manifest": result.manifest,
                            "create_engine": result.create_engine,
                            "module": result.module,
                        }
                except Exception as e:
                    # Log error but continue with other plugins
                    print(f"Warning: Failed to load plugin from {entry}: {e}")

        self._loaded = True

    def _ensure_loaded(self) -> None:
        """Ensure plugins have been discovered."""
        if not self._loaded:
            self.discover()

    def list_plugins(self) -> List[PluginManifest]:
        """Return manifests for all loaded plugins."""
        self._ensure_loaded()
        return [info["manifest"] for info in self._plugins.values()]

    def get_plugin(self, plugin_id: str) -> Optional[dict]:
        """Get plugin info by ID."""
        self._ensure_loaded()
        return self._plugins.get(plugin_id)

    def has_plugin(self, plugin_id: str) -> bool:
        """Check if a plugin is loaded."""
        self._ensure_loaded()
        return plugin_id in self._plugins

    def create_engine(self, plugin_id: str, **kwargs: Any) -> Engine:
        """Create an engine instance for the given plugin.

        Args:
            plugin_id: The plugin identifier (e.g., "kokoro")
            **kwargs: Arguments passed to the engine constructor

        Returns:
            An Engine instance

        Raises:
            KeyError: If plugin_id is not found
            Exception: If engine creation fails
        """
        self._ensure_loaded()

        if plugin_id not in self._plugins:
            raise KeyError(f"Plugin not found: {plugin_id}")

        plugin_info = self._plugins[plugin_id]
        create_engine_func = plugin_info["create_engine"]

        # Create engine using the plugin's factory
        engine = create_engine_func(**kwargs)
        return engine

    def get_or_create_engine(self, plugin_id: str, **kwargs: Any) -> Engine:
        """Get an existing engine or create a new one.

        Engines are cached by plugin_id. If you need multiple instances
        with different parameters, use create_engine() directly.
        """
        self._ensure_loaded()

        cache_key = plugin_id
        if cache_key in self._engines:
            return self._engines[cache_key]

        engine = self.create_engine(plugin_id, **kwargs)
        self._engines[cache_key] = engine
        return engine

    def dispose_all(self) -> None:
        """Dispose all cached engines."""
        for engine in self._engines.values():
            try:
                engine.dispose()
            except Exception:
                pass  # dispose() should never raise
        self._engines.clear()


# Global singleton
_manager: Optional[PluginManager] = None


def get_plugin_manager() -> PluginManager:
    """Get the global PluginManager instance."""
    global _manager
    if _manager is None:
        _manager = PluginManager()
    return _manager


def reset_plugin_manager() -> None:
    """Reset the global PluginManager (for testing)."""
    global _manager
    if _manager is not None:
        _manager.dispose_all()
    _manager = None
