#!/usr/bin/env bash
# buildbox audit|bootstrap keep every pre-existing check and additionally run the fleet
# engine. No real buildbox is contacted: the registry, the fleet declaration, the ssh
# binary and the engine module are all fixtures under $TMP.
set -euo pipefail

ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/../../.." && pwd)"
BUILDBOX="$ROOT/modules/buildbox/bin/buildbox"
TMP="$(mktemp -d)"
trap 'rm -rf "$TMP"' EXIT

pass=0
fail=0
ok() { pass=$((pass + 1)); printf 'PASS %s\n' "$1"; }
bad() { fail=$((fail + 1)); printf 'FAIL %s\n' "$1"; }

# ---------------------------------------------------------------- fixtures

write_registry() { # extra-host-name|""
  local extra="$1" extra_json=""
  if [ -n "$extra" ]; then
    extra_json=$(cat <<EOF
,
    {
      "name": "$extra",
      "ssh_alias": "$extra",
      "state": "reachable",
      "machine_id": null,
      "roles": ["builder"],
      "access": {
        "lan": null,
        "tailscale_ip": { "host": "127.0.0.1", "port": 1, "user": "test", "identity_file": null },
        "tailscale_ssh": null
      },
      "rustdesk": null,
      "notes": "$extra fixture"
    }
EOF
)
  fi
  cat >"$TMP/buildbox-hosts.json" <<EOF
{
  "schema_version": 1,
  "hosts": [
    {
      "name": "stubbox",
      "ssh_alias": "stubbox",
      "state": "reachable",
      "machine_id": null,
      "roles": ["builder"],
      "access": {
        "lan": null,
        "tailscale_ip": { "host": "127.0.0.1", "port": 1, "user": "test", "identity_file": null },
        "tailscale_ssh": null
      },
      "rustdesk": null,
      "notes": "stubbox fixture"
    }$extra_json
  ],
  "orders": {
    "build": ["stubbox"]
  }
}
EOF
}

write_fleet() {
  cat >"$TMP/fleet.json" <<'EOF'
{
  "schema_version": 1,
  "nodes": {
    "workstation": {
      "transport": "local",
      "roles": ["control"],
      "profiles": ["workstation"],
      "execution": "last-resort"
    },
    "stubbox": {
      "transport": "ssh",
      "host_ref": "stubbox",
      "roles": ["builder"],
      "profiles": ["buildbox"],
      "execution": "normal"
    }
  },
  "fallback": {
    "enabled": true,
    "node": "workstation",
    "requires_all_unavailable": ["stubbox"],
    "max_concurrent_local_jobs": 1,
    "activation_windows": 3,
    "health_window_sec": 60,
    "lease_ttl_sec": 900
  }
}
EOF
}

# The stub engine keeps the real report formatting and exit-code aggregation and replaces
# only the two calls that would touch a box. Its verdict is backed by a file on disk, so a
# converge that claims to have applied an item is observable by the audit that follows it.
write_stub_engine() {
  cat >"$TMP/stub-engine.mjs" <<EOF
import { appendFileSync, readFileSync, writeFileSync } from "node:fs";
import * as real from "$ROOT/lib/fleet/engine.mjs";

export const formatAuditReport = real.formatAuditReport;
export const printAuditReport = real.printAuditReport;
export const aggregateExitCode = real.aggregateExitCode;
export const computeItemsRevision = real.computeItemsRevision;

const specPath = process.env.STUB_ENGINE_SPEC;
const logPath = process.env.STUB_ENGINE_LOG;

function readSpec() {
  return JSON.parse(readFileSync(specPath, "utf8"));
}

function record(call, nodeName, items) {
  appendFileSync(
    logPath,
    JSON.stringify({ call, node: nodeName, items: items.length, first: items[0]?.id ?? null }) + "\n",
  );
}

export async function auditNode(nodeName, items) {
  record("audit", nodeName, items);
  const drift = readSpec().drift;
  return {
    node: nodeName,
    revision: real.computeItemsRevision(items),
    ok: drift.length === 0,
    drift,
    unreachable: false,
    delegated: null,
  };
}

export async function convergeNode(nodeName, items) {
  record("converge", nodeName, items);
  const spec = readSpec();
  const changed = spec.drift.map((entry) => entry.id);
  writeFileSync(specPath, JSON.stringify({ ...spec, drift: [] }));
  const finalAudit = await auditNode(nodeName, items);
  return { node: nodeName, revision: finalAudit.revision, changed, failed: [], finalAudit };
}
EOF
}

write_spec() { # json-array-of-drift-entries
  printf '{"drift":%s}\n' "$1" >"$TMP/engine-spec.json"
}

DRIFT_ID='buildbox:user-config:bin/ci-scratch-prune.sh'
write_drift_spec() {
  write_spec "[{\"id\":\"$DRIFT_ID\",\"field\":\"digest\",\"want\":\"want-sha\",\"have\":\"have-sha\"}]"
}

# Every remote assertion the pre-existing checks make is answered here, so a nonzero exit
# in these runs can only come from the engine pass.
write_fake_ssh() {
  mkdir -p "$TMP/bin"
  cat >"$TMP/bin/ssh" <<'EOF'
#!/usr/bin/env bash
printf 'SSH %s\n' "$*" >>"${FAKE_SSH_LOG:?}"
command=${!#}
if [[ "$command" == *'tar -xzf'* || "$command" == CLAUDE_HOME_ENTRIES=* ]]; then
  HOME="${FAKE_REMOTE_HOME:?}" bash -c "$command"
  exit $?
fi
cat >>"${FAKE_SSH_LOG}.stdin"
exit "${FAKE_SSH_RC:-0}"
EOF
  chmod +x "$TMP/bin/ssh"
}

# claude_home_stage runs on the workstation for every non-harden verb. It reads the
# manifest, so a fixture manifest over a fixture tree keeps this run off the real ~/.claude.
write_claude_fixture() {
  mkdir -p "$TMP/claude-src/bin" "$TMP/claude-src/lib"
  printf '{}\n' >"$TMP/claude-src/settings.json"
  printf '#!/usr/bin/env bash\necho 1.9.9\n' >"$TMP/claude-src/bin/bun"
  printf '#!/usr/bin/env bash\nexit 0\n' >"$TMP/claude-src/bin/_agent-build-scope"
  printf '# fixture\n' >"$TMP/claude-src/lib/confine.sh"
  chmod +x "$TMP/claude-src/bin/bun" "$TMP/claude-src/bin/_agent-build-scope"
  cat >"$TMP/claude-manifest.json" <<'EOF'
{
  "entries": [
    { "path": "settings.json", "source": "repo" },
    { "path": "bin", "source": "repo" },
    { "path": "lib", "source": "repo" }
  ]
}
EOF
}

write_build_remote() {
  cat >"$TMP/build-remote.json" <<'EOF'
{ "enabled": false }
EOF
}

# The run below is env -i, so the interpreters buildbox shells out to must be on the PATH
# it is given. A gated run installs node outside /usr/bin, so its directory is appended.
NODE_DIR="$(dirname "$(command -v node)")"
SANDBOX_PATH="$TMP/bin:/usr/bin:/bin:$NODE_DIR"

mkdir -p "$TMP/home" "$TMP/remote-home"
write_fleet
write_stub_engine
write_fake_ssh
write_claude_fixture
write_build_remote

run_buildbox() { # verb host... -> stdout+stderr in $TMP/out, rc in $rc
  : >"$TMP/ssh.log"
  : >"$TMP/ssh.log.stdin"
  : >"$TMP/engine.log"
  set +e
  env -i \
    PATH="$SANDBOX_PATH" \
    HOME="$TMP/home" \
    TERM=dumb \
    FAKE_SSH_LOG="$TMP/ssh.log" \
    FAKE_SSH_RC="${FAKE_SSH_RC:-0}" \
    FAKE_REMOTE_HOME="$TMP/remote-home" \
    BUILDBOX_HOSTS_CONFIG="$TMP/buildbox-hosts.json" \
    BUILDBOX_CONFIG="$TMP/build-remote.json" \
    BUILDBOX_FLEET_ENGINE="$TMP/stub-engine.mjs" \
    DECKCTL_FLEET_FILE="$TMP/fleet.json" \
    STUB_ENGINE_SPEC="$TMP/engine-spec.json" \
    STUB_ENGINE_LOG="$TMP/engine.log" \
    CLAUDE_MANIFEST="$TMP/claude-manifest.json" \
    CLAUDE_REPO_ROOT="$TMP/claude-src" \
    "$BUILDBOX" "$@" >"$TMP/out" 2>&1
  rc=$?
  set -e
}

# ------------------------------------------------- 1. engine drift alone fails the audit

write_registry ""
write_drift_spec
run_buildbox audit stubbox
if [ "$rc" -ne 0 ] \
  && grep -q "ENGINE DRIFT stubbox $DRIFT_ID digest" "$TMP/out" \
  && grep -q 'bash -s audit' "$TMP/ssh.log"; then
  ok 'engine drift alone exits nonzero while every legacy check passes'
else
  bad "engine drift alone exits nonzero while every legacy check passes (rc=$rc)"
fi
if grep -q '"call":"audit","node":"stubbox"' "$TMP/engine.log" \
  && ! grep -q '"items":0' "$TMP/engine.log"; then
  ok 'the engine pass receives the fleet node resolved from host_ref, with expanded items'
else
  bad "the engine pass receives the fleet node resolved from host_ref, with expanded items ($(tr '\n' ' ' <"$TMP/engine.log"))"
fi

# ------------------------------------------------- 2. clean engine + clean legacy exits 0

write_spec '[]'
run_buildbox audit stubbox
if [ "$rc" -eq 0 ] && grep -q '^ENGINE OK stubbox$' "$TMP/out"; then
  ok 'a clean engine expansion and clean legacy checks exit 0'
else
  bad "a clean engine expansion and clean legacy checks exit 0 (rc=$rc)"
fi
# probe_doors reports the fixture doors shut. Those lines have never fed the exit code and
# must not start to: only a check or the engine may fail a host.
if grep -q '^DRIFT tailscale_ip 127.0.0.1:1 unreachable' "$TMP/out" && [ "$rc" -eq 0 ]; then
  ok 'door probe lines still print without changing the exit code'
else
  bad 'door probe lines still print without changing the exit code'
fi
if grep -q 'bash -s audit' "$TMP/ssh.log" && grep -q '^== stubbox (audit)$' "$TMP/out"; then
  ok 'the pre-existing remote check still runs on a clean host'
else
  bad 'the pre-existing remote check still runs on a clean host'
fi

# ------------------------------------------------- 3. the engine pass cannot mask a legacy failure

write_spec '[]'
FAKE_SSH_RC=1 run_buildbox audit stubbox
if [ "$rc" -ne 0 ] && grep -q '^ENGINE OK stubbox$' "$TMP/out"; then
  ok 'a failing legacy check still exits nonzero when the engine is clean'
else
  bad "a failing legacy check still exits nonzero when the engine is clean (rc=$rc)"
fi

# ------------------------------------------------- 4. bootstrap converges, then observes it

write_drift_spec
run_buildbox bootstrap stubbox
applied_then_audited=0
if [ "$(grep -c '"call":"converge"' "$TMP/engine.log")" = 1 ] \
  && [ "$(head -1 "$TMP/engine.log" | grep -c '"call":"converge"')" = 1 ] \
  && tail -1 "$TMP/engine.log" | grep -q '"call":"audit"'; then
  applied_then_audited=1
fi
if [ "$rc" -eq 0 ] \
  && [ "$applied_then_audited" = 1 ] \
  && grep -q "^ENGINE APPLY stubbox $DRIFT_ID$" "$TMP/out" \
  && grep -q '^ENGINE OK stubbox$' "$TMP/out" \
  && grep -q '"drift":\[\]' "$TMP/engine-spec.json"; then
  ok 'bootstrap converges the drifted item and its audit pass observes the applied state'
else
  bad "bootstrap converges the drifted item and its audit pass observes the applied state (rc=$rc)"
fi
if grep -q 'mkdir -p ~/.claude' "$TMP/ssh.log" && grep -qE '^(OK|PUSH) +claude-home' "$TMP/out"; then
  ok 'bootstrap still performs its pre-existing step: push_claude_home'
else
  bad 'bootstrap still performs its pre-existing step: push_claude_home'
fi
for probe in ci-scratch-prune.sh buildbox-build-cache-prune.sh buildbox-telemetry.sh 'bash -s bootstrap'; do
  if grep -q -- "$probe" "$TMP/ssh.log"; then
    ok "bootstrap still performs its pre-existing step: $probe"
  else
    bad "bootstrap still performs its pre-existing step: $probe"
  fi
done

# ------------------------------------------------- 5. a host no fleet node claims is drift

write_registry orphanbox
write_spec '[]'
run_buildbox audit orphanbox
if [ "$rc" -ne 0 ] && grep -q 'ENGINE FAIL orphanbox no fleet node declares host_ref "orphanbox"' "$TMP/out"; then
  ok 'a registry host no fleet node declares exits nonzero naming it'
else
  bad "a registry host no fleet node declares exits nonzero naming it (rc=$rc)"
fi
write_registry ""

# ------------------------------------------------- 6. no pre-existing check was removed

CLAUDE_HOME_LIB="$ROOT/modules/buildbox/lib/claude-home.sh"
for fn in probe_doors gc_sha is_lab_host lab_manifest push_scratch_prune \
  push_build_cache_prune push_telemetry_sampler push_dangerlab push_npmrc_token \
  push_gh_token push_claude_home boot_id_of wait_for_reboot stage_payload harden_host \
  check_host parity_host; do
  defs_here=$(grep -c "^${fn}() {" "$BUILDBOX" || true)
  defs_lib=$(grep -c "^${fn}() {" "$CLAUDE_HOME_LIB" || true)
  mentions=$(grep -ow "$fn" "$BUILDBOX" | wc -l)
  if [ $((defs_here + defs_lib)) -ge 1 ] && [ $((mentions - defs_here)) -ge 1 ]; then
    ok "pre-existing check still defined and invoked: $fn"
  else
    bad "pre-existing check still defined and invoked: $fn (defs=$((defs_here + defs_lib)) uses=$((mentions - defs_here)))"
  fi
done

printf '\n%s passed, %s failed\n' "$pass" "$fail"
[ "$fail" -eq 0 ]
