"""mgf.hrb.cli — operator CLI (DEPRECATED — superseded by hrb-app cli).

The embedded CLI that previously shipped here has been ABSORBED into
hrb-app (the desktop GUI repo, at /home/bassam/PycharmProjects/hrb-app
or codeberg.org/magogi-admin/hrb-app). Phase B of the presentation-
layer plan (~/.claude/plans/async-tumbling-waterfall.md) moved each
subcommand under hrb-app's existing 3-layer architecture:

  * `mgf-hrb scan`    → `hrb-app cli scan`    (alias `python -m hrb_app.ui.cli scan`)
  * `mgf-hrb info`    → `hrb-app cli info`
  * `mgf-hrb doctor`  → `hrb-app cli doctor`
  * `mgf-hrb explain` → `hrb-app cli explain`

This module is RETAINED for one release cycle as a deprecation banner
— calling `mgf-hrb` prints a migration pointer + delegates to the old
subcommand implementations (which still live under
`mgf.hrb.cli._{doctor,info,scan,explain}` for backwards compatibility
during the transition). After 0.1.x, both the entry point and the
private modules will be removed; the `mgf-hrb` command will no longer
exist and `pip install mgf-hrb` will leave operators with the SDK only.

**Why hrb-app and not `mgf-hrb-cli`?** The original plan considered a
sibling `mgf-hrb-cli` package; the Magogi Foundation discipline
(PR-02 — see mgf-common/docs/standards/PRESENTATION_LAYER.md when
shipped) recommends concentrating presentation logic in an `<sibling>-app`
repo with the canonical 3-layer split. hrb-app already follows that
shape; absorbing the CLI as `hrb-app/ui/cli/` reuses the existing
`view/` projections + the `core/` broker facade. One owner, no
cross-sibling drift.

**Why no shim that delegates to hrb-app?** Forwarding `mgf-hrb` →
`hrb-app cli` would inverse the dependency direction (mgf-hrb importing
hrb-app), violating the federation's closed-box invariant. Instead this
module prints a clear migration message + still runs the legacy
subcommands locally for one cycle. Operators get a smooth transition
without the SDK gaining an app-side dep.
"""

from __future__ import annotations

import argparse
import logging
import sys
from typing import TYPE_CHECKING

import mgf.common

from mgf.hrb import HrbSettings, __version__

if TYPE_CHECKING:
    from collections.abc import Sequence

__all__ = ["build_parser", "main"]

logger = logging.getLogger(__name__)


# S4 — sanitize untrusted text before interpolating into argparse
# help. The default value for `--socket` comes from `HrbSettings`
# which honours the MGF_HRB_SOCKET_PATH env var; a careless
# operator setting it to a value with newlines / control chars
# would bleed those into `--help` output (and into any log sink
# that captures the help text). Bounded-output discipline.
_HELP_MAX_LEN = 200


def _safe_for_help(text: str) -> str:
    """Strip control chars + cap length; defensive for env-derived defaults."""
    cleaned = "".join(c if c.isprintable() else "?" for c in str(text))
    if len(cleaned) > _HELP_MAX_LEN:
        cleaned = cleaned[: _HELP_MAX_LEN - 3] + "..."
    return cleaned


def build_parser(settings: HrbSettings | None = None) -> argparse.ArgumentParser:
    """Construct the top-level argument parser.

    ``settings`` is injected so tests can override defaults without
    touching the environment (CF-08, CF-10). When omitted the CLI
    constructs ``HrbSettings()``, which honours CF-02's precedence
    chain (CLI > env > YAML > defaults).
    """
    settings = settings if settings is not None else HrbSettings()

    p = argparse.ArgumentParser(
        prog="mgf-hrb",
        description="Operator CLI for the Magogi Foundation hardware-resource broker.",
    )
    p.add_argument("--version", action="version", version=f"mgf-hrb {__version__}")
    p.add_argument(
        "--socket",
        default=settings.socket_path,
        help=(
            "Path to the broker's Unix socket "
            f"(default: {_safe_for_help(settings.socket_path)}; "
            "override via MGF_HRB_SOCKET_PATH)."
        ),
    )

    sub = p.add_subparsers(dest="command", required=True)

    scan = sub.add_parser("scan", help="Enumerate broker resources and render the tree.")
    scan.add_argument(
        "scope",
        nargs="?",
        default="hw:/**",
        help="URN-glob filter (default: hw:/**).",
    )

    info = sub.add_parser("info", help="Show detail on one resource.")
    info.add_argument("urn", help="The resource URN to inspect.")

    sub.add_parser(
        "doctor",
        help="Diagnose broker preconditions (socket, IOMMU, udev, group membership).",
    )

    explain = sub.add_parser(
        "explain",
        help=(
            "Explain a hardware family or tool: visual diagram + the vocabulary "
            "used in the matching scan/doctor output. Topics: usb, pci, doctor."
        ),
    )
    explain.add_argument(
        "topic",
        nargs="?",
        default=None,
        help="Topic to explain. Omit to list available topics.",
    )

    return p


_DEPRECATION_BANNER = (
    "DEPRECATION NOTICE: the `mgf-hrb` CLI moved to the hrb-app "
    "project.\n"
    "  Install hrb-app and run `hrb-app cli <subcommand>` instead "
    "(or `python -m hrb_app.ui.cli <subcommand>`).\n"
    "  Same subcommands, same output shape, same exit codes — "
    "operator UX is preserved.\n"
    "  This `mgf-hrb` entry point will be REMOVED after 0.1.x.\n"
)


def main(argv: Sequence[str] | None = None) -> int:
    """Entry point used by the ``mgf-hrb`` console script and ``python -m
    mgf.hrb.cli``. Returns a process exit code; never raises uncaught.

    **Deprecated.** Prints a migration pointer on every invocation +
    continues to run the legacy subcommand implementations for one
    release cycle. Operators should install hrb-app and use
    ``hrb-app cli <subcommand>`` going forward. After 0.1.x this
    entry point will be removed; the SDK alone won't include any
    CLI surface.

    Wraps the dispatch in ``mgf.common.bootstrap`` (EH-04) so the
    federation's top-level handler, logging configuration, and
    observability subsystems are wired uniformly. Expected failures
    translate via the typed exception tree in ``mgf.hrb.exceptions``;
    any unexpected exception escapes so the user sees the traceback.
    """
    # Print the migration banner to stderr first thing — visible
    # whether or not the operator's logging config picks up the
    # DeprecationWarning. Single source of truth for the message
    # text (re-used by any tooling that wants to surface the same
    # pointer).
    import warnings as _warnings

    print(_DEPRECATION_BANNER, file=sys.stderr)
    _warnings.warn(
        "The `mgf-hrb` CLI is deprecated; install hrb-app and use "
        "`hrb-app cli` instead. See mgf-hrb's CHANGELOG for the "
        "migration plan.",
        DeprecationWarning,
        stacklevel=2,
    )

    settings = HrbSettings()
    args = build_parser(settings).parse_args(argv)

    # S3 — lazy-import subcommand modules AFTER the deprecation
    # banner. If a transitive dep of a legacy subcommand is missing
    # in the operator's environment, importing at module-top would
    # raise BEFORE the migration banner is printed — the operator
    # would see a traceback, not the migration pointer. Importing
    # here means the banner / DeprecationWarning are visible first,
    # then any per-subcommand import failure surfaces normally.
    with mgf.common.bootstrap(app_name="mgf-hrb", app_version=__version__):
        logger.debug(
            "dispatching subcommand %s",
            args.command,
            extra={"socket_path": args.socket, "subcommand": args.command},
        )
        if args.command == "scan":
            from mgf.hrb.cli._scan import scan_command

            return scan_command(socket_path=args.socket, scope=args.scope)
        if args.command == "info":
            from mgf.hrb.cli._info import info_command

            return info_command(socket_path=args.socket, urn=args.urn)
        if args.command == "doctor":
            from mgf.hrb.cli._doctor import doctor_command

            return doctor_command(socket_path=args.socket)
        if args.command == "explain":
            from mgf.hrb.cli._explain import explain_command

            return explain_command(topic=args.topic)

    logger.error("unknown subcommand: %s", args.command)
    print(f"unknown subcommand: {args.command}", file=sys.stderr)
    return 2
