#!/usr/bin/env bash
# The telemetry producer owns sampling on the box and publishes one complete JSON summary.
set -euo pipefail

REPO="$(cd "$(dirname "${BASH_SOURCE[0]}")/../../.." && pwd)"
SAMPLER="$REPO/modules/buildbox/user-config/bin/buildbox-telemetry.sh"
WORK="$(mktemp -d)"
trap 'rm -rf "$WORK"' EXIT

fail() { printf 'FAIL %s\n' "$1" >&2; exit 1; }

# A fixture kernel tree: two thermal zones where the hotter zone is not the package one, a
# passive trip below the current reading, and a critical trip that is the only ceiling.
FIX="$WORK/fixture"
mkdir -p "$FIX/proc/net" "$FIX/proc/pressure" "$FIX/cgroup" \
  "$FIX/sys/class/thermal/thermal_zone0" "$FIX/sys/class/thermal/thermal_zone1"
printf '0.42 0.31 0.20 1/500 9999\n' > "$FIX/proc/loadavg"
printf 'MemTotal:       16000000 kB\nMemAvailable:    6000000 kB\nSwapTotal:       4000000 kB\nSwapFree:        3000000 kB\n' > "$FIX/proc/meminfo"
printf 'cpu  1 1 1 1 1 1 1 1\ncpu0 100 0 100 800 0 0 0 0\ncpu1 200 0 200 600 0 0 0 0\n' > "$FIX/proc/stat"
printf 'Inter-|   Receive\n face |bytes\n  lo: 5 0 0 0 0 0 0 0 5 0\n eth0: 1000 0 0 0 0 0 0 0 2000 0\n' > "$FIX/proc/net/dev"
printf 'some avg10=0.00 avg60=1.25 avg300=0.10 total=1\nfull avg10=0.00 avg60=0.50 avg300=7.25 total=1\n' > "$FIX/proc/pressure/memory"
printf '52000\n' > "$FIX/sys/class/thermal/thermal_zone0/temp"
printf 'x86_pkg_temp\n' > "$FIX/sys/class/thermal/thermal_zone0/type"
printf '40000\n' > "$FIX/sys/class/thermal/thermal_zone0/trip_point_0_temp"
printf 'passive\n' > "$FIX/sys/class/thermal/thermal_zone0/trip_point_0_type"
printf '89000\n' > "$FIX/sys/class/thermal/thermal_zone0/trip_point_1_temp"
printf 'critical\n' > "$FIX/sys/class/thermal/thermal_zone0/trip_point_1_type"
printf '61000\n' > "$FIX/sys/class/thermal/thermal_zone1/temp"
printf 'acpitz\n' > "$FIX/sys/class/thermal/thermal_zone1/type"

# A box with no session ledger reports null rather than a fabricated zero, so the fleet panel
# can distinguish "no agents here" from "this box cannot answer".
READER_ABSENT="$WORK/no-such-reader.mjs"
cat > "$WORK/reader.mjs" <<'MJS'
export const ledgerDir = () => process.env.FIXTURE_LEDGER_DIR;
export const readEntries = () => [
  { state: "ALIVE-WORKING" },
  { state: "ALIVE-IDLE" },
  { state: "DEAD" },
  { state: "DETACHED-ALIVE" },
];
export const classify = async (entries) => entries;
MJS
mkdir -p "$WORK/ledger/sessions"

run_fixture() {
  BUILDBOX_TELEMETRY_SESSION_READER="${FIXTURE_READER:-$READER_ABSENT}" \
  FIXTURE_LEDGER_DIR="$WORK/ledger" \
  BUILDBOX_TELEMETRY_STATE_DIR="$1" \
  BUILDBOX_TELEMETRY_PROC_ROOT="$FIX/proc" \
  BUILDBOX_TELEMETRY_SYS_ROOT="$FIX/sys" \
  BUILDBOX_TELEMETRY_CGROUP_ROOT="$FIX/cgroup" \
  BUILDBOX_TELEMETRY_SAMPLE_SLEEP_SECONDS=0 \
  bash "$SAMPLER"
}

STATE_DIR="$WORK/state"
run_fixture "$STATE_DIR"
STATE="$STATE_DIR/buildbox-telemetry.json"

test -s "$STATE" || fail "sampler published no summary"
[ ! -e "$STATE.tmp" ] || fail "sampler left a partial publication file behind"

# The temperature ceiling comes from the critical trip point only: a passive trip is a
# throttle hint, and treating it as the ceiling reports a healthy box as near shutdown.
jq -e '
  .load == 0.42 and
  .memUsedBytes == 10240000000 and
  .memTotalBytes == 16384000000 and
  .swapUsedBytes == 1024000000 and
  .temperatures.pkg == 52 and
  .temperatures.max == 61 and
  .temperatures.crit == 89 and
  .guard.memoryStallSome60 == 1.25 and
  .guard.memoryStallFull60 == 0.5 and
  .guard.memoryStallFull300 == 7.25 and
  (.guard.workSlices | length) == 0 and
  (.coreLoadPercent | length) == 2 and
  (.coreLoadPercent | all(. == 0)) and
  .sessions == null
' "$STATE" >/dev/null || fail "fixture summary did not carry the expected kernel facts"

# Work reaches a box down three independent paths and no single job table sees all of them,
# so the count is taken from the process list and deduplicated per work unit: one seat fans
# out into a supervisor, several wrappers and the runtime binary.
cat > "$WORK/procs.txt" <<'PROCS'
bash /home/user/builds/envmarkers-1696/.harness-seat/20260808091154-705366/runtime/seat/seat-run.sh --run-id 20260808091154-705366
bash /w/.harness-seat/20260808091154-705366/runtime/wrappers/ca.sh --workspace /w/.
timeout -k 5 5400 cursor-agent --model grok-4.5 --workspace /w/.harness-seat/20260808091154-705366
bash /w/.harness-seat/20260808120000-111111/runtime/wrappers/ca.sh --workspace /w/.
FT_FROM_HOOK=1 FT_HINTS=1 ft node /home/user/.rb/command-supervisor.mjs --mode opaque --job-dir /home/user/.rb/jobs/886b6c8863ec7f3f
FT_FROM_HOOK=1 FT_HINTS=1 ft node /home/user/.rb/command-supervisor.mjs --mode opaque --job-dir /home/user/.rb/jobs/b82c248f61979663
bash .rb/runner.sh /home/user/.rb/jobs/b82c248f61979663
bash /home/user/.rb/runner-remote.sh /home/user/.rb/jobs/44aa11bb22cc33dd /home/user/mirror w bash /w/.harness-seat/20260808120000-111111/runtime/seat/seat-run.sh
/usr/bin/tmux -S /home/user/.local/state/human-session/tmux.sock -f /home/user/cdx-offload/t54-tmux-765a2b7510/conf
/usr/lib/systemd/systemd --user
PROCS
WORK_DIR="$WORK/state-work"
BUILDBOX_TELEMETRY_PROCESS_LIST="$WORK/procs.txt" run_fixture "$WORK_DIR"
jq -e '
  .remoteWork.agentSeats == 2 and
  .remoteWork.remoteBuildJobs == 2 and
  .remoteWork.offloadShells == 1 and
  .ciJobsRunning == 0
' "$WORK_DIR/buildbox-telemetry.json" >/dev/null \
  || fail "remote work units were not counted once each across their wrapper processes"

# CI workers are counted separately from the offload placement paths.
printf '/usr/lib/systemd/systemd --user\n/home/user/actions-runner/bin/Runner.Worker spawnclient\n/home/user/actions-runner/bin/Runner.Worker spawnclient\n' > "$WORK/idle-procs.txt"
IDLE_DIR="$WORK/state-idle"
BUILDBOX_TELEMETRY_PROCESS_LIST="$WORK/idle-procs.txt" run_fixture "$IDLE_DIR"
jq -e '
  .remoteWork.agentSeats == 0 and
  .remoteWork.remoteBuildJobs == 0 and
  .remoteWork.offloadShells == 0 and
  .ciJobsRunning == 2
' "$IDLE_DIR/buildbox-telemetry.json" >/dev/null || fail "CI workers were not sampled from the live process list"

# No process list at all means the box could not look. Every real box runs at least init, so
# an empty read is a failed sample, and publishing its zeros would be the blind zero this
# counter exists to replace.
: > "$WORK/no-procs.txt"
BLIND_DIR="$WORK/state-blind"
BUILDBOX_TELEMETRY_PROCESS_LIST="$WORK/no-procs.txt" run_fixture "$BLIND_DIR"
jq -e '.remoteWork == null and .ciJobsRunning == null' "$BLIND_DIR/buildbox-telemetry.json" >/dev/null \
  || fail "an unreadable process list was published as a measured zero"

SESSIONS_DIR="$WORK/state-sessions"
FIXTURE_READER="$WORK/reader.mjs" run_fixture "$SESSIONS_DIR"
jq -e '.sessions == 3' "$SESSIONS_DIR/buildbox-telemetry.json" >/dev/null \
  || fail "live session states were not counted, or dead ones were counted"

# No zone reports a package temperature: pkg falls back to the hottest zone, because a
# reported 0 degrees would read as a cold, idle box.
rm "$FIX/sys/class/thermal/thermal_zone0/type"
NOPKG_DIR="$WORK/state-nopkg"
run_fixture "$NOPKG_DIR"
jq -e '.temperatures.pkg == 61 and .temperatures.max == 61' "$NOPKG_DIR/buildbox-telemetry.json" >/dev/null \
  || fail "package temperature did not fall back to the hottest zone"

UID_NOW=$(id -u)
SLICE="$FIX/cgroup/user.slice/user-$UID_NOW.slice/user@$UID_NOW.service/agent.slice"
mkdir -p "$SLICE"
printf '2000000\n' > "$SLICE/memory.current"
printf 'low 0\noom_kill 3\n' > "$SLICE/memory.events"
printf '12\n' > "$SLICE/pids.current"
SLICE_DIR="$WORK/state-slice"
run_fixture "$SLICE_DIR"
jq -e '
  (.guard.workSlices | length) == 1 and
  .guard.workSlices[0].slice == "agent.slice" and
  .guard.workSlices[0].memoryBytes == 2000000 and
  .guard.workSlices[0].oomKillTotal == 3 and
  .guard.workSlices[0].pidsCurrent == 12
' "$SLICE_DIR/buildbox-telemetry.json" >/dev/null || fail "work slice facts were not reported"

# A reader cannot tell a frozen file from a live one without the publication stamp, and the
# per-core percentages only exist if the two-snapshot delta ran against this kernel.
LIVE_DIR="$WORK/state-live"
BUILDBOX_TELEMETRY_SESSION_READER="$READER_ABSENT" \
BUILDBOX_TELEMETRY_STATE_DIR="$LIVE_DIR" bash "$SAMPLER"
NOW_MS=$(($(date +%s) * 1000))
jq -e --argjson now "$NOW_MS" '
  (.sampledAtEpochMs | type == "number") and
  ($now - .sampledAtEpochMs) >= 0 and ($now - .sampledAtEpochMs) < 30000 and
  .cores > 0 and
  (.coreLoadPercent | length) == .cores and
  (.coreLoadPercent | all(. >= 0 and . <= 100)) and
  .guard.tmpSizeBytes > 0
' "$LIVE_DIR/buildbox-telemetry.json" >/dev/null || fail "live sample did not report a stamped, in-range summary"

# Overlapping runs must not both publish; the loser leaves the previous summary intact.
LOCK_DIR="$WORK/state-lock"
mkdir -p "$LOCK_DIR"
exec 8>"$LOCK_DIR/buildbox-telemetry.lock"
flock -n 8
BUILDBOX_TELEMETRY_STATE_DIR="$LOCK_DIR" BUILDBOX_TELEMETRY_SAMPLE_SLEEP_SECONDS=0 bash "$SAMPLER"
[ ! -e "$LOCK_DIR/buildbox-telemetry.json" ] || fail "sampler published while another run held the lock"
exec 8>&-

printf 'ok   buildbox telemetry publishes a stamped, complete local summary\n'
