#!/usr/bin/env bash
set -euo pipefail

MOD="$(cd -P "$(dirname "$(readlink -f "${BASH_SOURCE[0]}")")/.." && pwd)"
PAYLOAD="$MOD/host-config/scratch-bind.sh"
REGISTRY_CLI="$MOD/../workstation/claude/lib/buildbox-registry.mjs"
REGISTRY="${BUILDBOX_HOSTS_CONFIG:-$HOME/.claude/buildbox-hosts.json}"
RESERVE_BYTES=$((10 * 1024 * 1024 * 1024))

usage() { echo "usage: buildbox-scratch-bind [host...]" >&2; exit 2; }
[ "${1:-}" != --help ] || usage
[ -r "$PAYLOAD" ] || { echo "buildbox-scratch-bind: missing $PAYLOAD" >&2; exit 2; }
[ -r "$REGISTRY" ] || { echo "buildbox-scratch-bind: registry missing: $REGISTRY" >&2; exit 2; }

reg() { BUILDBOX_HOSTS_CONFIG="$REGISTRY" CPU_GUARD_ACTIVE=1 /usr/bin/node "$REGISTRY_CLI" "$@"; }
if [ "$#" -gt 0 ]; then
  hosts_csv=$(reg check "$(IFS=,; echo "$*")") || { echo "buildbox-scratch-bind: invalid host selection" >&2; exit 2; }
else
  hosts_csv=$(reg hosts --all) || { echo "buildbox-scratch-bind: registry unusable" >&2; exit 2; }
fi
IFS=, read -r -a hosts <<<"$hosts_csv"
[ "${#hosts[@]}" -gt 0 ] || { echo "buildbox-scratch-bind: no reachable hosts" >&2; exit 2; }

ssh_host() {
  local host="$1" target port ident
  read -r target port ident < <(reg access "$host" --path tailscale_ip)
  [ "$ident" = - ] && ident=""
  ident="${ident/#\~/$HOME}"
  ssh -F /dev/null -p "$port" ${ident:+-i "$ident"} -o BatchMode=yes -o ConnectTimeout=8 "$target" "${@:2}"
}

preflight_host() {
  local host="$1"
  ssh_host "$host" bash -s -- "$RESERVE_BYTES" <<'REMOTE'
set -euo pipefail
reserve="$1"
[ "$(findmnt -no TARGET --mountpoint /var/lib/buildbox)" = /var/lib/buildbox ]
[ "$(findmnt -no FSTYPE --mountpoint /var/lib/buildbox)" = ext4 ]
source=$(findmnt -no SOURCE --mountpoint /var/lib/buildbox)
[ "$(lsblk -no LABEL "$source")" = buildbox-scratch ]
required=0
for target in /home/user/runs /home/user/.cache/ms-playwright /home/user/actions-runner-*/_work; do
  [ -e "$target" ] || continue
  [ ! -L "$target" ] || { echo "REFUSE symlink $target" >&2; exit 1; }
  mountpoint -q "$target" && continue
  bytes=$(du -sx -B1 "$target" | cut -f1)
  required=$((required + bytes))
done
avail=$(df -B1 --output=avail /var/lib/buildbox | tail -1 | tr -dc '0-9')
[ "$avail" -ge $((required + reserve)) ] || {
  echo "REFUSE scratch needs=$required reserve=$reserve available=$avail" >&2; exit 1;
}
[ ! -e /home/user/runs.migrated ] || echo "RESUME /home/user/runs.migrated"
[ ! -e /home/user/.cache/ms-playwright.migrated ] || echo "RESUME /home/user/.cache/ms-playwright.migrated"
printf 'PREFLIGHT required=%s available=%s reserve=%s\n' "$required" "$avail" "$reserve"
REMOTE
}

verify_host() {
  local host="$1"
  ssh_host "$host" bash -s <<'REMOTE'
set -euo pipefail
for target in /home/user/runs /home/user/.cache/ms-playwright /home/user/actions-runner-*/_work; do
  [ -d "$target" ] || continue
  source=$(findmnt -no SOURCE --mountpoint "$target")
  case "$source" in /dev/sda1\[*\]|/var/lib/buildbox/*) ;; *) echo "BAD $target source=$source" >&2; exit 1;; esac
  [ ! -e "$target.migrated" ] || { echo "BAD stash remains $target.migrated" >&2; exit 1; }
  echo "BOUND $target source=$source"
done
df -h / /var/lib/buildbox
REMOTE
}

apply_host() {
  local host="$1"
  ssh_host "$host" 'set -euo pipefail
umask 077
tmp=$(mktemp /tmp/buildbox-scratch-bind.XXXXXX)
cleanup() { rm -f -- "$tmp"; }
cleanup_and_exit() { cleanup; exit 1; }
trap cleanup EXIT
trap cleanup_and_exit HUP INT TERM
cat >"$tmp"
sudo -n install -m 0755 -- "$tmp" /usr/local/sbin/buildbox-scratch-bind-apply
sudo -n /usr/local/sbin/buildbox-scratch-bind-apply apply' <"$PAYLOAD"
}

for host in "${hosts[@]}"; do
  echo "=== PREFLIGHT $host ==="
  preflight_host "$host"
done

echo "=== PREFLIGHT COMPLETE: ${#hosts[@]} hosts; applying sequentially ==="
for host in "${hosts[@]}"; do
  echo "=== APPLY $host ==="
  apply_host "$host" || {
    echo "buildbox-scratch-bind: stopped at $host; rerun this command after correcting the reported failure" >&2
    exit 1
  }
  echo "=== VERIFY $host ==="
  verify_host "$host"
done

echo "buildbox-scratch-bind: all ${#hosts[@]} hosts converged"
