"""Fi-Wi framework CLI: argv dispatch, --ssh, fiber-map forwarding."""

import json
import os
import sys

from fiwi.harness import FiWiHarness
from fiwi.brainstem_loader import load_brainstem
from fiwi.ssh_dispatch import dispatch_fiber_mapped_ssh_if_needed
from fiwi.ssh_node import SshNode
from fiwi import usb_probe as usb


def _strip_async_from_argv(argv: list[str]) -> list[str]:
    """``--async`` → ``FIWI_REMOTE_DEFER=1`` (deferred remote calls return handles / Tasks)."""
    out = []
    for a in argv:
        if a == "--async":
            os.environ["FIWI_REMOTE_DEFER"] = "1"
            continue
        out.append(a)
    return out


def _parse_panel_calibrate_argv(args):
    """
    ``panel calibrate [merge] [N] [--ssh user@host] …``
    Returns ``(merge, limit, calibrate_ssh_hosts)``.
    """
    merge = False
    limit = None
    hosts = []
    i = 0
    while i < len(args):
        a = args[i]
        low = a.lower()
        if low == "merge":
            merge = True
            i += 1
            continue
        if low == "--ssh":
            if i + 1 >= len(args):
                print("panel calibrate: --ssh requires user@host", file=sys.stderr, flush=True)
                sys.exit(2)
            hosts.append(args[i + 1].strip())
            i += 2
            continue
        if a.isdigit():
            limit = int(a)
            i += 1
            continue
        print(f"panel calibrate: unknown argument {a!r}", file=sys.stderr, flush=True)
        sys.exit(2)
    return merge, limit, hosts


def main() -> int:
    sys.argv[:] = [sys.argv[0]] + _strip_async_from_argv(sys.argv[1:])
    argv = sys.argv[1:]
    if len(argv) >= 2 and argv[0] in ("--ssh", "--remote"):
        remote_host = argv[1]
        rest = argv[2:]
        if not rest:
            print(
                "Usage: fiwi.py --ssh user@host <command> [args...]\n"
                "  Example: fiwi.py --ssh pi@192.168.1.39 discover\n"
                "  If brainstem is in a Pi venv: copy remote_ssh.env.example → remote_ssh.env next to\n"
                "  this script on the PC where you run --ssh (paths in the file are on the Pi).\n"
                "  Or export FIWI_REMOTE_PYTHON / FIWI_REMOTE_SCRIPT.\n"
                "  On the Pi: pip install -r requirements.txt in that venv; udev 24ff.",
                file=sys.stderr,
                flush=True,
            )
            return 2
        return SshNode.parse(remote_host).invoke(rest, defer=False)

    rc_ssh_map = dispatch_fiber_mapped_ssh_if_needed(argv)
    if rc_ssh_map is not None:
        return rc_ssh_map

    os.write(2, b"fiwi: start\n")
    try:
        load_brainstem()
    except Exception as exc:
        print(f"fiwi: failed to import brainstem: {exc}", file=sys.stderr, flush=True)
        if isinstance(exc, ImportError):
            print(
                "  If this text came from `fiwi.py --ssh …`: the remote used system python3 by default.\n"
                "  On your PC export FIWI_REMOTE_PYTHON to the Pi venv’s python and\n"
                "  FIWI_REMOTE_SCRIPT to that fiwi.py (absolute paths on the Pi).",
                file=sys.stderr,
                flush=True,
            )
        return 1
    harness = FiWiHarness()
    try:
        cmd = sys.argv[1].lower() if len(sys.argv) > 1 else "status"
        target = sys.argv[2] if len(sys.argv) > 2 else "all"
        if cmd == "status":
            harness.status(target)
        elif cmd == "calibrate-ports-json":
            if not harness.hubs and not harness.connect():
                print("[]", flush=True)
            else:
                pairs = harness._ordered_downstream_ports()
                print(json.dumps([[h, p] for h, p in pairs]), flush=True)
        elif cmd == "lsusb-lines-json":
            print(json.dumps(usb.lsusb_lines()), flush=True)
        elif cmd == "wlan-info-json":
            from fiwi.ieee80211_dev import discover_wireless_for_map

            print(json.dumps(discover_wireless_for_map()), flush=True)
        elif cmd == "discover":
            harness.discover()
        elif cmd == "power":
            if len(sys.argv) < 5 or sys.argv[2].lower() != "fiber-port":
                print(
                    "Usage: fiwi.py power fiber-port <fiber_port_id> on|off\n"
                    "  Uses fiber_map.json; per-entry ssh / host+user forwards to that host (see help).",
                    file=sys.stderr,
                    flush=True,
                )
                return 2
            try:
                fp_n = int(sys.argv[3])
            except ValueError:
                print("fiber_port_id must be an integer.", file=sys.stderr, flush=True)
                return 2
            mode = sys.argv[4].lower()
            if mode not in ("on", "off"):
                print("Last argument must be on or off.", file=sys.stderr, flush=True)
                return 2
            harness.fiber_power(mode, fp_n)
        elif cmd == "fiber":
            if len(sys.argv) < 3:
                print(
                    "Usage: fiwi.py fiber status\n"
                    "       fiwi.py fiber chip <fiber_port_id> [save]\n"
                    "  status — hub.port, Route, power, and saved previews from fiber_map.json\n"
                    "  chip   — local lsusb diff only (not used for SSH-mapped / PCIe-fiber paths)",
                    file=sys.stderr,
                    flush=True,
                )
                return 2
            sub = sys.argv[2].lower()
            if sub == "status":
                harness.fiber_map_status()
            elif sub == "chip":
                if len(sys.argv) < 4:
                    print(
                        "Usage: fiwi.py fiber chip <fiber_port_id> [save]",
                        file=sys.stderr,
                        flush=True,
                    )
                    return 2
                try:
                    chip_fp = int(sys.argv[3])
                except ValueError:
                    print("fiber_port_id must be an integer.", file=sys.stderr, flush=True)
                    return 2
                save_chip = len(sys.argv) >= 5 and sys.argv[4].lower() == "save"
                harness.fiber_chip(chip_fp, save=save_chip)
            else:
                print(f"Unknown fiber subcommand: {sub!r}", file=sys.stderr, flush=True)
                return 2
        elif cmd == "panel":
            if len(sys.argv) < 3:
                print(
                    "Usage: fiwi.py panel status\n"
                    "       fiwi.py panel on|off <panel_port>\n"
                    "       fiwi.py panel reboot|reboot-force <panel_port>\n"
                    "       fiwi.py panel calibrate [merge] [<N>] [--ssh user@host] …\n"
                    "  calibrate: local hub ports first, then each --ssh host, calibrate_remotes in JSON, and/or\n"
                    "    FIWI_CALIBRATE_REMOTES in remote_ssh.env (comma-separated) for one-command hybrid.\n"
                    "  merge / N as before; remote steps set \"ssh\" on new fiber_ports entries.\n"
                    "  Calibrate starts by setting patch_panel.slots (front-panel positions); panel <n> is 1…slots.\n"
                    "  Use power fiber-port for arbitrary fiber ids beyond the panel if needed.\n"
                    "  Preset: fiber_map.rpi20.json → fiber_map.json for 8+8+4 → fiber ports 1–20.",
                    file=sys.stderr,
                    flush=True,
                )
                return 2
            sub = sys.argv[2].lower()
            if sub == "status":
                harness.panel_status()
            elif sub == "calibrate":
                args = sys.argv[3:]
                merge, limit, cal_hosts = _parse_panel_calibrate_argv(args)
                harness.panel_calibrate(merge=merge, limit=limit, calibrate_ssh_hosts=cal_hosts)
            elif sub in ("on", "off"):
                if len(sys.argv) < 4:
                    print(
                        f"Usage: fiwi.py panel {sub} <1-N> (N = patch_panel.slots in fiber_map, default 24)",
                        file=sys.stderr,
                        flush=True,
                    )
                    return 2
                harness.panel_power(sub, int(sys.argv[3]))
            elif sub == "reboot":
                if len(sys.argv) < 4:
                    print(
                        "Usage: fiwi.py panel reboot <1-N> (N from fiber_map patch_panel.slots)",
                        file=sys.stderr,
                        flush=True,
                    )
                    return 2
                harness.panel_reboot(int(sys.argv[3]), skip_empty=True)
            elif sub == "reboot-force":
                if len(sys.argv) < 4:
                    print(
                        "Usage: fiwi.py panel reboot-force <1-N> (N from fiber_map patch_panel.slots)",
                        file=sys.stderr,
                        flush=True,
                    )
                    return 2
                harness.panel_reboot(int(sys.argv[3]), skip_empty=False)
            else:
                print(f"Unknown panel subcommand: {sub!r}", file=sys.stderr, flush=True)
                return 2
        elif cmd in ("on", "off"):
            if not harness.power(cmd, target):
                return 1
        elif cmd in ("reboot", "reboot-force"):
            harness.reboot(target, skip_empty=(cmd == "reboot"))
        elif cmd == "setup":
            harness.setup_udev()
        elif cmd == "verify":
            harness.verify()
        elif cmd in ("help", "-h", "--help"):
            print(
                "Fi-Wi test framework — CLI\n"
                "Usage: fiwi.py [--async] <command> [target]\n"
                "  --async  set FIWI_REMOTE_DEFER: deferred calls spawn ssh child processes immediately;\n"
                "           panel calibrate overlaps them (join via handle.result(); no Python threads).\n"
                "           Or set FIWI_REMOTE_DEFER=1 / remote_ssh.env (see remote_ssh.env.example).\n"
                "  discover          — list hubs (serial, port count); no port I/O\n"
                "  status [target]   — default command; target like all, 1.3, all.2\n"
                "  fiber status      — fiber_ports + power (local or per-entry ssh / host+user)\n"
                "  fiber chip <id> [save] — local lsusb probe (SSH-mapped ports: wlan/pcie from calibrate, not fiber chip)\n"
                "  wlan-info-json — machine-readable wireless NIC snapshot (sysfs + lspci/iw); used by panel calibrate over SSH\n"
                "  power fiber-port <id> on|off — power by fiber key (ssh forward if map says so)\n"
                "  panel status      — rack positions 1…N (N = patch_panel.slots, default 24)\n"
                "  panel calibrate … — set patch panel size first, then USB hub walk → fiber_map.json\n"
                "  panel on|off|reboot|reboot-force <n>\n"
                "  on|off [target]  reboot|reboot-force [target]  setup  verify\n"
                "\n"
                "Remote (hubs on another host — no local brainstem needed):\n"
                "  fiwi.py --ssh user@host discover\n"
                "  remote_ssh.env next to fiwi.py (see remote_ssh.env.example) or env vars:\n"
                "  FIWI_REMOTE_PYTHON  remote interpreter (default python3)\n"
                "  FIWI_REMOTE_SCRIPT  remote script path (default /usr/local/bin/fiwi.py)\n"
                "  FIWI_SSH_OPTS       e.g. '-o BatchMode=yes'\n"
                "  FIWI_CALIBRATE_REMOTES  optional comma-separated user@host for panel calibrate (no --ssh needed)\n"
                "  Pi: venv + requirements.txt; udev 24ff.\n"
                "\n"
                "fiber_map.json fiber_ports entries may set ssh routing (hubs on another machine):\n"
                '  "ssh": "user@host"  or  "remote": "…"  or  "host": "ip", "user": "pi"\n'
                "  On the SSH destination, the same fiber id should be local (omit ssh) so commands are not re-forwarded.\n"
                '  Optional "pcie": { bus, switch, slot, adapter_port, sfp_serial, board_serial, … } — calibrate can fill via 1–6+SFP.\n'
                "\n"
                "Hybrid calibrate: put {\"calibrate_remotes\": [\"pi@ip\"]} in fiber_map.json or pass --ssh per host;\n"
                "  order is all local downstream ports, then each remote’s ports (see calibrate-ports-json on the Pi)."
            )
        else:
            print(f"Unknown command: {cmd!r}", file=sys.stderr, flush=True)
            print(
                "Try: --ssh user@host … | discover | calibrate-ports-json | wlan-info-json | status | fiber | power | panel | … | help",
                file=sys.stderr,
                flush=True,
            )
            return 2
    finally:
        harness.disconnect()
    return 0
