"""Plugin manager: discovers, loads, and executes plugins."""

from __future__ import annotations

import logging
from typing import Any

from bilgeai.plugins.base import BilgePlugin, HookType

logger = logging.getLogger(__name__)


class PluginManager:
    """Discovers and manages BilgeAI plugins."""

    ENTRY_POINT_GROUP = "bilgeai.plugins"

    def __init__(self) -> None:
        self._plugins: list[BilgePlugin] = []
        self._hooks: dict[HookType, list] = {hook: [] for hook in HookType}

    @property
    def plugins(self) -> list[BilgePlugin]:
        """Return loaded plugins."""
        return list(self._plugins)

    def discover(self) -> list[BilgePlugin]:
        """Discover plugins via entry_points and load them."""
        discovered: list[BilgePlugin] = []

        try:
            from importlib.metadata import entry_points

            eps = entry_points()
            if hasattr(eps, "select"):
                plugin_eps = eps.select(group=self.ENTRY_POINT_GROUP)
            else:
                plugin_eps = eps.get(self.ENTRY_POINT_GROUP, [])

            for ep in plugin_eps:
                try:
                    plugin_cls = ep.load()
                    if isinstance(plugin_cls, type) and issubclass(plugin_cls, BilgePlugin):
                        plugin = plugin_cls()
                        discovered.append(plugin)
                        logger.info("Discovered plugin: %s v%s", plugin.name, plugin.version)
                    else:
                        logger.warning(
                            "Entry point '%s' is not a BilgePlugin subclass, skipping", ep.name
                        )
                except Exception:
                    logger.warning("Failed to load plugin '%s'", ep.name, exc_info=True)

        except Exception:
            logger.debug("Plugin discovery failed", exc_info=True)

        return discovered

    def load(self, plugin: BilgePlugin) -> None:
        """Load and activate a single plugin."""
        if any(p.name == plugin.name for p in self._plugins):
            logger.warning("Plugin '%s' already loaded, skipping", plugin.name)
            return

        plugin.activate()
        self._plugins.append(plugin)

        for hook_type, handler in plugin.get_hooks().items():
            self._hooks[hook_type].append(handler)

        logger.info("Loaded plugin: %s v%s", plugin.name, plugin.version)

    def load_all(self) -> int:
        """Discover and load all available plugins. Returns count loaded."""
        discovered = self.discover()
        for plugin in discovered:
            self.load(plugin)
        return len(discovered)

    def unload(self, plugin_name: str) -> bool:
        """Unload a plugin by name."""
        for i, plugin in enumerate(self._plugins):
            if plugin.name == plugin_name:
                # Remove hooks
                for hook_type, handler in plugin.get_hooks().items():
                    if handler in self._hooks[hook_type]:
                        self._hooks[hook_type].remove(handler)
                plugin.deactivate()
                self._plugins.pop(i)
                logger.info("Unloaded plugin: %s", plugin_name)
                return True
        return False

    def run_hook(self, hook_type: HookType, **kwargs: Any) -> list[Any]:
        """Execute all handlers for a given hook type. Returns list of results."""
        results = []
        for handler in self._hooks[hook_type]:
            try:
                result = handler(**kwargs)
                results.append(result)
            except Exception:
                logger.warning(
                    "Plugin hook %s failed",
                    hook_type.value,
                    exc_info=True,
                )
        return results

    def get_plugin(self, name: str) -> BilgePlugin | None:
        """Get a loaded plugin by name."""
        for plugin in self._plugins:
            if plugin.name == name:
                return plugin
        return None
