"""``mgf-fed start`` — the single federation front-door (print-only).

One command, one consistent agent message. ``start`` is a thin dispatcher over
the four onboarding prompts that already ship in ``mgf-standard`` — it never
duplicates their bodies, it routes to them:

* ``new``     → ``federation-new-project-prompt`` — an **empty** repo becomes a member
* ``onboard`` → ``federation-onboard-prompt`` — **existing** external code becomes a member
* ``sync``    → ``federation-sync-prompt`` — an **existing member** is brought current
* ``adopt``   → ``federation-adopt-prompt`` — a **non-member** adopts the standards, stays out

Detection resolves the one signal it can know for certain — *is this already a
federation member?* (the repo's ``[project].name`` is in the shipped registry,
:func:`mgf.standard.libraries`) — and prints the sync prompt. The join-vs-adopt
fork is **intent**, which no heuristic can read, so for a non-member ``start``
prints a scenario menu listing every mode + the exact re-run command (with a
detection hint: empty vs has-code, consumer vs not). ``--mode`` forces a mode;
``--list`` always prints the menu. Print-only by contract: ``start`` emits a
prompt or a menu and never touches the filesystem — the agent acts.
"""

from __future__ import annotations

import sys
from pathlib import Path
from typing import TYPE_CHECKING

from mgf.common.cli import EXIT_SUCCESS

from mgf.fed.cli._recipe_prompt import _print_recipe

if TYPE_CHECKING:
    import argparse

# mode → the mgf-standard recipe stem it dispatches to.
_MODES: dict[str, str] = {
    "new": "federation-new-project-prompt",
    "onboard": "federation-onboard-prompt",
    "sync": "federation-sync-prompt",
    "adopt": "federation-adopt-prompt",
}

# mode → (one-line "when", the re-run command) — CLI chrome for the menu. The
# prompt *bodies* live in mgf-standard; only this index lives in code.
_MENU: list[tuple[str, str]] = [
    ("sync", "already an mgf-* member — bring it current with the standards"),
    ("new", "empty/greenfield repo that will become a new mgf-* member"),
    ("onboard", "existing external project (has code/history) that will JOIN as a member"),
    ("adopt", "adopt the MGF standards but stay INDEPENDENT (not a member)"),
]


def add_start_args(subparser: argparse.ArgumentParser) -> None:
    """Register flags for ``mgf-fed start``."""
    subparser.add_argument(
        "--mode",
        choices=sorted(_MODES),
        default=None,
        help="force a scenario instead of auto-detecting (new | onboard | sync | adopt).",
    )
    subparser.add_argument(
        "--path",
        type=Path,
        default=None,
        help="repo directory to inspect for auto-detection (default: cwd).",
    )
    subparser.add_argument(
        "--list",
        action="store_true",
        help="just print the scenario menu; detect and dispatch nothing.",
    )


def _project_name(path: Path) -> str | None:
    """Return ``[project].name`` from ``path/pyproject.toml``, or ``None``."""
    pp = path / "pyproject.toml"
    if not pp.is_file():
        return None
    try:
        import tomllib

        data = tomllib.loads(pp.read_text(encoding="utf-8"))
    except (OSError, ValueError):
        return None
    name = (data.get("project") or {}).get("name")
    return name if isinstance(name, str) and name else None


def _member_names() -> frozenset[str]:
    """The federation member dist names from the shipped registry, or empty."""
    try:
        import mgf.standard

        libs = mgf.standard.libraries()
    except Exception:
        # Any resolve failure (missing/old mgf-standard, bad data) ⇒ "can't
        # tell" ⇒ fall through to the menu rather than crash.
        return frozenset()
    return frozenset(
        lib["repo"] for lib in libs if isinstance(lib, dict) and isinstance(lib.get("repo"), str)
    )


def _looks_empty(path: Path) -> bool:
    """True when ``path`` has no project shape yet (no pyproject, no source)."""
    if (path / "pyproject.toml").is_file():
        return False
    if (path / "src").is_dir() or (path / "build.zig").is_file():
        return False
    return not any(path.glob("*.py"))


def _depends_on_mgf(path: Path) -> bool:
    """True when ``path``'s pyproject declares any ``mgf-*`` dependency."""
    pp = path / "pyproject.toml"
    if not pp.is_file():
        return False
    try:
        import tomllib

        data = tomllib.loads(pp.read_text(encoding="utf-8"))
    except (OSError, ValueError):
        return False
    proj = data.get("project") or {}
    deps = list(proj.get("dependencies") or [])
    for group in (proj.get("optional-dependencies") or {}).values():
        deps.extend(group or [])
    return any(str(d).lower().lstrip().startswith("mgf-") for d in deps)


def _detection_hint(path: Path) -> str:
    """A one-line, human-readable read of the repo for the menu."""
    if _looks_empty(path):
        return "empty/greenfield repo → likely `new` (to join) or `adopt` (to stay out)"
    consumer = " depends on mgf-* (consumer)" if _depends_on_mgf(path) else ""
    return f"existing code{consumer} → likely `onboard` (to join) or `adopt` (to stay out)"


def _print_menu(path: Path, *, member_checked: bool) -> int:
    """Print the scenario menu to stdout. Returns ``EXIT_SUCCESS``."""
    lines = ["mgf-fed start — pick your federation entry point", ""]
    if member_checked:
        lines.append("This repo is NOT a federation member. Choose by intent:")
    else:
        lines.append("Choose your scenario by intent:")
    lines.append("")
    width = max(len(m) for m, _ in _MENU)
    for mode, when in _MENU:
        lines.append(f"  mgf-fed start --mode {mode.ljust(width)}   {when}")
    lines.append("")
    lines.append(f"Detected here: {_detection_hint(path)}.")
    lines.append("Re-run with --mode, or `mgf-fed start --list` to show this again.")
    print("\n".join(lines))
    return EXIT_SUCCESS


def run_start(args: argparse.Namespace) -> int:
    """Dispatch to one onboarding prompt, or print the scenario menu.

    Print-only: emits a prompt (resolved from ``mgf-standard``) or the menu, and
    never writes to the filesystem.
    """
    path = (args.path or Path.cwd()).resolve()

    # Explicit mode wins — no detection.
    if args.mode:
        return _print_recipe(_MODES[args.mode])

    # --list: menu only, no detection/dispatch.
    if args.list:
        return _print_menu(path, member_checked=False)

    # Auto-detect the one certain signal: already a member → sync.
    name = _project_name(path)
    if name and name in _member_names():
        print(
            f"mgf-fed start: detected federation member '{name}' → sync prompt.",
            file=sys.stderr,
        )
        return _print_recipe(_MODES["sync"])

    # Non-member (or undetectable): the join-vs-adopt fork is intent → menu.
    return _print_menu(path, member_checked=True)


__all__ = ["add_start_args", "run_start"]
