#!/usr/bin/env bash
set -euo pipefail

# Ensure every k3s node carries the ARC runner image in its containerd.
#
# Why this exists: the cluster has no container registry (no registries.yaml,
# private GHCR packages, no imagePullSecret), so runner pods run with
# `imagePullPolicy: Never` against a side-loaded image. A node missing that
# image does not fail loudly -- pods land there, go ErrImageNeverPull, and jobs
# queue forever with nothing in the CI UI to explain it. That is exactly the
# silent-wedge class modules/ci/CLAUDE.md requires a standing repair for, so
# this script both detects and repairs, and is safe to re-run at any time.
#
# It never builds on the workstation. If no node has the image, it builds on a
# builder node over the buildbox SSH lane and side-loads from there.

script_dir=$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd)
repo_root=$(git -C "$script_dir" rev-parse --show-toplevel)
hosts_json="$repo_root/modules/workstation/claude/buildbox-hosts.json"
image=${ARC_RUNNER_IMAGE:-localhost/overdeck-ci-runner:arc-pr-gate-v1}
ctr="sudo /usr/local/bin/k3s ctr -n k8s.io images"

[[ -s "$hosts_json" ]] || { printf 'distribute: missing %s\n' "$hosts_json" >&2; exit 2; }
command -v node >/dev/null || { printf 'distribute: node is required\n' >&2; exit 2; }

# name<TAB>user@host<TAB>port<TAB>identity, one reachable builder per line.
mapfile -t fleet < <(node -e '
const fs = require("fs");
const doc = JSON.parse(fs.readFileSync(process.argv[1], "utf8"));
for (const h of doc.hosts || []) {
  const a = (h.access || {}).tailscale_ip;
  if (!a || h.state !== "reachable" || !(h.roles || []).includes("builder")) continue;
  const id = (a.identity_file || "").replace(/^~/, process.env.HOME);
  process.stdout.write([h.name, `${a.user}@${a.host}`, a.port, id].join("\t") + "\n");
}
' "$hosts_json")

(( ${#fleet[@]} > 0 )) || { printf 'distribute: no reachable builder hosts\n' >&2; exit 2; }

# The buildbox key exists on the workstation and nowhere else -- the nodes hold
# no fleet key at all. Run from a node and every probe would fail on publickey,
# read as "all three nodes MISSING", and try to build. Say which machine this
# belongs on instead of emitting three ssh errors that look like a fleet outage.
for entry in "${fleet[@]}"; do
  identity=$(cut -f4 <<<"$entry")
  [[ -r "$identity" ]] || {
    printf 'distribute: no readable ssh identity at %s -- run this from the workstation\n' \
      "$identity" >&2
    exit 2
  }
done

ssh_to() { # ssh_to <index> <remote command>
  local -a f
  read -r -a f <<<"${fleet[$1]}" # name dest port identity
  ssh -p "${f[2]}" -i "${f[3]}" -o StrictHostKeyChecking=no -o BatchMode=yes "${f[1]}" "$2"
}

has_image() { ssh_to "$1" "$ctr ls -q 2>/dev/null | grep -Fxq '$image'"; }

present=() missing=()
for i in "${!fleet[@]}"; do
  name=$(cut -f1 <<<"${fleet[$i]}")
  if has_image "$i"; then present+=("$i"); printf 'present  %s\n' "$name"
  else missing+=("$i"); printf 'MISSING  %s\n' "$name"; fi
done

if (( ${#missing[@]} == 0 )); then
  printf 'distribute: %s present on all %d nodes\n' "$image" "${#fleet[@]}"
  exit 0
fi

# No node has it: build on the first builder, then side-load from there. The
# build lands in that node's podman store, which is a different store from
# containerd, so it still has to be imported below like any other node.
source_idx=${present[0]:-}
if [[ -z "$source_idx" ]]; then
  source_idx=${missing[0]}
  name=$(cut -f1 <<<"${fleet[$source_idx]}")
  printf 'distribute: no node has %s; building on %s\n' "$image" "$name"
  remote_dir="\$HOME/.cache/overdeck/arc-runner-image"
  ssh_to "$source_idx" "mkdir -p $remote_dir"
  read -r -a f <<<"${fleet[$source_idx]}"
  tar czf - -C "$script_dir" . \
    | ssh -p "${f[2]}" -i "${f[3]}" -o StrictHostKeyChecking=no -o BatchMode=yes "${f[1]}" \
        "tar xzf - -C $remote_dir"
  ssh_to "$source_idx" "bash $remote_dir/build.sh"
  ssh_to "$source_idx" "podman save --format docker-archive '$image' | $ctr import -"
  has_image "$source_idx" || { printf 'distribute: build did not land on %s\n' "$name" >&2; exit 3; }
  missing=("${missing[@]:1}")
fi

# Node-to-node so the payload never transits the workstation. Agent forwarding
# supplies the buildbox key on the source hop; the fleet shares one key, and
# `-i` alone cannot serve the second hop -- only an agent can.
read -r -a src <<<"${fleet[$source_idx]}"
src_name=${src[0]}
started_agent=
if ! ssh-add -l >/dev/null 2>&1; then
  eval "$(ssh-agent -s)" >/dev/null
  started_agent=1
fi
ssh-add "${src[3]}" </dev/null >/dev/null 2>&1 || true
cleanup() { [[ -n "$started_agent" ]] && ssh-agent -k >/dev/null 2>&1 || true; }
trap cleanup EXIT

for i in "${missing[@]}"; do
  read -r -a dst <<<"${fleet[$i]}"
  printf 'distribute: %s -> %s\n' "$src_name" "${dst[0]}"
  ssh -A -p "${src[2]}" -i "${src[3]}" -o StrictHostKeyChecking=no -o BatchMode=yes "${src[1]}" \
    "$ctr export - '$image' | ssh -p ${dst[2]} -o StrictHostKeyChecking=no -o BatchMode=yes ${dst[1]} \"$ctr import -\"" \
    >/dev/null
done

status=0
for i in "${!fleet[@]}"; do
  name=$(cut -f1 <<<"${fleet[$i]}")
  if has_image "$i"; then printf 'ok       %s\n' "$name"
  else printf 'FAILED   %s\n' "$name" >&2; status=1; fi
done
(( status == 0 )) && printf 'distribute: %s present on all %d nodes\n' "$image" "${#fleet[@]}"
exit "$status"
