#!/usr/bin/env bash
# _quietlsp-shim.sh — shared body for the typescript-language-server and
# rust-analyzer PATH shims (symlinks to this file, dispatched by $0 basename,
# same house pattern as _cpu-guard-shim.sh). Routes the real language server
# through QuietLSP (https://github.com/alexcodeplace/quietlsp) so Claude
# Code's LSP feature only ever receives diagnostics for the session's own
# cwd subtree. Fail-open: if the quietlsp repo, its wrapper, or node is
# unavailable, this execs the real server directly — a missing product repo
# must never break LSP.
#
# OD_PATH_SHIM_MARKER
set -e
name="${0##*/}"
SHIM_DIR="$(cd "$(dirname "$(readlink -f -- "${BASH_SOURCE[0]}")")" && pwd)"

GUARD_LIB="$SHIM_DIR/../lib/shim-guard.sh"
if [[ ! -r "$GUARD_LIB" ]]; then
  echo "$name: quietlsp shim cannot read $GUARD_LIB — refusing" >&2
  exit 78
fi
# shellcheck source=../lib/shim-guard.sh
source "$GUARD_LIB"
shim_guard_enter "$name"

# SANITIZED_PATH — $PATH with SHIM_DIR removed, used for every subprocess this
# shim spawns (candidate --version probes AND the final real-binary exec).
# Confirmed on this box: ~/.cargo/bin/rust-analyzer (a rustup toolchain
# proxy) does its OWN internal PATH search for "rust-analyzer" when the
# active toolchain lacks the component, landing on whatever is first on
# PATH — with SHIM_DIR present that is this shim itself, reproduced as an
# actual unbounded fork loop (10+ recursive re-invocations before an outer
# `timeout` killed it; shim_guard's re-entry bound does not catch it because
# each recursive hop is a genuinely new process rustup spawns, not this
# script re-`exec`ing itself). Stripping SHIM_DIR from every subprocess's
# PATH makes that structurally impossible, not just less likely — and it's
# free protection against any other proxy binary with the same habit.
SANITIZED_PATH=""
IFS=':' read -ra _quietlsp_path_parts <<<"$PATH"
for _quietlsp_d in "${_quietlsp_path_parts[@]}"; do
  [[ "$_quietlsp_d" == "$SHIM_DIR" ]] && continue
  SANITIZED_PATH="${SANITIZED_PATH:+$SANITIZED_PATH:}$_quietlsp_d"
done
unset _quietlsp_path_parts _quietlsp_d

# quietlsp_resolve_validated <name> <shim_dir> — walks PATH past shim_dir like
# shim_resolve_real, but additionally runs each candidate's `--version`
# (PATH=$SANITIZED_PATH, 2s timeout) and requires exit 0 before accepting it.
# A rustup-style proxy binary is executable and not a shim, so the base
# resolver would accept it; it only reveals itself as broken when actually
# invoked, and even invoking it is unsafe (see SANITIZED_PATH above) —  so a
# candidate that readlink -f's to a binary literally named "rustup" is
# skipped outright, never validated, never used as the last-resort fallback
# either. Sets QUIETLSP_RESOLVED_REAL to the first candidate that passes
# validation, and QUIETLSP_FALLBACK_REAL to the first non-rustup-proxy
# candidate seen at all (this shim's fail-open default when nothing
# validates). Prints nothing; rc 1 only when PATH holds no usable candidate.
quietlsp_resolve_validated() {
  local cmd="$1" shim_dir="$2" d cand target
  local -a parts
  QUIETLSP_RESOLVED_REAL=""
  QUIETLSP_FALLBACK_REAL=""
  IFS=':' read -ra parts <<<"$PATH"
  for d in "${parts[@]}"; do
    [[ -n "$d" && "$d" != "$shim_dir" ]] || continue
    cand="$d/$cmd"
    [[ -x "$cand" && ! -d "$cand" ]] || continue
    shim_is_shim "$cand" && continue
    target="$(readlink -f -- "$cand" 2>/dev/null || printf '%s' "$cand")"
    [[ "${target##*/}" == rustup ]] && continue
    [[ -z "$QUIETLSP_FALLBACK_REAL" ]] && QUIETLSP_FALLBACK_REAL="$cand"
    if PATH="$SANITIZED_PATH" timeout 2 "$cand" --version >/dev/null 2>&1; then
      QUIETLSP_RESOLVED_REAL="$cand"
      return 0
    fi
  done
  [[ -n "$QUIETLSP_FALLBACK_REAL" ]]
}

quietlsp_resolve_validated "$name" "$SHIM_DIR" || true
if [[ -n "$QUIETLSP_RESOLVED_REAL" ]]; then
  real="$QUIETLSP_RESOLVED_REAL"
elif [[ -n "$QUIETLSP_FALLBACK_REAL" ]]; then
  # Every non-rustup-proxy candidate failed --version validation — fail open
  # to the first one anyway. A broken server surfacing its own real error to
  # the client beats this shim silently pretending no server exists at all.
  real="$QUIETLSP_FALLBACK_REAL"
  mkdir -p -- "$HOME/.local/state/overdeck" 2>/dev/null || true
  printf '%s %s: no PATH candidate passed --version validation — using first candidate %s anyway\n' \
    "$(date -Is 2>/dev/null || date)" "$name" "$real" \
    >>"${OD_QUIETLSP_LOG:-$HOME/.local/state/overdeck/quietlsp.log}" 2>/dev/null || true
else
  echo "$name: command not found (quietlsp shim found no usable real binary outside $SHIM_DIR — a rustup toolchain proxy stub does not count)" >&2
  exit 127
fi
shim_guard_clear "$name"

if [[ "$SHIM_REENTRY" == 1 ]]; then
  PATH="$SANITIZED_PATH" exec "$real" "$@"
fi

QUIETLSP_REPO="${OD_QUIETLSP_REPO:-$HOME/Projects/quietlsp}"
QUIETLSP_BIN="$QUIETLSP_REPO/quietlsp"
NODE_BIN="${OD_QUIETLSP_NODE:-}"
[[ -n "$NODE_BIN" ]] || NODE_BIN="$(PATH="$SANITIZED_PATH" command -v node 2>/dev/null || true)"
LOG_FILE="${OD_QUIETLSP_LOG:-$HOME/.local/state/overdeck/quietlsp.log}"

# fail_open <reason> — log once, then exec the real server unfiltered. Only
# ever called before quietlsp has spoken on stdio, never after: once a
# filtering process owns the LSP stream, handing off mid-stream would
# corrupt it, so every fitness check below runs pre-exec.
fail_open() {
  local reason="$1"
  shift
  mkdir -p -- "$(dirname -- "$LOG_FILE")" 2>/dev/null || true
  printf '%s %s: %s — falling back to real %s\n' \
    "$(date -Is 2>/dev/null || date)" "$name" "$reason" "$real" >>"$LOG_FILE" 2>/dev/null || true
  PATH="$SANITIZED_PATH" exec "$real" "$@"
}

[[ -x "$QUIETLSP_BIN" ]] || fail_open "no executable quietlsp wrapper at $QUIETLSP_BIN" "$@"
[[ -n "$NODE_BIN" && -x "$NODE_BIN" ]] || fail_open "no usable node binary" "$@"

PATH="$SANITIZED_PATH" exec "$NODE_BIN" "$QUIETLSP_BIN" "$real" "$@"
