#!/usr/bin/env bash
# buildbox-watch announces build-pool transitions. Every crossing must produce exactly one
# message and no crossing may be held back, so the run is driven here against a stub ssh
# whose answers are scripted and a stub notify-send that records what the user would see.
set -uo pipefail

REPO="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)"
WATCH="$REPO/modules/workstation/claude/bin/buildbox-watch.sh"
WORK="$(mktemp -d)"
trap 'rm -rf "$WORK"' EXIT
fail=0
check() { [ "$2" = "$3" ] && printf 'ok   %s\n' "$1" || { printf 'FAIL %s\n  want: %s\n  got:  %s\n' "$1" "$2" "$3"; fail=1; }; }

export HOME="$WORK/home"
mkdir -p "$HOME/.claude" "$WORK/bin"
cat >"$HOME/.claude/buildbox-hosts.json" <<'JSON'
{
  "schema_version": 1,
  "hosts": [
    { "name": "boxa", "ssh_alias": "boxa", "state": "reachable", "machine_id": null,
      "roles": ["builder"], "rustdesk": null, "notes": "fixture host for the edge-trigger suite",
      "access": {
        "lan": null,
        "tailscale_ip": { "host": "10.0.0.1", "port": 2222, "user": "user", "identity_file": null },
        "tailscale_ssh": null
      } },
    { "name": "laptop", "ssh_alias": "laptop", "state": "reachable", "machine_id": null,
      "roles": ["seat"], "rustdesk": null, "notes": "fixture controller",
      "access": { "lan": null, "tailscale_ip": null, "tailscale_ssh": null } }
  ],
  "orders": { "build": ["boxa"] }
}
JSON

# UP/DOWN in this file is what the stub ssh answers; the script reads it once per probe.
printf UP >"$WORK/reachable"
cat >"$WORK/bin/ssh" <<EOF
#!/usr/bin/env bash
[ "\$(cat "$WORK/reachable")" = UP ] || exit 255
exit 0
EOF
cat >"$WORK/bin/notify-send" <<EOF
#!/usr/bin/env bash
printf '%s\n' "\${*: -2:1}" >>"$WORK/notifications"
EOF
printf 'runner-a\tonline\n' >"$WORK/runners"
cat >"$WORK/bin/gh" <<EOF
#!/usr/bin/env bash
cat "$WORK/runners"
EOF
cat >"$WORK/bin/bun" <<EOF
#!/usr/bin/env bash
printf '%s\n' "\${*: -1}" >>"$WORK/fires"
EOF
cat >"$WORK/bin/od-requests" <<'EOF'
#!/usr/bin/env bash
exit 0
EOF
chmod 755 "$WORK/bin/ssh" "$WORK/bin/notify-send" "$WORK/bin/gh" "$WORK/bin/bun" "$WORK/bin/od-requests"
export PATH="$WORK/bin:$PATH"
export OD_REQUESTS="$WORK/bin/od-requests"
export OVERDECK_SEAT_HOST=laptop

: >"$WORK/notifications"
notifications() { tr '\n' '|' <"$WORK/notifications"; }

# STABLE_CHECKS=1 removes the debounce from the assertions: this suite is about whether a
# reached tier announces, not about how many probes it takes to reach one.
run() { STABLE_CHECKS=1 bash "$WATCH" >/dev/null 2>&1; }

run   # first run baselines silently, so a fresh workstation does not announce at boot
check "first run baselines silently" "" "$(notifications)"

run
check "no crossing, no message" "" "$(notifications)"

printf 'runner-a\toffline\n' >"$WORK/runners"
run
check "runner online to offline fires once" "gh-runner:runner-a:offline|" "$(tr '\n' '|' <"$WORK/fires")"
run
check "runner remaining offline does not repeat" "gh-runner:runner-a:offline|" "$(tr '\n' '|' <"$WORK/fires")"

printf DOWN >"$WORK/reachable"
run
check "pool going offline announces once" "Build pool OFFLINE|" "$(notifications)"
run
check "still offline does not repeat" "Build pool OFFLINE|" "$(notifications)"

# The recovery is the case a cooldown timer would swallow: it lands seconds after the
# outage message, and the user has to see it then, not half an hour later.
printf UP >"$WORK/reachable"
run
check "immediate recovery announces" "Build pool OFFLINE|Build pool ONLINE|" "$(notifications)"

printf DOWN >"$WORK/reachable"; run
printf UP   >"$WORK/reachable"; run
check "each further crossing announces" \
  "Build pool OFFLINE|Build pool ONLINE|Build pool OFFLINE|Build pool ONLINE|" "$(notifications)"

exit "$fail"
