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

job_dir="$1"
mirror="$2"
rel="$3"
shift 3
mkdir -p "$job_dir"
exec >> "$job_dir/log" 2>&1

rc=254
write_rc() {
  local tmp="$job_dir/.rc.tmp.$$.$RANDOM"
  printf '%s\n' "$1" > "$tmp" && mv -f "$tmp" "$job_dir/rc"
}
finish() {
  trap - EXIT HUP INT TERM QUIT
  [ "$rc" -eq 255 ] && rc=254
  local receipt_rc="$rc"
  write_rc "$receipt_rc"
  exit "$receipt_rc"
}
interrupt() {
  rc="$1"
  exit "$rc"
}
trap 'interrupt 129' HUP
trap 'interrupt 130' INT
trap 'interrupt 131' QUIT
trap 'interrupt 143' TERM
trap finish EXIT

# Load canonical per-user environment for non-login build jobs.
for env_file in "$HOME"/.config/environment.d/*.conf; do
  [ -r "$env_file" ] || continue
  set -a
  # shellcheck disable=SC1090
  . "$env_file"
  set +a
done

# Build boxes often inherit DISPLAY from a forwarded X11 session on the workstation.
# Headless browsers and GTK still probe X when DISPLAY is set, which paints brief
# windows on Cinnamon before exiting.
unset DISPLAY WAYLAND_DISPLAY
export GDK_BACKEND=x11

export PATH="$HOME/.cargo/bin:$HOME/.local/bin:$PATH"
if [ -x "$HOME/.local/bin/mise" ]; then
  eval "$("$HOME/.local/bin/mise" env -s bash)"
fi
# corepack shims in ~/.local/bin resolve pnpm per-mirror from packageManager;
# must outrank any mise-pinned pnpm a rival session re-adds
export PATH="$HOME/.local/bin:$PATH"
export COREPACK_ENABLE_DOWNLOAD_PROMPT=0
# A gate run dispatched here must not dispatch again from the box.
export GATE0_REMOTE_ACTIVE=1

if [ -z "${NODE_AUTH_TOKEN:-}" ] && [ -f "$HOME/.npmrc" ]; then
  NODE_AUTH_TOKEN=$(sed -n 's/.*_authToken=//p' "$HOME/.npmrc" | head -1)
  [ -n "$NODE_AUTH_TOKEN" ] && export NODE_AUTH_TOKEN
fi

# Package owner = nearest lockfile from cwd or an existing command argument.
# Nested test packages can own browser dependencies inside non-JS repositories.
#
# One narrow exception exists for the source-controlled harness seat transport overlay. seat-run.sh
# itself is Bash/Podman plumbing and consumes no workspace node_modules; forcing a package install
# before it runs couples credential delivery to unrelated registry health. The path predicate is
# exact and fail-closed: regular non-symlink executable beneath one per-run .harness-seat runtime.
seat_transport=0
seat_runner=${1:-}
case "$seat_runner" in
  "$mirror"/.harness-seat/*/runtime/seat/seat-run.sh)
    seat_rel=${seat_runner#"$mirror"/.harness-seat/}
    seat_run=${seat_rel%%/*}
    seat_tail=${seat_rel#*/}
    if [ -n "$seat_run" ] && [ "$seat_run" != "$seat_rel" ] \
      && [ "$seat_tail" = runtime/seat/seat-run.sh ] \
      && [ -f "$seat_runner" ] && [ ! -L "$seat_runner" ] && [ -x "$seat_runner" ]; then
      seat_transport=1
    fi
    ;;
esac

PACKAGE_OWNER_LIB="$(dirname "${BASH_SOURCE[0]}")/package-owner.sh"
[ -r "$PACKAGE_OWNER_LIB" ] || { echo "remote-runner: package owner resolver missing: $PACKAGE_OWNER_LIB" >&2; exit 254; }
# shellcheck disable=SC1090
. "$PACKAGE_OWNER_LIB"
if [ "$seat_transport" -eq 1 ]; then
  ws_root="$mirror"
else
  if ! ws_root=$(find_package_owner "$mirror" "$rel" "$@"); then
    echo "remote-runner: no package lock owns cwd or command arguments under $mirror" >&2
    exit 254
  fi
fi

cd "$ws_root" || exit 254
# node_modules is a derived cache and is never trusted: a rival package manager's
# layout (bun/npm over a pnpm workspace) or a broken link farm makes frozen installs
# no-op while builds die on phantom resolve errors. Every dependency of every
# workspace package must resolve to a real directory, or the tree is rebuilt once
# from the registry; still broken → fail closed with the store as prime suspect.
verify_pnpm_tree() {
  if [ -d node_modules/.bun ]; then
    echo "remote-runner: foreign bun layout in pnpm workspace $ws_root" >&2
    return 1
  fi
  local chk="$job_dir/depcheck.js"
  cat > "$chk" <<'NODE'
const fs = require("fs");
const path = require("path");
const p = JSON.parse(fs.readFileSync("package.json", "utf8"));
const deps = Object.keys(Object.assign({}, p.dependencies, p.devDependencies));
let bad = 0;
for (const d of deps) {
  let dir = process.cwd();
  let ok = false;
  for (;;) {
    if (fs.existsSync(path.join(dir, "node_modules", d, "package.json"))) { ok = true; break; }
    const up = path.dirname(dir);
    if (up === dir) break;
    dir = up;
  }
  if (!ok) { console.error(`depcheck: missing dependency ${d} of ${p.name || "root"}`); bad = 1; }
}
process.exit(bad);
NODE
  pnpm -r exec node "$chk"
}

if [ "$seat_transport" -eq 0 ]; then
  install_rc=0
  # The lockfile ratchet always lives at the mirror root, even when a nested
  # package owns the install: repository gates (the IPZ remote gate) enumerate
  # untracked files against a whitelist that admits .rb-lockhash only at the
  # root, so a ratchet written beside a nested lockfile fails their pristine
  # check on every run.
  lockhash_file="$mirror/.rb-lockhash"
  if [ -f pnpm-lock.yaml ]; then
    lockf=pnpm-lock.yaml
    new_hash=$(sha256sum "$lockf" | cut -d' ' -f1)
    old_hash=$(cat "$lockhash_file" 2>/dev/null || true)
    if [ "$new_hash" != "$old_hash" ] || [ ! -d node_modules ]; then
      pnpm install --frozen-lockfile --prefer-offline --config.confirmModulesPurge=false
      install_rc=$?
      [ "$install_rc" -eq 0 ] && printf '%s' "$new_hash" > "$lockhash_file"
    fi
    if [ "$install_rc" -eq 0 ] && ! verify_pnpm_tree; then
      echo "remote-runner: node_modules failed integrity check — rebuilding from scratch" >&2
      find . -maxdepth 3 -type d -name node_modules -prune -exec rm -rf {} +
      rm -f "$lockhash_file"
      pnpm install --frozen-lockfile --force --config.confirmModulesPurge=false
      install_rc=$?
      if [ "$install_rc" -eq 0 ]; then
        if verify_pnpm_tree; then
          printf '%s' "$new_hash" > "$lockhash_file"
        else
          echo "remote-runner: integrity still failing after rebuild — failing closed (suspect corrupt pnpm store on this box; wipe the store path for the missing package and rerun)" >&2
          install_rc=254
        fi
      fi
    fi
  elif [ -f package-lock.json ]; then
    lockf=package-lock.json
    new_hash=$(sha256sum "$lockf" | cut -d' ' -f1)
    old_hash=$(cat "$lockhash_file" 2>/dev/null || true)
    if [ "$new_hash" != "$old_hash" ] || [ ! -d node_modules ]; then
      npm ci
      install_rc=$?
      [ "$install_rc" -eq 0 ] && printf '%s' "$new_hash" > "$lockhash_file"
    fi
  elif [ -f bun.lock ] || [ -f bun.lockb ]; then
    lockf=bun.lock; [ -f bun.lockb ] && lockf=bun.lockb
    new_hash=$(sha256sum "$lockf" | cut -d' ' -f1)
    old_hash=$(cat "$lockhash_file" 2>/dev/null || true)
    if [ "$new_hash" != "$old_hash" ] || [ ! -d node_modules ]; then
      bun install --frozen-lockfile
      install_rc=$?
      [ "$install_rc" -eq 0 ] && printf '%s' "$new_hash" > "$lockhash_file"
    fi
  fi
  if [ "$install_rc" -ne 0 ]; then
    rc="$install_rc"
    exit "$rc"
  fi
else
  install_rc=0
  echo "remote-runner: seat transport skips workspace dependency install" >&2
fi

# Trigger on `playwright` invoked as a program, not on any argv substring: a test path such as
# packages/playwright/test/x.test.ts is not a playwright CLI run.
invokes_playwright=0
for arg in "$@"; do
  case "$arg" in
    playwright|*/playwright) invokes_playwright=1; break;;
  esac
done
if [ "$invokes_playwright" -eq 1 ]; then
  playwright_bin="$ws_root/node_modules/.bin/playwright"
  [ -x "$playwright_bin" ] || { echo "remote-runner: installed Playwright binary not found: $playwright_bin" >&2; rc=254; exit "$rc"; }
  "$playwright_bin" install
fi

cd "$mirror/$rel" || exit 254
# project-local bins (tsc, vitest, eslint) resolve like a pnpm-run PATH: cwd up to mirror root
d="$PWD"
while :; do
  [ -d "$d/node_modules/.bin" ] && PATH="$d/node_modules/.bin:$PATH"
  [ "$d" = "$mirror" ] && break
  d=$(dirname "$d")
done
export PATH
supervisor="${SUPERVISOR_BIN:-$HOME/.rb/command-supervisor.mjs}"
if [ ! -r "$supervisor" ]; then
  echo "remote-runner: command supervisor missing: $supervisor" >&2
  rc=254
  exit "$rc"
fi
exec node "$supervisor" \
  --mode "${SUPERVISOR_MODE:-opaque}" \
  --job-dir "$job_dir" \
  --state "$job_dir/state.json" \
  --rc "$job_dir/rc" \
  --diagnostics "$job_dir/diagnostics.json" \
  --lease-ms "${SUPERVISOR_LEASE_MS:-60000}" \
  --terminal-grace-ms "${SUPERVISOR_TERMINAL_GRACE_MS:-5000}" \
  -- "$@"
