#!/usr/bin/env bash
# cage: graphify metering interceptor — routes queries through `cage interceptor graphify` so
# each query/path/explain files a token-saving receipt; stdout/exit pass through unchanged.
# `graphify update .` and any non-query verb pass straight through (nothing to meter).
# Installed by `cage setup`.
#
# Recursion guard (capture-precision §3.5): the "real" graphify is resolved by scanning
# PATH and SKIPPING **every** cage interceptor — not just this dir. Two stacked shims
# (e.g. a fresh one plus a stale `cage adopt` one) each used to strip only their own dir,
# so they resolved to *each other* and recursed forever. Skipping all interceptors, plus
# the CAGE_GRAPHIFY_SHIM re-entry guard below, makes a resolution loop impossible.
set -euo pipefail

# Second-layer guard: if we're already inside a cage metering shim, do not meter again —
# go straight to the real binary. Breaks any residual loop cage's own resolution missed.
if [ "${CAGE_GRAPHIFY_SHIM:-}" = "1" ]; then _CAGE_GF_REENTRY=1; else _CAGE_GF_REENTRY=0; fi

# An interceptor is any `graphify` that routes through cage or self-identifies as one.
# The marker set is ADDITIVE and only ever GROWS (ADR-GRAPHIFY B3): it must match the
# current shim (`cage interceptor graphify`), the SURFACE-CUT-era one (`cage data
# graphify`), and a stale pre-rename adopt shim (`cage graphify` / "graphify metering
# interceptor"), so none of them can be picked as REAL. Dropping an old spelling would
# let a new twin select an OLD twin already installed on a real disk as the real binary,
# holing the anti-recursion proof for exactly the machines that never get healed.
_cage_is_interceptor() { grep -Eq 'cage (data |interceptor )?graphify|graphify metering interceptor' "$1" 2>/dev/null; }

REAL=""
_OIFS=$IFS; IFS=:
for _d in $PATH; do
  [ -n "$_d" ] || continue
  _cand="$_d/graphify"
  [ -x "$_cand" ] || continue
  if _cage_is_interceptor "$_cand"; then continue; fi   # skip ALL cage shims
  REAL="$_cand"; break
done
IFS=$_OIFS

if [ -z "$REAL" ]; then
  # Only cage interceptors are on PATH — the real graphify is not installed. Refuse to
  # fall back to the bare name (that would re-enter a shim and recurse); fail cleanly.
  echo "graphify: not found — only the metering interceptor shim is on PATH" >&2
  exit 127
fi

# B5: meter only when cage can actually RUN the verb — two arms, tried in order.
# Arm 1 is the `cage` command and always wins first, so standard installs are unchanged
# in both behaviour and latency.
if [ "$_CAGE_GF_REENTRY" = "0" ]; then
  if command -v cage >/dev/null 2>&1 && cage interceptor graphify --help >/dev/null 2>&1; then
    CAGE_GRAPHIFY_SHIM=1 exec cage interceptor graphify -- "$REAL" "$@"
  fi
  # Arm 2 (B5b, GF-LAUNCHER verdict B): "no `cage` COMMAND" is not "no cage". The probe
  # used to ask *is there a cage command*; the question it means is *can cage run*.
  # `cage setup --python-launcher` removes the command by design, so under it BOTH twins
  # metered nothing — but the same miss covers a cage.pyz on PYTHONPATH, an unactivated
  # venv, and any importable-but-not-on-PATH install. Costs one interpreter start
  # (~50 ms warm) and only on the path that was already going to run unmetered.
  if command -v python3 >/dev/null 2>&1 \
     && python3 -m cage interceptor graphify --help >/dev/null 2>&1; then
    CAGE_GRAPHIFY_SHIM=1 exec python3 -m cage interceptor graphify -- "$REAL" "$@"
  fi
fi
exec "$REAL" "$@"   # cage unreachable, or re-entry → identical, unmetered behaviour
