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

GATE="$HOME/.claude/bin/local-gate"
TMP=$(mktemp -d "$HOME/.cache/rb-test-XXXXXX")
trap 'rm -rf "$TMP"' EXIT
PASS=0; FAIL=0; SKIP=0

check() {
  local name="$1" want="$2" got="$3" extra="${4:-0}"
  if [ "$want" = "$got" ] && [ "$extra" = "0" ]; then
    echo "PASS $name"; PASS=$((PASS+1))
  else
    echo "FAIL $name (want rc=$want got rc=$got extra=$extra)"; FAIL=$((FAIL+1))
  fi
}
skip() { echo "SKIP $1 (debian1 unreachable)"; SKIP=$((SKIP+1)); }

mk_fixture() {
  local d="$1"
  mkdir -p "$d"
  cat > "$d/package.json" <<'JSON'
{ "name": "rb-fixture", "private": true }
JSON
  cat > "$d/build.mjs" <<'JS'
import { mkdirSync, writeFileSync } from "node:fs";
mkdirSync("dist", { recursive: true });
writeFileSync("dist/out.txt", `built:${process.env.RB_MARK ?? "x"}`);
JS
}

mk_cfg() {
  local sd="$1" host="$2"
  mkdir -p "$sd"
  cat > "$sd/local-gate.json" <<JSON
{ "min_slots": 1, "max_slots": 4, "load_per_slot": 2, "min_mem_available_gb": 1, "max_swap_used_gb": 128, "state_dir": "$sd/state" }
JSON
  cat > "$sd/remote.json" <<JSON
{ "enabled": true, "host": "$host", "port": 2222, "ssh_user": "user", "remote_root": "/home/user/builds",
  "identity_file": "~/.ssh/id_ed25519_buildbox",
  "health_ttl_sec": 0, "connect_timeout_sec": 2, "max_remote_jobs": 6, "grace_window_sec": 20,
  "reconnect_interval_sec": 1, "local_only": ["forced-local-marker"] }
JSON
}
BOX_SSH="ssh -F /dev/null -o BatchMode=yes -o ConnectTimeout=3 -o StrictHostKeyChecking=accept-new -o IdentitiesOnly=yes -i $HOME/.ssh/id_ed25519_buildbox -p 2222"

mk_cfg_remote_only() {
  local sd="$1" host="$2"
  mk_cfg "$sd" "$host"
  node -e "const f='$sd/remote.json',c=require(f);c.local_fallback=false;c.remote_wait_sec=1;c.health_ttl_sec=1;c.reconnect_interval_sec=1;require('fs').writeFileSync(f,JSON.stringify(c))"
}

F2="$TMP/fallback"; mk_fixture "$F2"; mk_cfg "$TMP/c-fallback" "203.0.113.1"
( cd "$F2" && RB_MARK=local LOCAL_GATE_CONFIG="$TMP/c-fallback/local-gate.json" BUILD_REMOTE_CONFIG="$TMP/c-fallback/remote.json" \
  "$GATE" --key rbtest-fallback -- node build.mjs )
rc=$?
grep -q "built:local" "$F2/dist/out.txt" 2>/dev/null; art=$?
grep -q "remote skip key=rbtest-fallback reason=unreachable-or-full" "$HOME/.claude/local-gate.log"; logged=$?
check "fallback-unreachable-runs-local" 0 "$rc" "$((art || logged))"

F3="$TMP/disabled"; mk_fixture "$F3"; mk_cfg "$TMP/c-disabled" "debian1"
sed -i 's/"enabled": true/"enabled": false/' "$TMP/c-disabled/remote.json"
( cd "$F3" && LOCAL_GATE_CONFIG="$TMP/c-disabled/local-gate.json" BUILD_REMOTE_CONFIG="$TMP/c-disabled/remote.json" \
  "$GATE" --key rbtest-disabled -- node build.mjs )
check "disabled-runs-local" 0 "$?"

F4="$TMP/localonly"; mk_fixture "$F4"; cp "$F4/build.mjs" "$F4/forced-local-marker.mjs"
mk_cfg "$TMP/c-localonly" "debian1"
( cd "$F4" && LOCAL_GATE_CONFIG="$TMP/c-localonly/local-gate.json" BUILD_REMOTE_CONFIG="$TMP/c-localonly/remote.json" \
  "$GATE" --key rbtest-localonly -- node forced-local-marker.mjs )
rc=$?
grep -q "remote skip key=rbtest-localonly reason=local_only" "$HOME/.claude/local-gate.log"; logged=$?
check "local-only-pattern" 0 "$rc" "$logged"

F6="$TMP/remoteonly"; mk_fixture "$F6"; mk_cfg_remote_only "$TMP/c-ronly" "203.0.113.1"
( cd "$F6" && LOCAL_GATE_CONFIG="$TMP/c-ronly/local-gate.json" BUILD_REMOTE_CONFIG="$TMP/c-ronly/remote.json" \
  "$GATE" --key rbtest-remoteonly -- node build.mjs ) 2>/dev/null
rc=$?
[ ! -f "$F6/dist/out.txt" ]; no_local=$?
check "remote-only-refuses-local" 97 "$rc" "$no_local"

if $BOX_SSH user@debian1 true >/dev/null 2>&1; then LIVE=1; else LIVE=0; fi
F1="$TMP/live"; mk_fixture "$F1"; mk_cfg "$TMP/c-live" "debian1"

if [ "$LIVE" -eq 1 ]; then
  ( cd "$F1" && LOCAL_GATE_CONFIG="$TMP/c-live/local-gate.json" BUILD_REMOTE_CONFIG="$TMP/c-live/remote.json" \
    "$GATE" --key rbtest-ok -- node build.mjs )
  rc=$?; [ -f "$F1/dist/out.txt" ]; art=$?
  grep -q "via=remote" "$HOME/.claude/local-gate.log"; via=$?
  check "remote-success-artifact-pullback" 0 "$rc" "$((art || via))"
else skip "remote-success-artifact-pullback"; fi

if [ "$LIVE" -eq 1 ]; then
  ( cd "$F1" && LOCAL_GATE_CONFIG="$TMP/c-live/local-gate.json" BUILD_REMOTE_CONFIG="$TMP/c-live/remote.json" \
    "$GATE" --key rbtest-rc3 -- node -e "process.exit(3)" )
  check "remote-exit-code-passthrough" 3 "$?"
else skip "remote-exit-code-passthrough"; fi

if [ "$LIVE" -eq 1 ]; then
  ( cd "$F1" && LOCAL_GATE_CONFIG="$TMP/c-live/local-gate.json" BUILD_REMOTE_CONFIG="$TMP/c-live/remote.json" \
    "$GATE" --key rbtest-rc255 -- node -e "process.exit(255)" )
  check "remote-255-maps-254" 254 "$?"
else skip "remote-255-maps-254"; fi

if [ "$LIVE" -eq 1 ]; then
  ( cd "$F1" && LOCAL_GATE_CONFIG="$TMP/c-live/local-gate.json" BUILD_REMOTE_CONFIG="$TMP/c-live/remote.json" \
    "$GATE" --key rbtest-dedupe -- node -e "setTimeout(()=>{},10000)" ) &
  bg=$!; sleep 3
  ( cd "$F1" && LOCAL_GATE_CONFIG="$TMP/c-live/local-gate.json" BUILD_REMOTE_CONFIG="$TMP/c-live/remote.json" \
    "$GATE" --key rbtest-dedupe -- node -e "setTimeout(()=>{},10000)" ) > "$TMP/dedupe.out"
  rc=$?; grep -q "deduped key=rbtest-dedupe" "$TMP/dedupe.out"; deduped=$?
  kill "$bg" 2>/dev/null; wait "$bg" 2>/dev/null
  check "dedupe-remote" 0 "$rc" "$deduped"
else skip "dedupe-remote"; fi

if [ "$LIVE" -eq 1 ]; then
  watcher="$TMP/watcher.pid"
  ( cd "$F1" && BUILD_REMOTE_WATCHER_PID_FILE="$watcher" BUILD_REMOTE_WATCHER_HOLD_SEC=5 LOCAL_GATE_CONFIG="$TMP/c-live/local-gate.json" BUILD_REMOTE_CONFIG="$TMP/c-live/remote.json" \
    "$GATE" --key rbtest-blip -- node -e "setTimeout(()=>process.exit(3),8000)" ) &
  gate_pid=$!
  for _ in $(seq 1 20); do [ -s "$watcher" ] && break; sleep 1; done
  if [ -s "$watcher" ]; then kill -9 "$(cat "$watcher")" 2>/dev/null; fi
  wait "$gate_pid"; rc=$?
  check "reattach-after-watcher-kill" 3 "$rc" "$([ -s "$watcher" ]; echo $?)"
else skip "reattach-after-watcher-kill"; fi

if [ "$LIVE" -eq 1 ]; then
  rm -rf "$F1/dist"
  long_key=rbtest-orphan-old
  job_id=$(cd "$F1" && node --input-type=module -e "import {deriveJobId,mirrorName} from '$HOME/.claude/lib/remote-build.mjs'; process.stdout.write(deriveJobId('$long_key',mirrorName(process.cwd())))")
  ( cd "$F1" && LOCAL_GATE_CONFIG="$TMP/c-live/local-gate.json" BUILD_REMOTE_CONFIG="$TMP/c-live/remote.json" \
    "$GATE" --key "$long_key" -- node -e "if(require('os').hostname().startsWith('debian'))setTimeout(()=>{require('fs').mkdirSync('dist',{recursive:true});require('fs').writeFileSync('dist/stale.txt','stale')},15000)" ) &
  old_gate=$!
  for _ in $(seq 1 20); do $BOX_SSH user@debian1 "systemctl --user is-active --quiet rb-$job_id.service" >/dev/null 2>&1 && break; sleep 1; done
  ( cd "$F1" && LOCAL_GATE_CONFIG="$TMP/c-live/local-gate.json" BUILD_REMOTE_CONFIG="$TMP/c-live/remote.json" \
    "$GATE" --key rbtest-orphan-new -- node build.mjs )
  rc=$?
  $BOX_SSH user@debian1 "systemctl --user is-active --quiet rb-$job_id.service" >/dev/null 2>&1; active=$?
  [ ! -f "$F1/dist/stale.txt" ]; stale_absent=$?
  wait "$old_gate" 2>/dev/null
  [ "$active" -ne 0 ]; stopped=$?
  check "epoch-fences-orphan-and-blocks-stale-pull" 0 "$rc" "$((stopped || stale_absent))"
else skip "epoch-fences-orphan-and-blocks-stale-pull"; fi

if [ "$LIVE" -eq 1 ]; then
  F5="$TMP/gitpath"; mk_fixture "$F5"; mk_cfg "$TMP/c-git" "debian1"
  cat > "$F5/build.mjs" <<'JS'
import { mkdirSync, writeFileSync, readFileSync } from "node:fs";
mkdirSync("dist", { recursive: true });
writeFileSync("dist/out.txt", `tracked:${readFileSync("tracked.txt","utf8")} env:${readFileSync(".dev.vars","utf8")} new:${readFileSync("new.txt","utf8")}`);
JS
  ( cd "$F5" \
    && git init -q && git config user.email t@t && git config user.name t \
    && printf 'dist/\n.dev.vars\n' > .gitignore && printf v1 > tracked.txt \
    && git add -A && git commit -qm base \
    && printf v2 > tracked.txt && printf untracked > new.txt && printf secret > .dev.vars )
  ( cd "$F5" && LOCAL_GATE_CONFIG="$TMP/c-git/local-gate.json" BUILD_REMOTE_CONFIG="$TMP/c-git/remote.json" \
    "$GATE" --key rbtest-gitpath -- node build.mjs )
  rc=$?
  grep -q "^tracked:v2 env:secret new:untracked$" "$F5/dist/out.txt" 2>/dev/null; content=$?
  mirror=$(cd "$F5" && node --input-type=module -e "import {mirrorName} from '$HOME/.claude/lib/remote-build.mjs'; process.stdout.write(mirrorName(process.cwd()))")
  $BOX_SSH user@debian1 "git -C /home/user/builds/$mirror rev-parse --git-dir" >/dev/null 2>&1; is_git=$?
  check "git-path-snapshot-overlay-materialize" 0 "$rc" "$((content || is_git))"
else skip "git-path-snapshot-overlay-materialize"; fi

if [ "$LIVE" -eq 1 ]; then
  F5="$TMP/lockfile"; mk_fixture "$F5"
  sed -i 's/"private": true/"private": true, "dependencies": { "ms": "2.1.3" }/' "$F5/package.json"
  ( cd "$F5" && pnpm install --silent >/dev/null 2>&1 )
  if [ -f "$F5/pnpm-lock.yaml" ]; then
    ( cd "$F5" && LOCAL_GATE_CONFIG="$TMP/c-live/local-gate.json" BUILD_REMOTE_CONFIG="$TMP/c-live/remote.json" \
      "$GATE" --key rbtest-lockfile -- node build.mjs )
    rc=$?; [ -f "$F5/dist/out.txt" ]; art=$?
    check "pnpm-lockfile-remote-install" 0 "$rc" "$art"
  else
    echo "SKIP pnpm-lockfile-remote-install (local lockfile unavailable)"; SKIP=$((SKIP+1))
  fi
else skip "pnpm-lockfile-remote-install"; fi

echo
echo "passed=$PASS failed=$FAIL skipped=$SKIP"
[ "$FAIL" -eq 0 ]
