"""``mgf-hrb info <urn>`` — one-resource detail with fidelity tags visible.

Per the USB-worker design §5.9 (per-resource attrs over the wire) and
FEEDBACK paper TOOLS-01 (the operator-facing surface). The broker's
``inspect`` op returns the basic ``Resource`` (urn, family, kind,
fidelity) plus a backend-specific ``attrs`` dict:

- USB: ``idVendor``, ``idProduct``, ``serial``, ``bInterfaceClass``,
  ``bInterfaceSubClass``, ``bInterfaceProtocol``, ``busnum``, ``devnum``.
- PCI: ``vendor``, ``device``, ``class``, ``bdf``.
- FakeBackend: ``backend``, ``synthetic``.

This command renders the response in operator-friendly form, with
USB-IF class-code decoding when the resource is a usb_interface.
"""

from __future__ import annotations

import logging
import sys
from typing import Any

from mgf.hrb.exceptions import MgfHrbError, NotFoundError

logger = logging.getLogger(__name__)


# Subset of the USB-IF class codes that show up most often. Mirrors the
# table in `hrb-code/src/usb_scan.zig::classLabel`; keep in sync if either
# changes. Unknown codes render as the raw hex byte.
_USB_CLASS_LABEL: dict[int, str] = {
    0x01: "Audio",
    0x02: "Comm / CDC",
    0x03: "HID",
    0x05: "Physical",
    0x06: "Image",
    0x07: "Printer",
    0x08: "Mass Storage",
    0x09: "Hub",
    0x0A: "CDC-Data",
    0x0B: "Smart Card",
    0x0D: "Content Security",
    0x0E: "Video",
    0x0F: "Personal Health",
    0x10: "Audio/Video",
    0x11: "Billboard",
    0xDC: "Diagnostic",
    0xE0: "Wireless",
    0xEF: "Misc",
    0xFE: "App Specific",
    0xFF: "Vendor Specific",
}


def info_command(*, socket_path: str, urn: str) -> int:
    logger.debug("info starting", extra={"socket_path": socket_path, "urn": urn})
    try:
        from mgf.hrb import Client

        with Client.connect(socket_path) as c:
            # The CLI renderer walks the raw wire dict (it reads
            # backend-specific attr keys directly + may surface
            # forward-compat fields the typed shape doesn't promise).
            # FEEDBACK SDK-02 typed half: GUI consumers reach for the
            # typed `c.inspect()` instead — same data, dataclass shape.
            detail = c.inspect_raw(urn)
    except FileNotFoundError:
        logger.warning("broker socket missing at %s", socket_path)
        print(
            f"error: no broker is listening at {socket_path}.\n"
            "  Start one (`./hrb-code/zig-out/bin/mgf-hrb-server`) or pass --socket.",
            file=sys.stderr,
        )
        return 2
    except NotFoundError:
        logger.info("urn not found: %s", urn)
        print(f"error: no resource at URN {urn}", file=sys.stderr)
        print(
            "  Run `mgf-hrb scan` to see what URNs the broker currently serves.",
            file=sys.stderr,
        )
        return 1
    except MgfHrbError as exc:
        logger.error("broker returned error: %s", exc)
        print(f"error from broker: {exc}", file=sys.stderr)
        return 1

    _render(detail)
    return 0


def _render(detail: dict[str, Any]) -> None:
    """Format the inspect response in operator-friendly form."""
    print(f"mgf-hrb info {detail.get('urn', '?')}")
    print()
    print(f"  URN:         {detail.get('urn', '?')}")
    print(f"  Family:      {detail.get('family', '?')}")
    print(f"  Kind:        {detail.get('kind', '?')}")
    print(f"  Fidelity:    {detail.get('fidelity', '?')}")

    attrs = detail.get("attrs", {}) or {}
    if not attrs:
        print()
        print("  (no per-resource attributes — backend reported a bare Resource)")
        return

    kind = detail.get("kind", "")
    if kind == "usb_interface":
        _render_usb_attrs(attrs)
    elif kind == "pcie_function":
        _render_pci_attrs(attrs)
    else:
        _render_generic_attrs(attrs)


def _render_usb_attrs(a: dict[str, Any]) -> None:
    """USB descriptor render. Decodes bInterfaceClass to a human label
    matching mgf-hrb-usb-scan's `Class` column."""
    print()
    print("  USB descriptor:")
    vid, pid = a.get("idVendor", "?"), a.get("idProduct", "?")
    print(f"    VID:PID:           {vid}:{pid}")
    if "serial" in a:
        print(f"    Serial:            {a['serial']}")
    iface_class = a.get("bInterfaceClass")
    if iface_class is not None:
        try:
            label = _USB_CLASS_LABEL.get(int(iface_class, 16), f"Class 0x{iface_class}")
        except (TypeError, ValueError):
            label = f"Class 0x{iface_class}"
        sub = a.get("bInterfaceSubClass", "?")
        proto = a.get("bInterfaceProtocol", "?")
        print(
            f"    Class:             {label}  (class=0x{iface_class} sub=0x{sub} proto=0x{proto})"
        )
    if "busnum" in a and "devnum" in a:
        bn, dn = int(a["busnum"]), int(a["devnum"])
        print(f"    Kernel devnode:    /dev/bus/usb/{bn:03d}/{dn:03d}")
        print(
            "                       "
            "(devnum rotates on every replug — never store it as identity; §2.2)"
        )

    # Anything else the backend pushed but we don't render specially.
    rendered = {
        "idVendor",
        "idProduct",
        "serial",
        "bInterfaceClass",
        "bInterfaceSubClass",
        "bInterfaceProtocol",
        "busnum",
        "devnum",
    }
    extras = {k: v for k, v in a.items() if k not in rendered}
    if extras:
        print()
        print("  Other attributes:")
        for k, v in extras.items():
            print(f"    {k}: {v}")


def _render_pci_attrs(a: dict[str, Any]) -> None:
    """PCI descriptor render. Vendor/device IDs are raw hex (0xNNNN); a
    future iteration could look them up in the USB-IF / PCI-SIG ID
    databases, but operator-facing tooling typically pastes them into
    `lspci -nn` for the lookup, which is fine."""
    print()
    print("  PCI descriptor:")
    print(f"    BDF:               {a.get('bdf', '?')}")
    print(f"    Vendor / Device:   {a.get('vendor', '?')} / {a.get('device', '?')}")
    print(f"    Class:             {a.get('class', '?')}")
    rendered = {"bdf", "vendor", "device", "class"}
    extras = {k: v for k, v in a.items() if k not in rendered}
    if extras:
        print()
        print("  Other attributes:")
        for k, v in extras.items():
            print(f"    {k}: {v}")


def _render_generic_attrs(a: dict[str, Any]) -> None:
    """Fallback render for backends we don't recognise by kind (FakeBackend,
    future workers)."""
    print()
    print("  Attributes:")
    for k, v in a.items():
        print(f"    {k}: {v}")
