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

here="$(cd -- "$(dirname -- "$0")" && pwd)"
# shellcheck source=.agents/shared/cse-toold-resolver.sh
source "$here/cse-toold-resolver.sh"

install=0
optional=0
version_timeout="${CSE_TOOLD_VERSION_TIMEOUT_SECONDS:-5}"
smoke_pid=""
smoke_tmp_out=""
smoke_tmp_err=""

cleanup_version_smoke() {
  if [ -n "$smoke_pid" ]; then
    kill "$smoke_pid" 2>/dev/null || true
    wait "$smoke_pid" 2>/dev/null || true
  fi
  rm -f "$smoke_tmp_out" "$smoke_tmp_err"
}
trap cleanup_version_smoke EXIT INT TERM

while [ "$#" -gt 0 ]; do
  case "$1" in
    --install)
      install=1
      shift
      ;;
    --optional)
      optional=1
      shift
      ;;
    --)
      shift
      break
      ;;
    *)
      break
      ;;
  esac
done

# Subcommand is the first positional after the flags. `emit_stale_binary_panic`
# writes a `{systemMessage}` JSON line to stdout, which is only a valid contract
# on the SessionStart hook; on any MCP/proxy stdio channel it would corrupt the
# JSON-RPC handshake. Capture it so the panic is gated to the hook alone.
subcmd="${1:-}"

warn_missing() {
  if [ "${CSE_TOOLD_EXEC_WARNED:-0}" != "1" ]; then
    _cse_toold_diagnostics "cse-toold-exec"
    if root="$(_cse_toold_install_root 2>/dev/null)"; then
      printf '[cse-toold-exec] recovery: rm -rf %q && relaunch Claude Code to force marketplace resync\n' "$root" >&2
    else
      printf '[cse-toold-exec] recovery: rm -rf ~/.claude-work/plugins/cache/cse-tools/cse-tools && relaunch Claude Code to force marketplace resync\n' >&2
    fi
    export CSE_TOOLD_EXEC_WARNED=1
  fi
}

run_version_smoke() {
  local bin="$1" elapsed limit
  smoke_tmp_out="$(mktemp "${TMPDIR:-/tmp}/cse-toold-version.XXXXXX")"
  smoke_tmp_err="$(mktemp "${TMPDIR:-/tmp}/cse-toold-version-err.XXXXXX")"
  "$bin" --version >"$smoke_tmp_out" 2>"$smoke_tmp_err" &
  smoke_pid=$!
  elapsed=0
  limit=$((version_timeout * 10))
  while kill -0 "$smoke_pid" 2>/dev/null; do
    if [ "$elapsed" -ge "$limit" ]; then
      kill "$smoke_pid" 2>/dev/null || true
      wait "$smoke_pid" 2>/dev/null || true
      smoke_pid=""
      rm -f "$smoke_tmp_out" "$smoke_tmp_err"
      smoke_tmp_out=""
      smoke_tmp_err=""
      return 1
    fi
    sleep 0.1
    elapsed=$((elapsed + 1))
  done
  # `wait` redirected to /dev/null so bash's own job-control SIGKILL report
  # ("Killed: 9") does not leak to stderr. Caller decides whether to retry.
  if ! wait "$smoke_pid" 2>/dev/null; then
    smoke_pid=""
    rm -f "$smoke_tmp_out" "$smoke_tmp_err"
    smoke_tmp_out=""
    smoke_tmp_err=""
    return 1
  fi
  smoke_pid=""
  rm -f "$smoke_tmp_out" "$smoke_tmp_err"
  smoke_tmp_out=""
  smoke_tmp_err=""
}

run_version_smoke_with_retry() {
  # Catches the post-install amfid validation race after a plugin update:
  # macOS's code-signing daemon may SIGKILL the first exec of a freshly
  # extracted signed binary before its CDHash is cached. Linear backoff
  # gives amfid time to validate; subsequent execs hit the cache.
  local bin="$1"
  if run_version_smoke "$bin"; then return 0; fi
  sleep 0.5
  if run_version_smoke "$bin"; then return 0; fi
  sleep 1
  if run_version_smoke "$bin"; then return 0; fi
  sleep 2
  run_version_smoke "$bin"
}

emit_stale_binary_panic() {
  # SessionStart-hook JSON contract: stdout `{systemMessage,...}` shows a
  # warning in the transcript; `suppressOutput:true` keeps the raw JSON out
  # of Claude's context. Other hook events ignore systemMessage entirely.
  local version="" root msg
  if root="$(_cse_toold_install_root 2>/dev/null)" && [ -f "$root/package.json" ]; then
    version="$(sed -n 's/.*"version"[[:space:]]*:[[:space:]]*"\([^"]*\)".*/\1/p' "$root/package.json" | head -n1)"
  fi
  if [ -n "$version" ]; then
    msg="cse-tools just updated to $version. macOS is validating the new binary in the background. Quit Claude Code (Cmd+Q) and reopen to complete the update."
  else
    msg="cse-tools plugin updated. macOS is validating the new binary in the background. Quit Claude Code (Cmd+Q) and reopen to complete the update."
  fi
  msg="${msg//\\/\\\\}"
  msg="${msg//\"/\\\"}"
  printf '{"systemMessage":"%s","suppressOutput":true}\n' "$msg"
}

repair_install() {
  # Single overall deadline shared by lock wait + npm repair. Default 30s stays
  # under the host SessionStart hook's 45s timeout so a hung npm cannot stall
  # host startup. Override with CSE_TOOLD_INSTALL_TIMEOUT_SECONDS.
  local root lock deadline now npm_pid rc
  local install_timeout="${CSE_TOOLD_INSTALL_TIMEOUT_SECONDS:-30}"
  root="$(_cse_toold_install_root)" || {
    printf '[cse-toold-exec] no plugin root with package.json available for repair\n' >&2
    return 1
  }
  lock="$root/.cse-toold-install.lock"
  deadline=$(( $(date +%s) + install_timeout ))

  while ! mkdir "$lock" 2>/dev/null; do
    if _cse_export_toold_bin; then
      return 0
    fi
    now="$(date +%s)"
    if [ "$now" -ge "$deadline" ]; then
      printf '[cse-toold-exec] timed out waiting for install lock: %s\n' "$lock" >&2
      return 1
    fi
    sleep 1
  done

  trap 'rmdir "$lock" 2>/dev/null || true' RETURN
  if _cse_export_toold_bin; then
    return 0
  fi

  now="$(date +%s)"
  if [ "$now" -ge "$deadline" ]; then
    printf '[cse-toold-exec] timed out before npm repair under %s\n' "$root" >&2
    return 1
  fi

  printf '[cse-toold-exec] repairing cse-toold dependency under %s\n' "$root" >&2
  npm install \
    --prefix "$root" \
    --omit=dev \
    --include=optional \
    --no-audit \
    --no-fund \
    --package-lock=false \
    --loglevel=error \
    1>&2 &
  npm_pid=$!
  # Poll once per second against the shared deadline (not a fresh full wait).
  while kill -0 "$npm_pid" 2>/dev/null; do
    now="$(date +%s)"
    if [ "$now" -ge "$deadline" ]; then
      kill "$npm_pid" 2>/dev/null || true
      sleep 0.2
      if kill -0 "$npm_pid" 2>/dev/null; then
        kill -9 "$npm_pid" 2>/dev/null || true
      fi
      wait "$npm_pid" 2>/dev/null || true
      printf '[cse-toold-exec] timed out repairing cse-toold dependency under %s (limit %ss)\n' \
        "$root" "$install_timeout" >&2
      return 1
    fi
    sleep 1
  done
  rc=0
  wait "$npm_pid" 2>/dev/null || rc=$?
  if [ "$rc" -ne 0 ]; then
    printf '[cse-toold-exec] npm repair failed under %s (exit %s)\n' "$root" "$rc" >&2
    return 1
  fi
  _cse_export_toold_bin
}

if ! _cse_export_toold_bin; then
  if [ "$install" = "1" ]; then
    repair_install || true
  fi
fi

if ! _cse_export_toold_bin; then
  warn_missing
  if [ "$optional" = "1" ]; then
    exit 0
  fi
  exit 1
fi

[ -x "$CSE_TOOLD_BIN" ] || {
  printf '[cse-toold-exec] resolved cse-toold is not executable: %s\n' "$CSE_TOOLD_BIN" >&2
  [ "$optional" = "1" ] && exit 0
  exit 1
}

# The amfid retry (0.5+1+2s backoff between `--version` execs) only matters on
# the install/SessionStart path, where a freshly-extracted signed binary can be
# SIGKILLed by macOS code-signing validation before its CDHash caches. Optional
# hot-path callers (--optional) get a SINGLE fast smoke and fail open on failure
# -- a transiently-slow binary must never burn the hook timeout on retry sleeps.
if [ "$install" = "1" ] && [ "$subcmd" = "session-start-hook" ]; then
  if ! run_version_smoke_with_retry "$CSE_TOOLD_BIN"; then
    emit_stale_binary_panic
    exit 0
  fi
elif [ "$optional" = "1" ]; then
  run_version_smoke "$CSE_TOOLD_BIN" || exit 0
else
  if ! run_version_smoke_with_retry "$CSE_TOOLD_BIN"; then
    printf '[cse-toold-exec] cse-toold --version failed after retries\n' >&2
    exit 1
  fi
fi

exec "$CSE_TOOLD_BIN" "$@"
