"""Plugin discovery and registry for custom backends.

This module enables users to add custom LLM backend implementations by placing
Python modules in ~/.cleave/backends/. Plugins are discovered at runtime and
registered automatically.

Plugin Structure:
    A valid plugin must define:
    - __plugin_name__: Unique identifier for the backend
    - __plugin_version__: Semver version string
    - __plugin_backend__: Backend class implementing Backend interface

    Optional:
    - __plugin_author__: Author name
    - __plugin_description__: Brief description

Example:
    # ~/.cleave/backends/my_backend.py
    from cleave.tui.backends.base import Backend, BackendConfig

    class MyBackend(Backend):
        # Implementation here
        ...

    __plugin_name__ = "my-backend"
    __plugin_version__ = "1.0.0"
    __plugin_author__ = "Your Name"
    __plugin_backend__ = MyBackend

Usage:
    from cleave.tui.backends.plugin import PluginRegistry

    registry = PluginRegistry()
    registry.discover_and_register()

    backend = registry.get_backend("my-backend")
"""

import importlib.util
import logging
import sys
from dataclasses import dataclass
from pathlib import Path
from typing import Any

from cleave.tui.backends import (
    Backend,
    get_backend,
    get_backend_class,
    list_backends,
    register_backend,
)

logger = logging.getLogger(__name__)


@dataclass
class PluginInfo:
    """Metadata about a discovered plugin.

    Attributes:
        name: Unique plugin identifier.
        version: Semver version string.
        author: Plugin author name.
        description: Brief description of the plugin.
        module_path: Path to the plugin module file.
        backend_class: The Backend class implementation.
        is_builtin: Whether this is a built-in backend (not a plugin).
    """

    name: str
    version: str
    author: str
    module_path: Path
    backend_class: type[Backend]
    description: str = ""
    is_builtin: bool = False


def load_plugin(plugin_path: Path) -> PluginInfo | None:
    """Load a plugin from a Python file.

    Args:
        plugin_path: Path to the plugin module (.py file).

    Returns:
        PluginInfo if plugin is valid, None otherwise.
    """
    try:
        # Load the module
        module_name = f"cleave_plugin_{plugin_path.stem}"
        spec = importlib.util.spec_from_file_location(module_name, plugin_path)

        if spec is None or spec.loader is None:
            logger.warning(f"Cannot load plugin from {plugin_path}")
            return None

        module = importlib.util.module_from_spec(spec)
        sys.modules[module_name] = module
        spec.loader.exec_module(module)

        # Extract plugin metadata
        plugin_name = getattr(module, "__plugin_name__", None)
        plugin_version = getattr(module, "__plugin_version__", "0.0.0")
        plugin_author = getattr(module, "__plugin_author__", "Unknown")
        plugin_description = getattr(module, "__plugin_description__", "")
        plugin_backend = getattr(module, "__plugin_backend__", None)

        # Validate required fields
        if plugin_name is None:
            logger.warning(f"Plugin {plugin_path} missing __plugin_name__")
            return None

        if plugin_backend is None:
            logger.warning(f"Plugin {plugin_path} missing __plugin_backend__")
            return None

        # Validate backend class implements Backend interface
        if not issubclass(plugin_backend, Backend):
            logger.warning(
                f"Plugin {plugin_path}: {plugin_backend} does not implement Backend"
            )
            return None

        return PluginInfo(
            name=plugin_name,
            version=plugin_version,
            author=plugin_author,
            description=plugin_description,
            module_path=plugin_path,
            backend_class=plugin_backend,
        )

    except Exception as e:
        logger.warning(f"Error loading plugin {plugin_path}: {e}")
        return None


def discover_plugins(plugin_dir: Path) -> list[PluginInfo]:
    """Discover all plugins in a directory.

    Args:
        plugin_dir: Directory to search for plugins.

    Returns:
        List of discovered plugins.
    """
    plugins = []

    if not plugin_dir.exists():
        logger.debug(f"Plugin directory {plugin_dir} does not exist")
        return plugins

    if not plugin_dir.is_dir():
        logger.warning(f"Plugin path {plugin_dir} is not a directory")
        return plugins

    # Find all Python files
    for plugin_file in plugin_dir.glob("*.py"):
        # Skip __init__.py and special files
        if plugin_file.name.startswith("__"):
            continue

        plugin_info = load_plugin(plugin_file)
        if plugin_info:
            plugins.append(plugin_info)
            logger.info(f"Discovered plugin: {plugin_info.name} v{plugin_info.version}")

    return plugins


class PluginDiscovery:
    """Plugin discovery system.

    Scans ~/.cleave/backends/ for custom backend plugins and provides
    information about discovered plugins.
    """

    def __init__(self, plugin_dir: Path | None = None):
        """Initialize plugin discovery.

        Args:
            plugin_dir: Custom plugin directory. Defaults to ~/.cleave/backends.
        """
        if plugin_dir is None:
            plugin_dir = Path.home() / ".cleave" / "backends"

        self.plugin_dir = plugin_dir

    def discover(self) -> list[PluginInfo]:
        """Discover all plugins in the plugin directory.

        Returns:
            List of discovered plugins.
        """
        return discover_plugins(self.plugin_dir)

    def ensure_plugin_dir(self) -> None:
        """Ensure plugin directory exists."""
        self.plugin_dir.mkdir(parents=True, exist_ok=True)


class PluginRegistry:
    """Registry for backends (built-in and plugins).

    Manages both built-in backends and custom plugins, providing a unified
    interface for backend discovery and retrieval.
    """

    def __init__(self):
        """Initialize the plugin registry."""
        self._plugins: dict[str, PluginInfo] = {}

    def register_plugin(self, plugin_info: PluginInfo) -> None:
        """Register a plugin.

        Args:
            plugin_info: Plugin metadata and backend class.
        """
        # Store plugin info
        self._plugins[plugin_info.name] = plugin_info

        # Register backend with the global registry
        register_backend(plugin_info.name, plugin_info.backend_class)

        logger.info(f"Registered plugin: {plugin_info.name}")

    def discover_and_register(
        self, plugin_dir: Path | None = None
    ) -> int:
        """Discover and register all plugins.

        Args:
            plugin_dir: Custom plugin directory. Defaults to ~/.cleave/backends.

        Returns:
            Number of plugins registered.
        """
        discovery = PluginDiscovery(plugin_dir)
        plugins = discovery.discover()

        for plugin_info in plugins:
            self.register_plugin(plugin_info)

        return len(plugins)

    def list_plugins(self) -> list[str]:
        """List all registered plugin names.

        Returns:
            List of plugin identifiers.
        """
        return list(self._plugins.keys())

    def list_backends(self) -> list[str]:
        """List all available backends (built-in + plugins).

        Returns:
            List of backend identifiers.
        """
        return list_backends()

    def get_plugin_info(self, name: str) -> PluginInfo | None:
        """Get metadata about a plugin.

        Args:
            name: Plugin identifier.

        Returns:
            Plugin info if found, None otherwise.
        """
        return self._plugins.get(name)

    def get_backend(self, name: str) -> Backend:
        """Get a backend instance by name.

        Args:
            name: Backend identifier (plugin or built-in).

        Returns:
            Backend instance.

        Raises:
            KeyError: If backend not found.
        """
        return get_backend(name)

    def get_backend_class(self, name: str) -> type[Backend]:
        """Get a backend class by name.

        Args:
            name: Backend identifier.

        Returns:
            Backend class.

        Raises:
            KeyError: If backend not found.
        """
        return get_backend_class(name)

    def is_plugin_backend(self, name: str) -> bool:
        """Check if a backend is from a plugin.

        Args:
            name: Backend identifier.

        Returns:
            True if backend is from a plugin, False if built-in.
        """
        return name in self._plugins

    def get_backend_status(self, name: str) -> dict[str, Any]:
        """Get status information about a backend.

        Args:
            name: Backend identifier.

        Returns:
            Status dictionary with keys:
            - exists: Whether backend is registered
            - is_plugin: Whether backend is from a plugin
            - plugin_info: Plugin metadata (if is_plugin)
            - is_authenticated: Whether backend is authenticated
            - auth_message: Authentication status message
        """
        status = {
            "exists": name in list_backends(),
            "is_plugin": self.is_plugin_backend(name),
            "plugin_info": None,
            "is_authenticated": False,
            "auth_message": "",
        }

        if status["is_plugin"]:
            status["plugin_info"] = self.get_plugin_info(name)

        if status["exists"]:
            try:
                backend_class = get_backend_class(name)
                is_auth, auth_msg = backend_class.check_auth()
                status["is_authenticated"] = is_auth
                status["auth_message"] = auth_msg
            except Exception as e:
                status["auth_message"] = f"Error checking auth: {e}"

        return status


# Global registry instance
_global_registry: PluginRegistry | None = None


def get_global_registry() -> PluginRegistry:
    """Get the global plugin registry.

    Returns:
        Global plugin registry instance.
    """
    global _global_registry
    if _global_registry is None:
        _global_registry = PluginRegistry()
        # Auto-discover plugins on first access
        _global_registry.discover_and_register()

    return _global_registry


def list_all_backends() -> list[str]:
    """List all available backends (built-in + plugins).

    Returns:
        List of backend identifiers.
    """
    registry = get_global_registry()
    return registry.list_backends()


def get_backend_info(name: str) -> dict[str, Any]:
    """Get information about a backend.

    Args:
        name: Backend identifier.

    Returns:
        Backend information including status and plugin metadata.
    """
    registry = get_global_registry()
    return registry.get_backend_status(name)


__all__ = [
    "PluginInfo",
    "PluginDiscovery",
    "PluginRegistry",
    "load_plugin",
    "discover_plugins",
    "get_global_registry",
    "list_all_backends",
    "get_backend_info",
]
