"""Command registry for MakePython."""

from __future__ import annotations

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

from ..interfaces.command import Command, CommandType

if TYPE_CHECKING:
    from ..interfaces.command import CommandResult
    from .context import ExecutionContext


class CommandRegistry:
    """Central registry for all MakePython commands."""

    def __init__(self) -> None:
        """Initialize the command registry."""
        self._commands: Dict[str, Type[Command]] = {}
        self._aliases: Dict[str, str] = {}  # alias -> command_name mapping
        self._command_types: Dict[CommandType, List[str]] = {}

    def register(self, command_class: Type[Command]) -> None:
        """Register a command class.

        Args:
            command_class: The command class to register
        """
        # Validate command class
        if not hasattr(command_class, "name") or not hasattr(command_class, "alias"):
            raise ValueError(f"Command class {command_class.__name__} must have 'name' and 'alias' attributes")

        name = command_class.name
        alias = command_class.alias

        # Check for name conflicts
        if name in self._commands:
            raise ValueError(f"Command name '{name}' is already registered")

        # Check for alias conflicts
        if alias in self._aliases:
            raise ValueError(f"Command alias '{alias}' is already registered")

        # Register the command
        self._commands[name] = command_class
        self._aliases[alias] = name

        # Register by type
        cmd_type = getattr(command_class, "command_type", CommandType.UTIL)
        if cmd_type not in self._command_types:
            self._command_types[cmd_type] = []
        self._command_types[cmd_type].append(name)

    def get_command_class(self, name_or_alias: str) -> Optional[Type[Command]]:
        """Get a command class by name or alias.

        Args:
            name_or_alias: Command name or alias

        Returns
        -------
            Command class or None if not found
        """
        # First check if it's a direct name
        if name_or_alias in self._commands:
            return self._commands[name_or_alias]

        # Then check if it's an alias
        if name_or_alias in self._aliases:
            actual_name = self._aliases[name_or_alias]
            return self._commands.get(actual_name)

        return None

    def get_command_instance(self, name_or_alias: str) -> Optional[Command]:
        """Get a command instance by name or alias.

        Args:
            name_or_alias: Command name or alias

        Returns
        -------
            Command instance or None if not found
        """
        command_class = self.get_command_class(name_or_alias)
        if command_class:
            return command_class()
        return None

    def execute_command(self, name_or_alias: str, context: ExecutionContext) -> CommandResult:
        """Execute a command by name or alias.

        Args:
            name_or_alias: Command name or alias
            context: Execution context

        Returns
        -------
            CommandResult with execution status
        """
        command = self.get_command_instance(name_or_alias)
        if not command:
            from ..interfaces.command import CommandResult

            return CommandResult(success=False, message=f"Command '{name_or_alias}' not found")

        return command.execute(context)

    def list_commands(self) -> List[str]:
        """Get list of all registered command names.

        Returns
        -------
            List of command names
        """
        return list(self._commands.keys())

    def list_aliases(self) -> Dict[str, str]:
        """Get mapping of aliases to command names.

        Returns
        -------
            Dictionary mapping aliases to command names
        """
        return self._aliases.copy()

    def get_commands_by_type(self, cmd_type: CommandType) -> List[str]:
        """Get commands by type.

        Args:
            cmd_type: Command type

        Returns
        -------
            List of command names for the given type
        """
        return self._command_types.get(cmd_type, [])

    def get_all_types(self) -> List[CommandType]:
        """Get all registered command types.

        Returns
        -------
            List of command types
        """
        return list(self._command_types.keys())


# Global registry instance
_registry: Optional[CommandRegistry] = None


def get_registry() -> CommandRegistry:
    """Get the global command registry.

    Returns
    -------
        Global command registry instance
    """
    global _registry
    if _registry is None:
        _registry = CommandRegistry()
    return _registry


def register_command(command_class: Type[Command]) -> Type[Command]:
    """Register a command class.

    Args:
        command_class: The command class to register

    Returns
    -------
        The same command class (for decorator chaining)
    """
    registry = get_registry()
    registry.register(command_class)
    return command_class
