import argparse
import importlib
from pathlib import Path
from types import ModuleType
from typing import Any, Callable, Coroutine, Iterable, Protocol, TypeGuard

type CommandHandler = Callable[[argparse.Namespace], Coroutine[Any, Any, None]]


class CliModule(Protocol):
    name: str
    help: str
    add_args: Callable[[argparse.ArgumentParser], None]
    main: CommandHandler


def _is_cli_module(m: ModuleType) -> TypeGuard[CliModule]:
    return (
        hasattr(m, "name")
        and hasattr(m, "help")
        and hasattr(m, "add_args")
        and hasattr(m, "main")
    )


_clidir = Path(__file__).parent


def _load_cmds() -> Iterable[CliModule]:
    """Return all CLI command modules."""
    for f in _clidir.iterdir():
        if f.suffix == ".py" and not f.name.startswith("__"):
            m = importlib.import_module(f"{__package__}.{f.stem}")
            if not _is_cli_module(m):
                raise ValueError(f"Invalid CLI module: {m!a}")
            yield m


modules = sorted(_load_cmds(), key=lambda m: m.name)
