"""Base class for subcommand-based CLI configurations.

Subcommands provide a clean CLI experience where the first positional arg
selects the command, and each command has its own flags:

    sm create --tag dev
    sm r vajra              # alias for reattach
    sm cc --args "--resume" # alias for claude

This mirrors BasePolyConfig's pattern (variant classes inherit from a base)
but with positional type selection instead of --type flags.
"""

from __future__ import annotations

from typing import ClassVar, Dict, Optional, Type


class BaseCommand:
    """Base class for subcommand-based CLI tools.

    Usage::

        @frozen_dataclass
        class MyTool(BaseCommand):
            '''My Tool description'''
            verbose: bool = field(False, help="Verbose output")

        @frozen_dataclass
        class Run(MyTool, name="run", alias="r", default=True):
            '''Run the tool'''
            target: str = field(".", help="Target path")

        @frozen_dataclass
        class List(MyTool, name="list", alias="l"):
            '''List items'''

        cmd = parse_cli_args(MyTool)
    """

    # Registry attributes — initialized per concrete base class
    _subcommands: ClassVar[Dict[str, Type[BaseCommand]]]
    _aliases: ClassVar[Dict[str, str]]
    _default_command: ClassVar[Optional[str]]
    _is_base_command: ClassVar[bool]

    def __init_subclass__(
        cls,
        name: str | None = None,
        alias: str | None = None,
        default: bool = False,
        **kwargs,
    ):
        super().__init_subclass__(**kwargs)

        if name is None:
            # Intermediate base class (e.g., SmritiCommand) — initialize registry
            cls._subcommands = {}
            cls._aliases = {}
            cls._default_command = None
            cls._is_base_command = True
            return

        # Concrete subcommand (e.g., Create, Reattach)
        cls._is_base_command = False
        cls._command_name = name
        cls._command_alias = alias

        # Find the parent base command class
        base = _find_base_command(cls)
        if base is None:
            raise TypeError(
                f"{cls.__name__} must inherit from a BaseCommand subclass "
                f"that does not specify name="
            )

        # Register name
        if name in base._subcommands:
            raise ValueError(
                f"Subcommand name '{name}' already registered by "
                f"{base._subcommands[name].__name__}"
            )
        base._subcommands[name] = cls

        # Register alias
        if alias:
            if alias in base._aliases:
                raise ValueError(
                    f"Alias '{alias}' already registered for '{base._aliases[alias]}'"
                )
            if alias in base._subcommands:
                raise ValueError(
                    f"Alias '{alias}' conflicts with subcommand name '{alias}'"
                )
            base._aliases[alias] = name

        # Register default
        if default:
            if base._default_command is not None:
                raise ValueError(
                    f"Default command already set to '{base._default_command}', "
                    f"cannot also set '{name}' as default"
                )
            base._default_command = name

    @classmethod
    def resolve_subcommand(cls, name: str) -> Type[BaseCommand] | None:
        """Resolve a command name or alias to the subcommand class."""
        if name in cls._subcommands:
            return cls._subcommands[name]
        canonical = cls._aliases.get(name)
        if canonical:
            return cls._subcommands[canonical]
        return None

    @classmethod
    def get_default_subcommand(cls) -> Type[BaseCommand] | None:
        """Get the default subcommand class, if any."""
        if cls._default_command:
            return cls._subcommands[cls._default_command]
        return None


def _find_base_command(cls: type) -> Type[BaseCommand] | None:
    """Walk MRO to find the intermediate base command class (the one with the registry)."""
    for parent in cls.__mro__[1:]:
        if (
            parent is not BaseCommand
            and parent is not cls
            and issubclass(parent, BaseCommand)
            and getattr(parent, "_is_base_command", False)
        ):
            return parent
    return None
