#!/usr/bin/env bash
# ssh deadlines, ControlMaster self-heal and the host-unhealthy breaker.
# A wedged mux is simulated with an ssh shim: muxed calls hang, a direct
# (ControlMaster=no) call answers unless the host is also marked down.
set -uo pipefail

GATE="$HOME/.claude/bin/local-gate"
TMP=$(mktemp -d "$HOME/.cache/rb-transport-XXXXXX")
trap 'rm -rf "$TMP"' EXIT
PASS=0; FAIL=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=$want got=$got extra=$extra)"; FAIL=$((FAIL+1))
  fi
}

RUNNER_HASH=$(sha256sum "$HOME/.claude/lib/remote-runner.sh" | awk '{print $1}')
BIN="$TMP/bin"; mkdir -p "$BIN"
cat > "$BIN/ssh" <<EOF
#!/usr/bin/env bash
printf '%s\n' "\$*" >> "$TMP/ssh.log"
case "\$*" in
  *"-O exit"*) printf 'reset\n' >> "$TMP/reset.log"; rm -f "$TMP/wedged"; exit 0 ;;
  *"ControlMaster=no"*) if [ -f "$TMP/hostdown" ]; then sleep 30; fi; exit 0 ;;
esac
if [ -f "$TMP/wedged" ]; then sleep 30; fi
case "\$*" in
  *sha256sum*) printf '%s  .rb/runner.sh\n' "$RUNNER_HASH"; exit 0 ;;
esac
exit 0
EOF
chmod +x "$BIN/ssh"
export PATH="$BIN:$PATH"

CFG_JS='{ host: "rb-fake-box", port: 22, ssh_user: "u", remote_root: "/tmp/rb-fake", health_ttl_sec: 30, connect_timeout_sec: 2, ssh_probe_timeout_sec: 2, ssh_exec_timeout_sec: 5 }'
cat > "$TMP/ready.mjs" <<JS
const rb = await import(process.env.HOME + "/.claude/lib/remote-build.mjs");
const cfg = $CFG_JS;
const lines = [];
const ok = rb.remoteReady(cfg, process.argv[2], (m) => lines.push(m));
process.stdout.write(\`ready=\${ok} log=\${lines.join("|")}\n\`);
JS
cat > "$TMP/trip.mjs" <<JS
const rb = await import(process.env.HOME + "/.claude/lib/remote-build.mjs");
const cfg = $CFG_JS;
const lines = [];
rb.markHostUnhealthy(cfg, process.argv[2], "wedged", (m) => lines.push(m));
const ok = rb.remoteReady(cfg, process.argv[2], (m) => lines.push(m));
process.stdout.write(\`ready=\${ok} log=\${lines.join("|")}\n\`);
JS

# wedged master, host answers a direct connection: reset it and retry
touch "$TMP/wedged"; rm -f "$TMP/hostdown" "$TMP/reset.log" "$TMP/ssh.log"
start=$SECONDS
out=$(node "$TMP/ready.mjs" "$TMP/state-heal")
elapsed=$((SECONDS - start))
healed=1; [ -f "$TMP/reset.log" ] && healed=0
bounded=1; [ "$elapsed" -lt 15 ] && bounded=0
case "$out" in ready=true*) reported=0 ;; *) reported=1 ;; esac
check "wedged-mux-heals-and-retries" 0 "$((healed || bounded || reported))"

# host itself unreachable: never kill a master other jobs may be using
touch "$TMP/wedged" "$TMP/hostdown"; rm -f "$TMP/reset.log"
out=$(node "$TMP/ready.mjs" "$TMP/state-down")
kept=0; [ -f "$TMP/reset.log" ] && kept=1
case "$out" in ready=false*) reported=0 ;; *) reported=1 ;; esac
cached=1; grep -q '"ok":false' "$TMP"/state-down/remote-health-*.json 2>/dev/null && cached=0
check "host-down-keeps-master-and-caches-unhealthy" 0 "$((kept || reported || cached))"

# breaker rides the health cache: a tripped host is skipped without any ssh
rm -f "$TMP/wedged" "$TMP/hostdown"; rm -f "$TMP/ssh.log"
out=$(node "$TMP/trip.mjs" "$TMP/state-trip")
probed=0; [ -f "$TMP/ssh.log" ] && probed=1
case "$out" in ready=false*) reported=0 ;; *) reported=1 ;; esac
case "$out" in *"host tripped host=rb-fake-box reason=wedged"*) tripped=0 ;; *) tripped=1 ;; esac
case "$out" in *cached-unhealthy*) skipped=0 ;; *) skipped=1 ;; esac
check "tripped-host-skipped-without-ssh" 0 "$((probed || reported || tripped || skipped))"

# remote-only + wedged transport: bounded refusal (97), never a local build
F="$TMP/fixture"; mkdir -p "$F/c"
cat > "$F/package.json" <<'JSON'
{ "name": "rb-transport-fixture", "private": true }
JSON
cat > "$F/build.mjs" <<'JS'
import { mkdirSync, writeFileSync } from "node:fs";
mkdirSync("dist", { recursive: true });
writeFileSync("dist/out.txt", "built:local");
JS
cat > "$F/c/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": "$F/c/state" }
JSON
cat > "$F/c/remote.json" <<'JSON'
{ "enabled": true, "hosts": ["rb-fake-box"], "port": 22, "ssh_user": "u", "remote_root": "/tmp/rb-fake",
  "health_ttl_sec": 1, "connect_timeout_sec": 2, "ssh_probe_timeout_sec": 2, "grace_window_sec": 2,
  "reconnect_interval_sec": 1, "local_fallback": false, "remote_wait_sec": 1 }
JSON
touch "$TMP/wedged" "$TMP/hostdown"
start=$SECONDS
( cd "$F" && LOCAL_GATE_CONFIG="$F/c/local-gate.json" BUILD_REMOTE_CONFIG="$F/c/remote.json" \
  "$GATE" --key rbtest-wedged -- node build.mjs ) >/dev/null 2>&1
rc=$?
elapsed=$((SECONDS - start))
no_local=0; [ -f "$F/dist/out.txt" ] && no_local=1
bounded=1; [ "$elapsed" -lt 60 ] && bounded=0
check "remote-only-wedged-transport-refuses-local" 97 "$rc" "$((no_local || bounded))"

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