#!/usr/bin/env bash
# Runs a dev-server + client (browser/E2E) pair together on one remote build box.
set -euo pipefail

HOME_DIR="${HOME}"
BASE_CONFIG="${BUILD_REMOTE_CONFIG:-$HOME_DIR/.claude/build-remote.json}"
LOCAL_GATE="$HOME_DIR/.claude/bin/local-gate"
DEFAULT_WAIT_SEC=240

usage() {
  cat >&2 <<'USAGE'
usage: e2e-remote [--server <shell-cmd> --wait-port <port>] [options] -- <client argv...>

  --server <cmd>     shell command that starts the server (run on the remote box)
                     omit it when the client drives an already-deployed target;
                     the client still runs remotely, where the browser is allowed
  --wait-port <n>    TCP port on 127.0.0.1 the server must listen on
                     required with --server, rejected without it
  --wait-sec <n>     how long to wait for the port (default 240)
  --env K=V          environment for BOTH server and client (repeatable)
  --mkdir <dir>      repo-relative dir created remotely before the run (repeatable)
  --hosts a,b        remote boxes to try, in order
                     (default: the "e2e" order in ~/.claude/buildbox-hosts.json,
                     minus disabled hosts and the host this process runs on)
  --key <key>        local-gate dedupe key

exit codes: 3 port already in use, 4 server died before listening,
            5 server never listened, otherwise the client's own exit code.

Artifacts must be written inside the repo tree to be rsynced back; anything under
node_modules/.git/.cache/target is excluded from the pull.
USAGE
  exit "${1:-2}"
}

SERVER_CMD=""
WAIT_PORT=""
WAIT_SEC="$DEFAULT_WAIT_SEC"
HOSTS=""
KEY=""
ENV_ARGS=()
MKDIRS=""
CLIENT=()

while [ $# -gt 0 ]; do
  case "$1" in
    --server)    SERVER_CMD="${2-}"; shift 2 ;;
    --wait-port) WAIT_PORT="${2-}"; shift 2 ;;
    --wait-sec)  WAIT_SEC="${2-}"; shift 2 ;;
    --hosts)     HOSTS="${2-}"; shift 2 ;;
    --key)       KEY="${2-}"; shift 2 ;;
    --env)       ENV_ARGS+=("${2-}"); shift 2 ;;
    --mkdir)     MKDIRS="${MKDIRS}${2-}"$'\n'; shift 2 ;;
    --)          shift; CLIENT=("$@"); break ;;
    -h|--help)   usage 0 ;;
    *)           echo "e2e-remote: unknown arg: $1" >&2; usage ;;
  esac
done

if [ -n "$SERVER_CMD" ]; then
  [ -n "$WAIT_PORT" ] || { echo "e2e-remote: --wait-port is required with --server" >&2; usage; }
  case "$WAIT_PORT" in *[!0-9]*) echo "e2e-remote: --wait-port must be numeric" >&2; exit 2 ;; esac
else
  [ -z "$WAIT_PORT" ] || { echo "e2e-remote: --wait-port needs --server" >&2; usage; }
fi
[ "${#CLIENT[@]}" -gt 0 ] || { echo "e2e-remote: missing client command after --" >&2; usage; }
case "$WAIT_SEC" in *[!0-9]*|'') echo "e2e-remote: --wait-sec must be numeric" >&2; exit 2 ;; esac
for kv in "${ENV_ARGS[@]}"; do
  case "$kv" in *=*) ;; *) echo "e2e-remote: --env expects KEY=VALUE, got: $kv" >&2; exit 2 ;; esac
done
[ -x "$LOCAL_GATE" ] || { echo "e2e-remote: local-gate not found at $LOCAL_GATE" >&2; exit 2; }

SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
SEAT_ATTEST_LIB="$SCRIPT_DIR/../lib/seat-attest.sh"
# shellcheck source=../lib/seat-attest.sh
[ -f "$SEAT_ATTEST_LIB" ] || { echo "e2e-remote: seat attestation lib missing at $SEAT_ATTEST_LIB" >&2; exit 2; }
# shellcheck disable=SC1090
. "$SEAT_ATTEST_LIB"

E2E_PAIR_LIB="$SCRIPT_DIR/../lib/e2e-pair.sh"
[ -f "$E2E_PAIR_LIB" ] || { echo "e2e-remote: pair orchestrator missing at $E2E_PAIR_LIB" >&2; exit 2; }
# Shipped to the remote box inside the payload, so it is read as text here rather
# than sourced.
ORCHESTRATOR="$(cat "$E2E_PAIR_LIB")"

if [ -n "${OVERDECK_SEAT_HOST:-}" ]; then
  overdeck_seat_attest_or_die
  export E2E_REMOTE_OK=1
  for kv in "${ENV_ARGS[@]}"; do export "$kv"; done
  eval "$ORCHESTRATOR"
  e2e_remote_run "$WAIT_PORT" "$WAIT_SEC" "$SERVER_CMD" "$MKDIRS" "${CLIENT[@]}"
  exit $?
fi

if [ -z "$HOSTS" ]; then
  HOSTS="$(CPU_GUARD_ACTIVE=1 node "$SCRIPT_DIR/../lib/buildbox-registry.mjs" hosts --order e2e)" || {
    echo "e2e-remote: no usable buildbox host — refusing to run the pair locally" >&2
    exit 97
  }
else
  HOSTS="$(CPU_GUARD_ACTIVE=1 node "$SCRIPT_DIR/../lib/buildbox-registry.mjs" check "$HOSTS")" || {
    echo "e2e-remote: --hosts rejected by the buildbox registry — refusing to run the pair locally" >&2
    exit 97
  }
fi

# The pair is only self-contained on one host, so a local fallback would put the
# server and the browser back on the laptop. Spill across boxes, never downward.
BUILD_HOSTS="$(CPU_GUARD_ACTIVE=1 node "$SCRIPT_DIR/../lib/buildbox-registry.mjs" hosts --order build)" || {
  echo "e2e-remote: no usable buildbox host — refusing to run the pair locally" >&2
  exit 97
}
if [ "$HOSTS" != "$BUILD_HOSTS" ]; then
  echo "e2e-remote: e2e host order differs from remote-build host order — refusing an un-pinned dispatch" >&2
  exit 97
fi
CONFIG_DIR="$(mktemp -d -t e2e-remote-cfg.XXXXXX)"
trap 'rm -rf "$CONFIG_DIR"' EXIT
CONFIG_FILE="$CONFIG_DIR/build-remote.json"
node -e '
  const fs = require("node:fs");
  const [base, out] = process.argv.slice(1);
  const cfg = JSON.parse(fs.readFileSync(base, "utf8"));
  cfg.local_fallback = false;
  fs.writeFileSync(out, JSON.stringify(cfg));
' "$BASE_CONFIG" "$CONFIG_FILE"

# systemd-run on the box expands ${...} in the job command line against the
# unit's (empty) environment, so any brace reaching it is silently blanked.
# Base64 keeps the whole payload opaque to every quoting layer in between.
PAYLOAD="$ORCHESTRATOR"$'\n'
# Opens the fail-closed browser guard (bin/install-headless-guard) for this run.
# Not a --env: the caller must not be able to forge or omit it.
PAYLOAD+=$'export E2E_REMOTE_OK=1\n'
for kv in "${ENV_ARGS[@]}"; do
  PAYLOAD+="export $(printf '%q' "$kv")"$'\n'
done
PAYLOAD+="e2e_remote_run $(printf '%q' "$WAIT_PORT") $(printf '%q' "$WAIT_SEC")"
PAYLOAD+=" $(printf '%q' "$SERVER_CMD") $(printf '%q' "$MKDIRS")"
for arg in "${CLIENT[@]}"; do
  PAYLOAD+=" $(printf '%q' "$arg")"
done
PAYLOAD+=$'\nrc=$?\nexit "$rc"\n'
PAYLOAD_B64="$(printf '%s' "$PAYLOAD" | base64 -w0)"

if [ -z "$KEY" ]; then
  # local-gate DEDUPES on this key and returns success WITHOUT running, so a key
  # that collides across distinct runs reports a false green. Hash the whole
  # payload: two runs share a key only if they would do exactly the same work.
  KEY="e2e_$(basename "$PWD")_$(printf '%s' "$PAYLOAD" | sha256sum | cut -c1-12)"
  KEY="${KEY//[^A-Za-z0-9_.-]/_}"
fi

set +e
BUILD_REMOTE_CONFIG="$CONFIG_FILE" "$LOCAL_GATE" --key "$KEY" -- \
  bash -c 'eval "$(printf %s "$1" | base64 -d)"' e2e-remote "$PAYLOAD_B64"
exit $?
