#!/usr/bin/env python3
"""deus-seize <nick> — full campaign stop: revoke the lease + abort in-flight work across
every host an orchestrator could be running on. Design item (a), 2026-07-27 — "Deus hat
selbst keinen Token, kann ihn aber im Bedarfsfall entziehen, und ALLE laufenden Prompts
werden dabei abgebrochen."

Real fan-out, not a flag: this was the manual, ad-hoc sequence used earlier this session to
stop a stuck @foreman (restart opencode-foreman.service, hand-run oc-rpc abort) — bundled
into one command instead of a `systemctl stop` sledgehammer.

  deus-seize foreman
  deus-seize foreman --by deus
"""
import json, os, re, shutil, subprocess, sys
sys.path[:0] = [p for p in (os.path.join(os.path.dirname(os.path.realpath(__file__)), "..", "lib"), "/usr/local/lib/bullpen") if os.path.isdir(p)]
import bullpen_config as cfg

# Resolve via PATH so this works wherever sic is installed; the absolute path stays as the
# fallback because deus-seize may be run from a stripped environment (a systemd unit, a cron).
SIC = shutil.which("sic") or "/usr/local/bin/sic"


def sic(host, *args, timeout=30):
    try:
        return subprocess.run([SIC, host, *args], capture_output=True, text=True, timeout=timeout)
    except Exception as e:
        class R: returncode = 1; stdout = ""; stderr = str(e)
        return R()


def _post_secret():
    # local copies first (works if run ON the room host as root, or from a host with its own copy)
    for p in (cfg.SECRET_FILE, os.path.expanduser("~/.config/bullpen/post-secret")):
        try:
            s = open(p).read().strip()
            if s:
                return s
        except OSError:
            pass
    # otherwise fetch it from the room host over sic — the file is root-only there, and
    # deus-seize is meant to run as a normal user (with working sic/SSH), not as root (root has
    # no sic identity of its own, which is what broke this the first time it was run: `sudo
    # deus-seize` makes EVERY internal sic call below try to SSH out as root too).
    r = sic(cfg.ROOM_HOST, "sudo", "cat", cfg.SECRET_FILE)
    s = (r.stdout or "").strip()
    return s or None


def main():
    if len(sys.argv) < 2:
        print("usage: deus-seize <nick> [--by <name>]", file=sys.stderr)
        sys.exit(2)
    nick = sys.argv[1]
    if not re.fullmatch(r"[A-Za-z0-9_-]+", nick):
        # the nick is interpolated into a remote `sh -c` below (step 3) — keep it a bare word
        print(f"deus-seize: refusing suspicious nick {nick!r}", file=sys.stderr)
        sys.exit(2)
    by = "deus"
    if "--by" in sys.argv:
        by = sys.argv[sys.argv.index("--by") + 1]

    secret = _post_secret()
    if not secret:
        print("deus-seize: no post-secret readable — cannot authenticate lease_seize", file=sys.stderr)
        sys.exit(1)

    print(f"1. revoking {nick}'s campaign lease...")
    # NOTE: the secret travels in argv, so it is visible in `ps` on the room host (and in
    # curl's own argv inside lmcp-tool). lmcp-tool has no stdin form for argument values;
    # closing this needs a change to lmcp-tool itself, which is fleet-wide. Tracked, not fixed here.
    r = sic(cfg.ROOM_HOST, "lmcp-tool", "lease_seize", f"nick={nick}", f"by={by}", f"secret={secret}")
    print(f"   {(r.stdout or r.stderr).strip()}")

    print(f"2. restarting {nick}'s lurker on {cfg.COORD_HOST} (kills any in-flight subprocess/HTTP call)...")
    r = sic(cfg.COORD_HOST, "systemctl", "--user", "restart", f"bullpen-lurker@{nick}.service")
    print("   restarted" if r.returncode == 0 else f"   FAILED: {(r.stderr or r.stdout).strip()}")

    print(f"3. aborting {nick}'s opencode session...")
    # ~ expands to the remote user's home — no username baked in. nick is validated above.
    r = sic(cfg.COORD_HOST, "sh", "-c", f"cat ~/{nick}_lurker/.session")
    sid = (r.stdout or "").strip()
    if sid and r.returncode == 0:
        # oc-rpc talks straight to the opencode server's HTTP API (cfg.OC_URL) — it runs
        # LOCALLY (wherever deus-seize itself runs), not sic'd onto that host (it needs no
        # shell there at all, and that host has no copy of oc-rpc anyway — this bit for real
        # on first test).
        OC_RPC = shutil.which("oc-rpc") or os.path.expanduser("~/.local/bin/oc-rpc")
        try:
            r2 = subprocess.run([OC_RPC, "abort", sid], capture_output=True, text=True, timeout=30)
            print(f"   session {sid}: {(r2.stdout or r2.stderr).strip()}")
        except FileNotFoundError:
            print(f"   session {sid}: oc-rpc not found at {OC_RPC} on this host — abort skipped")
    else:
        print("   no cached session id found — nothing to abort")

    print(f"4. checking {cfg.GRIND_HOST} for active grind processes (NOT auto-killed — the grind "
          f"command line doesn't carry the dispatching nick, so attribution isn't safe to "
          f"automate; kill by hand if one of these is this campaign's):")
    r = sic(cfg.GRIND_HOST,
            "sh", "-c",
            r"""for p in $(pgrep -f '[b]ullpen-grinder --once' 2>/dev/null); do """
            r"""a=$(ps -o etimes= -p $p 2>/dev/null | tr -d ' '); """
            r"""c=$(ps -o args= -p $p 2>/dev/null | cut -c1-100); """
            r"""echo "  pid=$p age=${a}s cmd=$c"; done""")
    out = (r.stdout or "").strip()
    print(out if out else "   none running")

    print(f"\n{nick} seized by {by}. Lease revoked — its next `ask` dispatch is rejected "
          f"until lease_acquire runs again (the lurker does this automatically on its next "
          f"legitimate poke, so this is a real stop, not a permanent ban).")


if __name__ == "__main__":
    main()
