#!/usr/bin/env bash
# vast-gpu: thin front door to forgia-executor, the local Vast client.
# No retries, no fallbacks, no invented defaults. Failures stay loud.
set -euo pipefail

EXEC="${FORGIA_EXECUTOR:-$HOME/works/repos/torchio/executor/target/release/forgia-executor}"
CRED="${VAST_CRED_FILE:-$HOME/.agent_credentials/tokens/vast.env}"
ROOT="${VAST_EVIDENCE_ROOT:-$HOME/.forgia/vast-evidence}"

if [ ! -x "$EXEC" ]; then
  echo "vast-gpu: executor binary not found or not executable: $EXEC" >&2
  echo "build it: cd ~/works/repos/torchio/executor && cargo build --release" >&2
  exit 2
fi

stamp() { date -u +%Y%m%dT%H%M%SZ; }

# Turn the credential-store KEY=VALUE file into the raw 0600 token file the
# executor requires (single ASCII token, current-user owned, mode 0600).
tmp=""
# An EXIT trap whose last command fails would become the script's exit status,
# which silently broke `path`, `last` and `help` (no token, so tmp stays empty).
cleanup() { if [ -n "$tmp" ]; then rm -rf "$tmp"; fi; }
trap cleanup EXIT

token_file() {
  [ -f "$CRED" ] || { echo "vast-gpu: credential file missing: $CRED" >&2; exit 2; }
  tmp="$(mktemp -d "${TMPDIR:-/tmp}/vast-gpu.XXXXXX")"
  chmod 700 "$tmp"
  awk -F= '/^VAST_API_KEY=/{sub(/^VAST_API_KEY=/,""); print; found=1} END{if(!found) exit 1}' \
    "$CRED" > "$tmp/token" || { echo "vast-gpu: no VAST_API_KEY line in $CRED" >&2; exit 2; }
  chmod 600 "$tmp/token"
  [ -s "$tmp/token" ] || { echo "vast-gpu: VAST_API_KEY is empty" >&2; exit 2; }
  printf '%s' "$tmp/token"
}

# The executor creates the evidence directory itself and refuses a path that
# already exists, so compute a fresh name and never mkdir the leaf.
evidence_dir() {
  mkdir -p "$ROOT"
  base="$ROOT/$(stamp)-$1"
  d="$base"
  n=1
  while [ -e "$d" ]; do d="$base.$n"; n=$((n + 1)); done
  printf '%s' "$d"
}

# Documented executor exit codes.
explain() {
  case "$1" in
    0) echo "ok" ;;
    2) echo "error" ;;
    3) echo "not_submitted (nothing reached the provider)" ;;
    4) echo "duplicate (refused: unexplained retained state)" ;;
    5) echo "ambiguous (outcome unknown; reconcile, never re-send)" ;;
    6) echo "guardian absent" ;;
    8) echo "committed, mirror projection failed" ;;
    10) echo "committed execution failure" ;;
    *) echo "unmapped exit code" ;;
  esac
}

run() { # run <label> <args...> : executor creates the evidence dir itself
  local label="$1"; shift
  local dir; dir="$(evidence_dir "$label")"
  echo "evidence: $dir"
  set +e
  "$EXEC" "$@" --evidence-dir "$dir" --token-file "$(token_file)"
  local code=$?
  set -e
  echo "exit $code: $(explain "$code")"
  return $code
}

# `offers` takes no --evidence-dir: it prints its receipt. The wrapper keeps its
# own copy so the market read is still reproducible.
run_stdout() { # run_stdout <label> <args...>
  local label="$1"; shift
  local dir; dir="$(evidence_dir "$label")"
  mkdir -p "$dir"
  chmod 700 "$dir"
  echo "evidence: $dir"
  set +e
  "$EXEC" "$@" --token-file "$(token_file)" > "$dir/stdout.json" 2> "$dir/stderr.txt"
  local code=$?
  set -e
  grep -v '^$' "$dir/stdout.json" | tail -40
  [ -s "$dir/stderr.txt" ] && cat "$dir/stderr.txt" >&2
  echo "exit $code: $(explain "$code")"
  return $code
}

ssh_coords() { # ssh_coords <evidence-dir> -> "host port status" from the raw provider body
  python3 - "$1" <<'PY'
import json, sys, os, glob
path = os.path.join(sys.argv[1], "response.bin")
if not os.path.exists(path):
    pages = sorted(glob.glob(os.path.join(sys.argv[1], "page-*.bin")))
    if not pages:
        print("no provider body in %s" % sys.argv[1]); sys.exit(1)
    path = pages[0]
try:
    o = json.load(open(path))
except Exception as exc:
    print("unreadable provider body: %s" % exc); sys.exit(1)
if isinstance(o, dict) and "instances" in o:
    o = o["instances"]
if isinstance(o, list):
    o = o[0] if o else None
if not isinstance(o, dict):
    print("provider does not report this instance"); sys.exit(1)
host = o.get("ssh_host") or o.get("public_ipaddr")
port = o.get("ssh_port")
if host and port:
    print(host, port, o.get("actual_status", "?"))
    sys.exit(0)
print("no ssh coordinates in provider body (status=%s)" % o.get("actual_status", "?")); sys.exit(1)
PY
}

case "${1:-help}" in
  inventory)
    shift; run inventory vast inventory "$@"
    ;;
  instance)
    [ $# -ge 2 ] || { echo "usage: vast-gpu instance <id>" >&2; exit 2; }
    run "instance-$2" vast instance --id "$2"
    ;;
  offers)
    shift; run_stdout offers offers "$@"
    ;;
  preflight)
    [ $# -ge 2 ] || { echo "usage: vast-gpu preflight <plan.json>" >&2; exit 2; }
    run_stdout preflight vast preflight --plan "$2"
    ;;
  up)
    # up --ask <offer-id> --image <img> --onstart <file> --disk-gb <n> [--key <pubkey>]
    shift  # drop the subcommand word, else the flag loop sees "up" as an unknown flag
    ask=""; image=""; onstart=""; disk=""; key="${VAST_SSH_PUBKEY:-$HOME/.ssh/id_ed25519.pub}"
    while [ $# -gt 0 ]; do
      case "$1" in
        --ask) ask="$2"; shift 2 ;;
        --image) image="$2"; shift 2 ;;
        --onstart) onstart="$2"; shift 2 ;;
        --disk-gb) disk="$2"; shift 2 ;;
        --key) key="$2"; shift 2 ;;
        *) echo "vast-gpu up: unknown flag $1" >&2; exit 2 ;;
      esac
    done
    [ -n "$ask" ] && [ -n "$image" ] && [ -n "$onstart" ] && [ -n "$disk" ] \
      || { echo "usage: vast-gpu up --ask <offer-id> --image <img> --onstart <file> --disk-gb <n> [--key <pubkey>]" >&2; exit 2; }
    [ -f "$key" ] || { echo "vast-gpu: public key missing: $key" >&2; exit 2; }
    [ -f "$onstart" ] || { echo "vast-gpu: onstart file missing: $onstart" >&2; exit 2; }
    label="forgia-x-$(openssl rand -hex 16)"
    echo "label: $label  (record it; it is the only handle that reconciles this rental)"
    d="$(evidence_dir allocate)"; echo "evidence: $d"
    # A quoted "${VAR:+...}" expands to one EMPTY argument when VAR is unset, which
    # the executor rejects as an unknown flag before anything reaches the provider.
    # An array is the only form that omits the flag entirely.
    extra=()
    [ -n "${VAST_NOT_AFTER:-}" ] && extra=(--not-after-epoch-ms "$VAST_NOT_AFTER")
    set +e
    "$EXEC" vast allocate --ask "$ask" --label "$label" --image "$image" --disk-gb "$disk" \
      --onstart-file "$onstart" --evidence-dir "$d" --token-file "$(token_file)" ${extra[@]+"${extra[@]}"}
    code=$?
    set -e
    echo "allocate exit $code: $(explain "$code")"
    [ $code -eq 0 ] || exit $code
    iid="$(python3 - "$d" <<'PY'
import json, glob, os, sys
d = sys.argv[1]
for p in glob.glob(os.path.join(d, "*.json")):
    try: o = json.load(open(p))
    except Exception: continue
    def find(o):
        if isinstance(o, dict):
            if "instance_id" in o and o["instance_id"]: return o["instance_id"]
            for v in o.values():
                r = find(v)
                if r: return r
        elif isinstance(o, list):
            for v in o:
                r = find(v)
                if r: return r
    r = find(o)
    if r:
        print(r); sys.exit(0)
PY
)"
    [ -n "$iid" ] || { echo "vast-gpu: allocate succeeded but no instance id found; read $d" >&2; exit 2; }
    echo "instance: $iid"
    mkdir -p "$ROOT/labels"
    printf '%s %s %s\n' "$label" "$iid" "$(stamp)" > "$ROOT/labels/$label.txt"
    [ -d "$d" ] && printf '%s %s\n' "$label" "$iid" > "$d/label.txt"
    run "attach-ssh-$iid" vast attach-ssh --instance-id "$iid" --public-key-file "$key"
    echo "next: vast-gpu where $iid"
    ;;
  where|ssh)
    [ $# -ge 2 ] || { echo "usage: vast-gpu where <instance-id>" >&2; exit 2; }
    d="$(evidence_dir "instance-$2")"
    echo "evidence: $d"
    set +e
    "$EXEC" vast instance --id "$2" --evidence-dir "$d" --token-file "$(token_file)" >/dev/null
    code=$?
    set -e
    echo "instance read exit $code: $(explain "$code")"
    rc=0; coords="$(ssh_coords "$d")" || rc=$?
    [ $rc -eq 0 ] || { echo "vast-gpu: $coords" >&2; exit 2; }
    set -- $coords
    echo "status: $3"
    echo "ssh -p $2 root@$1"
    ;;
  logs)
    [ $# -ge 2 ] || { echo "usage: vast-gpu logs <instance-id> [--tail n]" >&2; exit 2; }
    run "logs-$2" vast logs --instance-id "$2" --print "${@:3}"
    ;;
  burn)
    # burn <instance-id> --yes [--label <label>]
    [ $# -ge 2 ] || { echo "usage: vast-gpu burn <instance-id> --yes" >&2; exit 2; }
    iid="$2"; confirmed=""
    for a in "$@"; do [ "$a" = "--yes" ] && confirmed=1; done
    [ -n "$confirmed" ] || { echo "vast-gpu burn: refusing without --yes (destructive, irreversible)" >&2; exit 2; }
    run "destroy-$iid" vast destroy --instance-id "$iid"
    echo
    echo "disposal is only proven by absence from a complete inventory:"
    run "inventory-after-$iid" vast inventory
    ;;
  last)
    # `ls … | head` takes SIGPIPE under `set -o pipefail` and reports 1, which
    # reads as a failure of a read-only listing. Bound the loop instead.
    n="${2:-10}"
    [ -d "$ROOT" ] || { echo "vast-gpu: no evidence root yet: $ROOT" >&2; exit 2; }
    i=0
    for d in $(ls -1t "$ROOT" 2>/dev/null); do
      [ "$i" -lt "$n" ] || break
      printf '%s\n' "$d"
      i=$((i + 1))
    done
    ;;
  path)
    echo "$EXEC"
    ;;
  help)
    cat <<'USAGE'
vast-gpu: local Vast client (wraps forge/torchio executor/target/release/forgia-executor)

  vast-gpu inventory                 what is actually running, owner-scoped
  vast-gpu instance <id>             one read-only instance read
  vast-gpu offers [flags]            read-only market scan (passes flags through)
  vast-gpu preflight <plan.json>     admit a plan against the live market
  vast-gpu up --ask <offer-id> --image <img> --onstart <file> --disk-gb <n>
  vast-gpu where <id>                status plus the ssh command
  vast-gpu logs <id> [--tail n]
  vast-gpu burn <id> --yes           destroy, then prove absence
  vast-gpu last [n]                  recent evidence directories
  vast-gpu path                      resolved executor binary

Policy: no retries, no fallbacks, every call writes a fresh evidence directory.
Ambiguous destroy (exit 5) is never re-sent; reconcile with inventory instead.
USAGE
    ;;
  *)
    echo "vast-gpu: unknown verb '$1'" >&2
    echo "run 'vast-gpu help' for the verb list" >&2
    exit 2
    ;;
esac