#!/usr/bin/env bash
# ctl-dev-kit convention: the integration suite under contention verdicts.
#
# integration.yml runs this, inside the dev shell and from the repo root, in
# place of `bun run test:integration` when the repo has opted in with
# scripts/integration/contention.json (thresholds; `{}` takes every default).
# It runs the suite under @norskvideo/ctl-test-harness's ctl-contention, which
# samples host pressure (Linux PSI) alongside it and classifies the result:
#
#   exit 0     the suite passed
#   exit N     a real failure: the suite's own status, as before
#   exit 75    uncertain: every failure overlapped host contention, or the
#              suite was stopped early because the host was starved
#
# An uncertain attempt is rerun up to rerun.max times (default 1), once the
# host has gone quiet or rerun.maxWaitSec has passed. Each attempt's timeline,
# verdict and summary land in $NORSK_CONTENTION_DIR/attempt-<n>/, the last
# verdict at $NORSK_CONTENTION_DIR/verdict.json for the workflow to read.
#
# Anything missing (a harness without ctl-contention, a config it cannot
# read) runs the suite exactly as before, with a warning: opting in must
# never be what turns a tier red.
set -uo pipefail

config=scripts/integration/contention.json
bin=node_modules/.bin/ctl-contention
dir="${NORSK_CONTENTION_DIR:-test-results/contention}"

as_before() {
  echo "::warning title=contention verdicts off::$1; the suite runs without them, exactly as before."
  exec bun run test:integration
}

[ -x "$bin" ] || as_before "$config is present but this repo's @norskvideo/ctl-test-harness has no ctl-contention (bump it)"
if ! max="$("$bin" config rerun.max --config "$config")"; then
  as_before "ctl-contention could not read $config"
fi

# Between attempts: the containers the last attempt launched. Its teardown may
# never have run (an abort kills the suite), and a held port or a leftover
# engine would starve or break the rerun. The same two rules the job-end reap
# steps use: instances the harness recorded, and anything mounting this job's
# test-temp.
reap_leftovers() {
  command -v docker >/dev/null 2>&1 || return 0
  local m="${NORSK_LAUNCHED_MANIFEST:-}" base="${NORSK_CTL_TEST_TMP:-}" id ids c src
  if [ -n "$m" ] && [ -f "$m" ]; then
    while IFS= read -r id; do
      [ -n "$id" ] || continue
      ids="$(docker ps -aq --filter "label=norsk-ctl.instance=$id" 2>/dev/null || true)"
      [ -z "$ids" ] || docker rm -f $ids >/dev/null 2>&1 || true
    done < <(sort -u "$m")
  fi
  [ -n "$base" ] || return 0
  for c in $(docker ps -aq 2>/dev/null || true); do
    src="$(docker inspect -f '{{range .Mounts}}{{println .Source}}{{end}}' "$c" 2>/dev/null)" || continue
    if grep -q "^$base/" <<<"$src"; then docker rm -f "$c" >/dev/null 2>&1 || true; fi
  done
}

attempt=1
while :; do
  out="$dir/attempt-$attempt"
  "$bin" run --config "$config" --out "$out" -- bun run test:integration
  rc=$?
  [ -f "$out/verdict.json" ] && cp "$out/verdict.json" "$dir/verdict.json"
  if [ -n "${GITHUB_STEP_SUMMARY:-}" ] && [ -f "$out/summary.md" ]; then
    { echo "#### Attempt $attempt"; echo; cat "$out/summary.md"; echo; } >>"$GITHUB_STEP_SUMMARY"
  fi
  if [ "$rc" -ne 75 ] || [ "$attempt" -gt "$max" ]; then break; fi
  echo "::warning title=integration attempt $attempt uncertain::the host was contended; waiting for it to go quiet, then rerunning (attempt $((attempt + 1)) of $((max + 1)))"
  reap_leftovers
  "$bin" wait-quiet --config "$config" || true
  attempt=$((attempt + 1))
done
exit "$rc"
