# vast-gpu-run: how one executor call is made, captured and recorded.
# Sourced, never executed. Expects EXEC, ROOT, GUARD, VIEW, TOKEN, token_file,
# evidence_dir, stamp, explain and `state` to be in scope.
#
# This file exists so the front door can be about *which* call to make. The
# capture rules are subtle enough to deserve their own home: the evidence
# directory must not exist before the executor sees it, and a reader that stops
# early must not abort the call under `set -euo pipefail`.

# capture_into_dir <dir> <args...>: output is captured outside the evidence
# directory and moved in after the call, so the executor still sees the fresh
# path it requires. Redirecting straight into it creates the path early and the
# call fails with evidence_directory_create_failed. The caller owns errexit:
# re-enabling it here would abort the shell on the non-zero return.
capture_into_dir() {
  local dir="$1"; shift
  local out err
  out="$(mktemp)"; err="$(mktemp)"
  "$EXEC" "$@" --evidence-dir "$dir" --token-file "$(token_file)" > "$out" 2> "$err"
  local code=$?
  if [ ! -d "$dir" ]; then mkdir -p "$dir"; chmod 700 "$dir"; fi
  mv "$out" "$dir/stdout.txt"; mv "$err" "$dir/stderr.txt"
  return $code
}

# The same capture discipline without injecting --evidence-dir, for subcommands
# that reject flags they do not know (`preflight` accepts only --plan,
# --timeout-secs and --token-file) or that were given one by the caller.
capture_plain() {
  local dir="$1"; shift
  local out err
  out="$(mktemp)"; err="$(mktemp)"
  "$EXEC" "$@" --token-file "$(token_file)" > "$out" 2> "$err"
  local code=$?
  if [ ! -d "$dir" ]; then mkdir -p "$dir"; chmod 700 "$dir"; fi
  mv "$out" "$dir/stdout.txt"; mv "$err" "$dir/stderr.txt"
  return $code
}

# run [--plain] <label> <args...>: one call, one evidence directory, one record.
run() {
  local plain=""
  if [ "${1:-}" = "--plain" ]; then plain=1; shift; fi
  local label="$1"; shift
  local dir; dir="$(evidence_dir "$label")"
  local started; started="$(python3 -c 'import time;print(time.time())')"
  echo "evidence: $dir" >&2
  local code
  set +e
  if [ -n "$plain" ]; then capture_plain "$dir" "$@"; else capture_into_dir "$dir" "$@"; fi
  code=$?
  set -e
  local seconds; seconds="$(python3 -c "import time;print(round(time.time()-$started,2))")"
  local meaning; meaning="$(explain "$code")"
  state record "$dir" "${label%%-*}" "$code" "$meaning" \
    "$(tail -c 400 "$dir/stderr.txt" | tr '\n' ' ')" "$seconds" >/dev/null
  while IFS= read -r line; do printf '%s\n' "$line"; done \
    < <(grep -v '^[[:space:]]*$' "$dir/stdout.txt" 2>/dev/null | tail -40 || true)
  if [ -s "$dir/stderr.txt" ]; then cat "$dir/stderr.txt" >&2; fi
  echo "exit $code: $meaning"
  rate_limit_note "$dir"
  return $code
}

# run_stdout <label> <args...>: the command prints its receipt instead of taking
# an --evidence-dir, so the wrapper keeps a copy. Never gate the tail on "grep
# found a line": on empty output that returns 1, and under `set -e` it swallowed
# both the real error and the exit code, so a schema rejection looked silent.
run_stdout() {
  local label="$1"; shift
  local dir; dir="$(evidence_dir "$label")"
  mkdir -p "$dir"; chmod 700 "$dir"
  local started; started="$(python3 -c 'import time;print(time.time())')"
  echo "evidence: $dir" >&2
  local code
  set +e
  "$EXEC" "$@" --token-file "$(token_file)" > "$dir/stdout.json" 2> "$dir/stderr.txt"
  code=$?
  set -e
  local seconds; seconds="$(python3 -c "import time;print(round(time.time()-$started,2))")"
  local summary
  if [ -s "$dir/stdout.json" ]; then
    # `|| true` is required: under `set -euo pipefail` a reader that stops
    # early (head) makes the writer die on SIGPIPE, the pipeline returns
    # non-zero, and the assignment aborts the whole call before it prints
    # anything. That is how `offers` started exiting 1 with no output at all
    # once its table grew past a pipe buffer.
    summary="$(view "$label" "$dir" 2>/dev/null | head -2 | tr '\n' ' ' || true)"
  else
    summary="no stdout; stderr: $(head -c 240 "$dir/stderr.txt" | tr '\n' ' ')"
  fi
  state record "$dir" "$label" "$code" "$(explain "$code")" "$summary" "$seconds" >/dev/null
  # The receipt is not echoed here: the verb prints its own projection, and
  # --json prints the receipt. Dumping both buried the answer under 60 lines of
  # policy JSON, which is why every caller used to write its own parser.
  if [ -s "$dir/stderr.txt" ]; then cat "$dir/stderr.txt" >&2; fi
  # The verdict line is printed by the verb, after its projection: a reader
  # wants the table first and "did it work" second, not the reverse.
  rate_limit_note "$dir"
  return $code
}

rate_limit_note() {
  if grep -q 'HTTP error 429' "$1/stderr.txt" 2>/dev/null; then
    echo "vast-gpu: the provider rate limited this call. Re-sending the same query makes it worse." >&2
    echo "vast-gpu: change a query flag or wait ${RATE_COOLDOWN}s; identical repeats inside that window are refused." >&2
  fi
}

# Read-only market queries are the ones that get rate limited, and the ones an
# agent re-asks when a filter was wrong. Twenty identical reads in thirty seconds
# earned HTTP 429 on 2026-09-22; the wall was self-inflicted.
guarded_read() {
  local label="$1"; shift
  # --force and --json belong to this wrapper; the executor would reject both.
  local force="" args=()
  for a in "$@"; do
    case "$a" in
      --force) force=1 ;;
      --json) VAST_JSON=1 ;;
      *) args+=("$a") ;;
    esac
  done
  local key; key="$(state query-key "$label" ${args[@]+"${args[@]}"})"
  if [ -z "$force" ]; then
    local refused=0
    set +e; state guard-check "$GUARD" "$key" "$COOLDOWN"; refused=$?; set -e
    [ $refused -eq 0 ] || return $refused
  fi
  local code
  set +e; run_stdout "$label" ${args[@]+"${args[@]}"}; code=$?; set -e
  local dir; dir="$(ls -td "$ROOT"/*"-$label" | head -1)"
  local cd="$COOLDOWN"
  if grep -q 'HTTP error 429' "$dir/stderr.txt" 2>/dev/null; then
    cd="$RATE_COOLDOWN"
  fi
  # The reminder on a refused repeat is the verdict of the last attempt, not
  # the first byte of its receipt.
  local first
  first="$(view "$label" "$dir" 2>/dev/null | grep -m1 -E 'qualified|selected|no offer|provider returned' || true)"
  state guard-set "$GUARD" "$key" "$code" "$dir" "$first" "$cd" >/dev/null
  return $code
}
