#!/usr/bin/bash
# install-headless-guard — wrap every Playwright browser binary so a headless run
# cannot start on this machine unless it came through e2e-remote.
#
# WHY A BINARY WRAPPER: Playwright launches the browser by ABSOLUTE PATH out of
# ~/.cache/ms-playwright. No PATH shim and no PreToolUse Bash hook can see that —
# a driver started by an already-running process, an MCP server, or a test runner
# bypasses both. The executable itself is the only place a guarantee exists.
#
# The guard is fail-closed: it refuses unless E2E_REMOTE_OK=1 is exported, which
# e2e-remote does for its remote payload. Installing this on debian1/debian2 is
# therefore harmless — those runs carry the marker and pass straight through.
#
# Idempotent: a binary is already wrapped iff its <name>.real sibling exists.
set -uo pipefail

PW_DIR="${PLAYWRIGHT_BROWSERS_PATH:-$HOME/.cache/ms-playwright}"
MARKER="e2e-headless-guard v1"

usage() {
  cat >&2 <<'USAGE'
usage: install-headless-guard [--uninstall] [--status] [--settle]
                             [--install-units] [--dir <ms-playwright dir>]

Wraps headless-capable Playwright browsers (chromium headless shell, firefox,
webkit) so they refuse to run without E2E_REMOTE_OK=1. Headed
chromium-*/chrome-linux64/chrome is deliberately NOT wrapped: a visible browser
is interactive debugging, not the automated fleet that saturates the machine.

  --uninstall      restore every wrapped binary from its .real sibling
  --status         report wrapped/unwrapped counts, exit 1 if any are unwrapped
  --settle         wrap repeatedly until the browser cache stops changing
                   (a download creates its version dir before extracting the
                   binary into it, so one pass can arrive too early)
  --install-units  link + enable the systemd --user path/timer that run --settle
USAGE
  exit "${1:-2}"
}

MODE=install
while [ $# -gt 0 ]; do
  case "$1" in
    --uninstall) MODE=uninstall; shift ;;
    --status)    MODE=status; shift ;;
    --settle)    MODE=settle; shift ;;
    --install-units) MODE=units; shift ;;
    --dir)       PW_DIR="${2-}"; shift 2 ;;
    -h|--help)   usage 0 ;;
    *)           echo "install-headless-guard: unknown arg: $1" >&2; usage ;;
  esac
done

if [ "$MODE" = units ]; then
  src="$HOME/.claude/systemd/user"
  dst="$HOME/.config/systemd/user"
  mkdir -p "$dst" || exit 1
  for u in headless-guard.service headless-guard.path headless-guard.timer; do
    [ -f "$src/$u" ] || { echo "install-headless-guard: FATAL missing unit $src/$u" >&2; exit 1; }
    ln -sfn "$src/$u" "$dst/$u" || exit 1
  done
  systemctl --user daemon-reload || exit 1
  systemctl --user enable --now headless-guard.path headless-guard.timer >/dev/null || exit 1
  echo "install-headless-guard: units linked from $src and enabled"
  exit 0
fi

[ -d "$PW_DIR" ] || { echo "install-headless-guard: no browser dir at $PW_DIR — nothing to do"; exit 0; }

if [ "$MODE" = settle ]; then
  prev=""; stable=0
  for _ in $(seq 1 120); do
    "$0" --dir "$PW_DIR" || exit 1
    cur="$(find "$PW_DIR" -maxdepth 4 -printf '%T@ %s %p\n' 2>/dev/null | sha256sum)"
    if [ "$cur" = "$prev" ]; then stable=$((stable + 1)); else stable=0; fi
    if [ "$stable" -ge 2 ]; then
      echo "install-headless-guard: cache stable, settled"
      exit 0
    fi
    prev="$cur"
    sleep 15
  done
  echo "install-headless-guard: cache still changing after 30min; everything present is wrapped" >&2
  exit 0
fi

# Headless-capable engines only. Headed chrome is left alone on purpose (see usage).
targets() {
  find "$PW_DIR" -maxdepth 4 -type f \
    \( -name 'chrome-headless-shell' -o -path '*/firefox-*/firefox/firefox' -o -name 'pw_run.sh' \) \
    2>/dev/null | sort
}

write_guard() {
  local dest=$1 real=$2 name=$3
  cat >"$dest" <<GUARD
#!/usr/bin/bash
# $MARKER — DO NOT EDIT. Reinstall with ~/.claude/bin/install-headless-guard
if [ "\${E2E_REMOTE_OK:-}" = "1" ]; then
  exec "$real" "\$@"
fi
cat >&2 <<'MSG'
BLOCKED: $name may not run on this machine.

A headless browser and its dev server are one coupled pair and belong on
debian2/debian1 — run locally they pin ~250% CPU to the laptop. Launch the whole
pair with the wrapper, which starts BOTH halves remotely and rsyncs artifacts back:

  ~/.claude/bin/e2e-remote --server "<dev-server> --host 127.0.0.1 --port <P>" \\
    --wait-port <P> --env PORT=<P> --mkdir <outdir> -- <browser-client>

Deliberate local run (boxes down, headed debugging)? Export E2E_REMOTE_OK=1.
If this fired right after a browser version bump, rerun install-headless-guard.
MSG
exit 97
GUARD
  chmod 755 "$dest"
}

wrapped=0; skipped=0; restored=0; unwrapped=0
while IFS= read -r bin; do
  [ -n "$bin" ] || continue
  real="$bin.real"
  case "$MODE" in
    install)
      if [ -e "$real" ]; then skipped=$((skipped + 1)); continue; fi
      mv -- "$bin" "$real" || { echo "install-headless-guard: FATAL cannot move $bin" >&2; exit 1; }
      if ! write_guard "$bin" "$real" "$(basename "$bin")"; then
        mv -- "$real" "$bin"                      # restore rather than leave it unlaunchable
        echo "install-headless-guard: FATAL cannot write guard at $bin" >&2
        exit 1
      fi
      wrapped=$((wrapped + 1)) ;;
    uninstall)
      [ -e "$real" ] || continue
      mv -f -- "$real" "$bin" && restored=$((restored + 1)) ;;
    status)
      if [ -e "$real" ] && grep -qF "$MARKER" "$bin" 2>/dev/null; then
        wrapped=$((wrapped + 1))
      else
        unwrapped=$((unwrapped + 1)); echo "UNWRAPPED $bin"
      fi ;;
  esac
done <<EOF
$(targets)
EOF

case "$MODE" in
  install)   echo "install-headless-guard: wrapped=$wrapped already=$skipped ($PW_DIR)" ;;
  uninstall) echo "install-headless-guard: restored=$restored ($PW_DIR)" ;;
  status)
    echo "install-headless-guard: wrapped=$wrapped unwrapped=$unwrapped ($PW_DIR)"
    [ "$unwrapped" -eq 0 ] || exit 1 ;;
esac
