"""
AdapterRegistry - Centralized adapter registration and management.

Provides:
- Factory-based adapter registration
- Dynamic adapter instantiation
- Lifecycle management
- Event hooks for adapter changes
"""

import logging
from dataclasses import dataclass, field
from datetime import datetime
from enum import Enum
from typing import Any, Callable, Optional, TypeVar

from ..adapters.base import BaseAdapter

logger = logging.getLogger(__name__)

T = TypeVar("T", bound=BaseAdapter)


class PluginStatus(Enum):
    """Status of a registered plugin."""

    REGISTERED = "registered"  # Factory registered but not instantiated
    ACTIVE = "active"  # Adapter instantiated and running
    DISABLED = "disabled"  # Temporarily disabled
    FAILED = "failed"  # Failed to initialize
    UNLOADED = "unloaded"  # Removed from registry


@dataclass
class PluginMetadata:
    """Metadata about a registered plugin."""

    name: str
    version: str = "1.0.0"
    description: str = ""
    author: str = ""
    adapter_type: str = ""  # "cli" or "api"
    capabilities: list[str] = field(default_factory=list)
    config_schema: Optional[dict[str, Any]] = None
    status: PluginStatus = PluginStatus.REGISTERED
    registered_at: datetime = field(default_factory=datetime.now)
    last_error: Optional[str] = None

    def to_dict(self) -> dict[str, Any]:
        """Convert to dictionary."""
        return {
            "name": self.name,
            "version": self.version,
            "description": self.description,
            "author": self.author,
            "adapter_type": self.adapter_type,
            "capabilities": self.capabilities,
            "status": self.status.value,
            "registered_at": self.registered_at.isoformat(),
            "last_error": self.last_error,
        }


# Type for adapter factory functions
AdapterFactory = Callable[..., BaseAdapter]


class AdapterRegistry:
    """
    Centralized registry for adapter factories and instances.

    Provides dynamic registration and lookup of adapters by name
    or capability. Supports factory-based lazy instantiation.

    Example:
        registry = AdapterRegistry()

        # Register a factory
        registry.register_factory(
            "claude-code",
            create_claude_code_adapter,
            metadata=PluginMetadata(
                name="claude-code",
                version="1.0",
                adapter_type="cli",
                capabilities=["code_edit", "file_read", "terminal"],
            ),
        )

        # Create an adapter instance
        adapter = registry.create_adapter("claude-code", workspace_path="/path")

        # Or register an existing instance
        registry.register_adapter("my-agent", existing_adapter)
    """

    def __init__(self) -> None:
        """Initialize the registry."""
        self._factories: dict[str, AdapterFactory] = {}
        self._metadata: dict[str, PluginMetadata] = {}
        self._adapters: dict[str, BaseAdapter] = {}
        self._default_configs: dict[str, dict[str, Any]] = {}

        # Event callbacks
        self._on_register: list[Callable[[str, PluginMetadata], None]] = []
        self._on_unregister: list[Callable[[str], None]] = []
        self._on_create: list[Callable[[str, BaseAdapter], None]] = []

    def register_factory(
        self,
        name: str,
        factory: AdapterFactory,
        metadata: Optional[PluginMetadata] = None,
        default_config: Optional[dict[str, Any]] = None,
        replace: bool = False,
    ) -> None:
        """
        Register an adapter factory.

        Args:
            name: Unique name for this adapter type
            factory: Callable that creates adapter instances
            metadata: Plugin metadata
            default_config: Default configuration for factory calls
            replace: Whether to replace existing factory

        Raises:
            ValueError: If name already registered and replace=False
        """
        if name in self._factories and not replace:
            raise ValueError(f"Adapter factory '{name}' already registered")

        self._factories[name] = factory
        self._metadata[name] = metadata or PluginMetadata(name=name)
        if default_config:
            self._default_configs[name] = default_config

        logger.info(f"Registered adapter factory: {name}")

        # Notify listeners
        for callback in self._on_register:
            try:
                callback(name, self._metadata[name])
            except Exception as e:
                logger.warning(f"Error in register callback: {e}")

    def register_adapter(
        self,
        agent_id: str,
        adapter: BaseAdapter,
        metadata: Optional[PluginMetadata] = None,
    ) -> None:
        """
        Register an existing adapter instance.

        Args:
            agent_id: Unique identifier for this agent
            adapter: The adapter instance
            metadata: Optional metadata
        """
        self._adapters[agent_id] = adapter

        if metadata:
            metadata.status = PluginStatus.ACTIVE
            self._metadata[agent_id] = metadata
        elif agent_id not in self._metadata:
            self._metadata[agent_id] = PluginMetadata(
                name=agent_id,
                status=PluginStatus.ACTIVE,
            )
        else:
            self._metadata[agent_id].status = PluginStatus.ACTIVE

        logger.info(f"Registered adapter instance: {agent_id}")

        # Notify listeners
        for callback in self._on_create:
            try:
                callback(agent_id, adapter)
            except Exception as e:
                logger.warning(f"Error in create callback: {e}")

    def create_adapter(
        self,
        name: str,
        agent_id: Optional[str] = None,
        **kwargs: Any,
    ) -> BaseAdapter:
        """
        Create an adapter instance from a registered factory.

        Args:
            name: Name of the registered factory
            agent_id: Optional custom agent ID (defaults to name)
            **kwargs: Configuration passed to factory

        Returns:
            The created adapter instance

        Raises:
            KeyError: If factory not registered
            RuntimeError: If factory fails
        """
        if name not in self._factories:
            raise KeyError(f"No factory registered for '{name}'")

        factory = self._factories[name]
        agent_id = agent_id or name

        # Merge default config with provided kwargs
        config = {**self._default_configs.get(name, {}), **kwargs}

        # Ensure agent_id is in config
        config["agent_id"] = agent_id

        try:
            adapter = factory(**config)
            self._adapters[agent_id] = adapter

            # Update metadata
            if name in self._metadata:
                self._metadata[agent_id] = PluginMetadata(
                    name=name,
                    version=self._metadata[name].version,
                    description=self._metadata[name].description,
                    adapter_type=self._metadata[name].adapter_type,
                    capabilities=self._metadata[name].capabilities.copy(),
                    status=PluginStatus.ACTIVE,
                )

            logger.info(f"Created adapter instance: {agent_id} from factory {name}")

            # Notify listeners
            for callback in self._on_create:
                try:
                    callback(agent_id, adapter)
                except Exception as e:
                    logger.warning(f"Error in create callback: {e}")

            return adapter

        except Exception as e:
            if name in self._metadata:
                self._metadata[name].status = PluginStatus.FAILED
                self._metadata[name].last_error = str(e)
            raise RuntimeError(f"Failed to create adapter '{name}': {e}") from e

    def get_adapter(self, agent_id: str) -> Optional[BaseAdapter]:
        """Get a registered adapter by ID."""
        return self._adapters.get(agent_id)

    def get_adapters(self) -> dict[str, BaseAdapter]:
        """Get all registered adapters."""
        return dict(self._adapters)

    def get_factory(self, name: str) -> Optional[AdapterFactory]:
        """Get a registered factory by name."""
        return self._factories.get(name)

    def get_metadata(self, name: str) -> Optional[PluginMetadata]:
        """Get metadata for a registered plugin."""
        return self._metadata.get(name)

    def list_factories(self) -> list[str]:
        """List all registered factory names."""
        return list(self._factories.keys())

    def list_adapters(self) -> list[str]:
        """List all active adapter IDs."""
        return list(self._adapters.keys())

    def has_factory(self, name: str) -> bool:
        """Check if a factory is registered."""
        return name in self._factories

    def has_adapter(self, agent_id: str) -> bool:
        """Check if an adapter is registered."""
        return agent_id in self._adapters

    def unregister_factory(self, name: str) -> bool:
        """
        Unregister an adapter factory.

        Returns True if factory was registered.
        """
        if name not in self._factories:
            return False

        del self._factories[name]
        if name in self._default_configs:
            del self._default_configs[name]

        # Update metadata
        if name in self._metadata:
            self._metadata[name].status = PluginStatus.UNLOADED

        logger.info(f"Unregistered factory: {name}")

        # Notify listeners
        for callback in self._on_unregister:
            try:
                callback(name)
            except Exception as e:
                logger.warning(f"Error in unregister callback: {e}")

        return True

    def unregister_adapter(self, agent_id: str) -> bool:
        """
        Unregister an adapter instance.

        Returns True if adapter was registered.
        """
        if agent_id not in self._adapters:
            return False

        del self._adapters[agent_id]

        # Update metadata
        if agent_id in self._metadata:
            self._metadata[agent_id].status = PluginStatus.UNLOADED

        logger.info(f"Unregistered adapter: {agent_id}")

        # Notify listeners
        for callback in self._on_unregister:
            try:
                callback(agent_id)
            except Exception as e:
                logger.warning(f"Error in unregister callback: {e}")

        return True

    def disable_adapter(self, agent_id: str) -> bool:
        """
        Disable an adapter (keeps in registry but marks as disabled).

        Returns True if adapter was disabled.
        """
        if agent_id not in self._metadata:
            return False

        self._metadata[agent_id].status = PluginStatus.DISABLED
        logger.info(f"Disabled adapter: {agent_id}")
        return True

    def enable_adapter(self, agent_id: str) -> bool:
        """
        Re-enable a disabled adapter.

        Returns True if adapter was enabled.
        """
        if agent_id not in self._metadata:
            return False

        if agent_id in self._adapters:
            self._metadata[agent_id].status = PluginStatus.ACTIVE
        else:
            self._metadata[agent_id].status = PluginStatus.REGISTERED

        logger.info(f"Enabled adapter: {agent_id}")
        return True

    def get_by_capability(self, capability: str) -> list[str]:
        """
        Get adapter names that have a specific capability.

        Args:
            capability: The capability to search for

        Returns:
            List of adapter names with that capability
        """
        results = []
        for name, meta in self._metadata.items():
            if capability in meta.capabilities:
                results.append(name)
        return results

    def get_by_type(self, adapter_type: str) -> list[str]:
        """
        Get adapter names of a specific type.

        Args:
            adapter_type: "cli" or "api"

        Returns:
            List of adapter names of that type
        """
        results = []
        for name, meta in self._metadata.items():
            if meta.adapter_type == adapter_type:
                results.append(name)
        return results

    def get_active_adapters(self) -> list[str]:
        """Get names of all active adapters."""
        return [
            name for name, meta in self._metadata.items()
            if meta.status == PluginStatus.ACTIVE
        ]

    def get_status_summary(self) -> dict[str, Any]:
        """Get a summary of registry status."""
        status_counts = {status.value: 0 for status in PluginStatus}
        for meta in self._metadata.values():
            status_counts[meta.status.value] += 1

        return {
            "total_factories": len(self._factories),
            "total_adapters": len(self._adapters),
            "status_counts": status_counts,
            "factories": self.list_factories(),
            "active_adapters": self.get_active_adapters(),
        }

    # Event hooks
    def on_register(self, callback: Callable[[str, PluginMetadata], None]) -> None:
        """Register callback for adapter registration events."""
        self._on_register.append(callback)

    def on_unregister(self, callback: Callable[[str], None]) -> None:
        """Register callback for adapter unregistration events."""
        self._on_unregister.append(callback)

    def on_create(self, callback: Callable[[str, BaseAdapter], None]) -> None:
        """Register callback for adapter creation events."""
        self._on_create.append(callback)

    def clear_callbacks(self) -> None:
        """Clear all event callbacks."""
        self._on_register.clear()
        self._on_unregister.clear()
        self._on_create.clear()
