#!/usr/bin/env bash
# gate-poll.sh — notify on INDIVIDUAL JOB TRANSITIONS, not whole-run completion.
#
# Replaces the ad-hoc poll used on run 34481993681, which had two defects:
#   1. its predicate waited for ALL jobs terminal, so a red that landed while a
#      `!cancelled()` job kept running sat unreported for ~12 minutes;
#   2. its conclusion collector printed `concl=[]` at EVERY tick, including after a
#      job had concluded `failure` — an empty collection read as clean status.
#
# Design rules this file exists to enforce:
#   - emit a line the moment ANY job changes state; silence must mean "nothing changed",
#     never "nothing happened".
#   - emit on EVERY terminal conclusion (failure, cancelled, timed_out, action_required,
#     neutral, skipped, success), not just success.
#   - the collector is validated against a KNOWN-FAILED job before it is trusted.
#
# Usage:  ./gate-poll.sh <run_id> [interval_seconds]
#         ./gate-poll.sh --selftest <run_id> <job_id_expected_failure>
set -uo pipefail

REPO="BigscreenVR/spt-bs-core"
JQ_ROW='.jobs[] | [.id, .name, .status, (.conclusion // "-")] | @tsv'

# Snapshot source is INJECTABLE so the transition path itself can be replayed and tested.
# SPT_GATE_POLL_FIXTURE=<dir> replays <dir>/tick-NN.tsv in order, one per call, then stops.
# NOTE: this function is called inside $( ), i.e. a SUBSHELL, so it CANNOT keep a counter of
# its own — an incremented variable dies with the subshell and the same tick replays forever.
# It therefore indexes off the PARENT's $ticks, which is incremented before each call.
jobs_snapshot() {
  if [ -n "${SPT_GATE_POLL_FIXTURE:-}" ]; then
    f="$(printf '%s/tick-%02d.tsv' "$SPT_GATE_POLL_FIXTURE" "${ticks:-1}")"
    [ -f "$f" ] && cat "$f" || true
    return 0
  fi
  gh api "repos/$REPO/actions/runs/$1/jobs" --paginate --jq "$JQ_ROW" 2>/dev/null || true
}

# --- self-test: the collector must SEE a known failure, or the instrument is not trusted ---
if [ "${1:-}" = "--selftest" ]; then
  run="$2"; want_job="$3"
  snap="$(jobs_snapshot "$run")"
  if [ -z "$snap" ]; then echo "SELFTEST FAIL: empty snapshot (API/auth?)"; exit 2; fi
  row="$(printf '%s\n' "$snap" | awk -F'\t' -v j="$want_job" '$1==j')"
  if [ -z "$row" ]; then echo "SELFTEST FAIL: job $want_job absent from snapshot"; exit 2; fi
  concl="$(printf '%s' "$row" | cut -f4)"
  echo "SELFTEST row: $row"
  if [ "$concl" = "failure" ]; then
    echo "SELFTEST PASS: collector reports conclusion=failure for job $want_job"
    exit 0
  fi
  echo "SELFTEST FAIL: collector reported '$concl', expected 'failure' — DO NOT TRUST THIS POLL"
  exit 1
fi

run="${1:?usage: gate-poll.sh <run_id> [interval]}"
interval="${2:-60}"
declare -A seen

echo "ARMED $(date -u +%H:%M:%SZ) run=$run interval=${interval}s predicate=per-job-transition"

ticks=0
hb_every="${SPT_GATE_POLL_HEARTBEAT_TICKS:-10}"
while :; do
  ticks=$((ticks+1))
  snap="$(jobs_snapshot "$run")"
  if [ -z "$snap" ]; then
    echo "$(date -u +%H:%M:%SZ) WARN empty snapshot (transient API failure) — continuing"
    sleep "$interval"; continue
  fi

  pending=0
  while IFS=$'\t' read -r id name status concl; do
    [ -z "${id:-}" ] && continue
    key="$status/$concl"
    if [ "${seen[$id]:-}" != "$key" ]; then
      seen[$id]="$key"
      echo "$(date -u +%H:%M:%SZ) TRANSITION job=$id status=$status conclusion=$concl name=$name"
      case "$concl" in
        failure|cancelled|timed_out|action_required)
          echo "$(date -u +%H:%M:%SZ) *** TERMINAL NON-SUCCESS: $name ($id) -> $concl ***" ;;
      esac
    fi
    [ "$status" != "completed" ] && pending=$((pending+1))
  done <<< "$snap"

  if [ "$pending" -eq 0 ]; then
    echo "$(date -u +%H:%M:%SZ) RUN TERMINAL. Per-job conclusions:"
    printf '%s\n' "$snap" | awk -F'\t' '{printf "  %s  %s  %s\n", $4, $1, $2}'
    echo "$(date -u +%H:%M:%SZ) NOTE: score from these per-job rows, never the run-level field."
    exit 0
  fi
  # Silence only means "no observed change" if the polls themselves are still succeeding.
  # A dead or blind poll is otherwise indistinguishable from a quiet run.
  if [ $((ticks % hb_every)) -eq 0 ]; then
    echo "$(date -u +%H:%M:%SZ) HEARTBEAT ok tick=$ticks pending=$pending (polls succeeding, no change)"
  fi
  sleep "$interval"
done
