#!/bin/sh
# vx-build.ai — use this gateway from Claude Code.
#
#   curl -fsSL https://vx-build.ai/install.sh | sh
#
# Prefer to read before you run? You should:
#   curl -fsSL https://vx-build.ai/install.sh -o vx-install.sh && less vx-install.sh && sh vx-install.sh
#
# Non-interactive:
#   VXB_MODE=tool     VXB_KEY=vxb_... sh vx-install.sh   # MCP server (additive)
#   VXB_MODE=provider VXB_KEY=vxb_... sh vx-install.sh   # replace Claude's provider
#   VXB_MODE=uninstall                sh vx-install.sh   # undo either of them
set -eu

BASE="https://vx-build.ai"
MODEL_DEFAULT="moonshotai/kimi-k2.7-code"
SMALL_DEFAULT="meta-llama/llama-3.2-3b-instruct"
MCP_NAME="${VXB_MCP_NAME:-vx-build}"

# Piped into sh? Then stdin is this script, and any prompt would eat its own
# source, so we talk to the terminal directly. But /dev/tty can EXIST and still
# refuse to open (containers, CI, cron) — testing -r is not enough, so actually
# try to open it and fall back to "no human present".
if [ -t 0 ]; then
  TTY=/dev/stdin
elif { exec 3</dev/tty; } 2>/dev/null; then
  exec 3<&-; TTY=/dev/tty
else
  TTY=""
fi

say() { printf '%s\n' "$*"; }
die() { printf '\n!! %s\n' "$*" >&2; exit 1; }

# Which profile would a block have been written to? Checked for ALL of them on
# uninstall, because $SHELL may have changed since install.
profile_for_shell() {
  case "${SHELL:-}" in
    */zsh)  printf '%s' "$HOME/.zshrc" ;;
    */bash) printf '%s' "$HOME/.bashrc" ;;
    */fish) printf '%s' "" ;;
    *)      printf '%s' "$HOME/.profile" ;;
  esac
}

# Strip the marked block from one file. Uses a temp file + mv rather than
# `sed -i`, whose in-place flag differs between GNU and BSD sed.
strip_block() {
  f="$1"
  [ -f "$f" ] || return 1
  grep -q '# >>> vx-build.ai' "$f" 2>/dev/null || return 1
  sed '/# >>> vx-build.ai/,/# <<< vx-build.ai/d' "$f" > "$f.vxb-tmp" && mv "$f.vxb-tmp" "$f"
  return 0
}

say ""
say "  vx-build.ai — Claude Code setup"
say "  ---------------------------------"
say ""

# ---- 0. mode ----------------------------------------------------------------
MODE="${VXB_MODE:-}"
if [ -z "$MODE" ]; then
  if [ -z "$TTY" ]; then
    die "No terminal to ask. Re-run with VXB_MODE=tool (or provider, or uninstall)."
  fi
  say "  How do you want to use vx-build?"
  say ""
  say "    1) As a TOOL for Claude  (recommended)"
  say "       Claude stays Claude (Opus/Fable) and gains a tool to run"
  say "       open-weight models through vx-build. Nothing else changes."
  say ""
  say "    2) As your PROVIDER"
  say "       Claude Code stops talking to Anthropic and becomes the model you"
  say "       pick below. This turns OFF claude.ai connectors and"
  say "       'claude --remote-control' — they need Anthropic auth."
  say ""
  say "    3) Uninstall — undo either of the above"
  say ""
  printf '  choice [1]: '
  read -r ANS < "$TTY" || ANS=1
  case "${ANS:-1}" in
    1|""|tool)      MODE=tool ;;
    2|provider)     MODE=provider ;;
    3|uninstall)    MODE=uninstall ;;
    *) die "Not a choice: $ANS" ;;
  esac
  say ""
fi

# ---- uninstall --------------------------------------------------------------
if [ "$MODE" = uninstall ]; then
  FOUND=0
  for f in "$HOME/.zshrc" "$HOME/.bashrc" "$HOME/.profile" "$HOME/.zprofile" "$HOME/.bash_profile"; do
    if strip_block "$f"; then say "  Removed the vx-build block from $f"; FOUND=1; fi
  done
  [ "$FOUND" = 1 ] || say "  No vx-build block found in your shell profiles."

  if command -v claude >/dev/null 2>&1; then
    if claude mcp remove "$MCP_NAME" >/dev/null 2>&1; then
      say "  Removed the '$MCP_NAME' MCP server from Claude Code."
    else
      say "  No '$MCP_NAME' MCP server registered (or it was already gone)."
    fi
  fi

  say ""
  say "  This shell still has the old values until you clear them:"
  say ""
  say "    unset ANTHROPIC_BASE_URL ANTHROPIC_AUTH_TOKEN ANTHROPIC_MODEL ANTHROPIC_SMALL_FAST_MODEL"
  say ""
  say "  New terminals are already clean."
  say ""
  exit 0
fi

# ---- 1. the key -------------------------------------------------------------
KEY="${VXB_KEY:-}"
if [ -z "$KEY" ]; then
  [ -n "$TTY" ] || die "No API key. Re-run with:  VXB_KEY=vxb_... sh vx-install.sh"
  say "  Paste your API key (create one at $BASE/dashboard)."
  printf '  key: '
  # No echo: a key pasted into a terminal stays in scrollback otherwise. The
  # trap matters more than the -echo does — under set -e a failed read would
  # otherwise exit with the user's terminal still silent, and they would have to
  # work out for themselves that they need to run: stty sane
  STTY_SAVED=$(stty -g 2>/dev/null || true)
  [ -n "$STTY_SAVED" ] && trap 'stty "$STTY_SAVED" 2>/dev/null || true' EXIT INT TERM
  stty -echo 2>/dev/null || true
  read -r KEY < "$TTY" || KEY=""
  stty echo 2>/dev/null || true
  [ -n "$STTY_SAVED" ] && trap - EXIT INT TERM
  say ""
fi
[ -n "$KEY" ] || die "No key given."
case "$KEY" in vxb_*) ;; *) die "That does not look like a vx-build key (they start with vxb_)." ;; esac

command -v curl >/dev/null 2>&1 || die "curl is required."

# ---- TOOL MODE: register the MCP server ------------------------------------
if [ "$MODE" = tool ]; then
  say "  Checking $BASE/mcp ..."
  OUT=$(curl -s -w '\n%{http_code}' -X POST "$BASE/mcp" \
    -H "Authorization: Bearer $KEY" -H 'content-type: application/json' \
    -d '{"jsonrpc":"2.0","id":1,"method":"tools/list"}' 2>/dev/null) \
    || die "Could not reach $BASE."
  CODE=$(printf '%s' "$OUT" | tail -n1)
  JSON=$(printf '%s' "$OUT" | sed '$d')
  case "$CODE" in
    200) ;;
    401) die "That key was rejected. Check it at $BASE/dashboard." ;;
    *)   die "Unexpected response ($CODE): $JSON" ;;
  esac
  # A JSON-RPC error is still HTTP 200, so the status code alone proves nothing.
  case "$JSON" in *'"tools"'*) say "  OK — the MCP server answered." ;;
    *) die "The endpoint answered but listed no tools: $JSON" ;; esac
  say ""

  CMD="claude mcp add --transport http $MCP_NAME $BASE/mcp --header \"Authorization: Bearer $KEY\""
  if command -v claude >/dev/null 2>&1; then
    # Re-adding under an existing name fails, so clear a previous one first.
    claude mcp remove "$MCP_NAME" >/dev/null 2>&1 || true
    if claude mcp add --transport http "$MCP_NAME" "$BASE/mcp" \
         --header "Authorization: Bearer $KEY" >/dev/null 2>&1; then
      say "  Added '$MCP_NAME' to Claude Code."
      say ""
      say "  Claude is unchanged — same model, connectors and remote control all"
      say "  still work. It now has two extra tools:"
      say "    list_models  — the open-weight models on this gateway"
      say "    chat         — run one and get the reply back"
      say ""
      say "  Try:  cd your-project && claude"
      say "        then ask it to \"use vx-build to summarise these files\""
    else
      say "  Could not register it automatically. Run this yourself:"
      say ""
      say "    $CMD"
    fi
  else
    say "  Claude Code is not on PATH. Once it is, run:"
    say ""
    say "    $CMD"
  fi
  say ""
  say "  Undo:  sh vx-install.sh  (choose 3), or:  claude mcp remove $MCP_NAME"
  say ""
  exit 0
fi

# ---- PROVIDER MODE ----------------------------------------------------------
[ "$MODE" = provider ] || die "Unknown mode: $MODE"

MODEL="${VXB_MODEL:-$MODEL_DEFAULT}"
SMALL="${VXB_SMALL_MODEL:-$SMALL_DEFAULT}"

# ---- prove it works BEFORE telling anyone to trust it -----------------------
say "  Checking $BASE ..."
BODY='{"model":"'"$MODEL"'","max_tokens":16,"messages":[{"role":"user","content":"reply with: ok"}]}'
OUT=$(curl -s -w '\n%{http_code}' -X POST "$BASE/v1/messages" \
  -H "x-api-key: $KEY" -H 'content-type: application/json' -d "$BODY" 2>/dev/null) || die "Could not reach $BASE."
CODE=$(printf '%s' "$OUT" | tail -n1)
JSON=$(printf '%s' "$OUT" | sed '$d')

case "$CODE" in
  200) say "  OK — $MODEL answered." ;;
  401) die "That key was rejected. Check it at $BASE/dashboard." ;;
  402) die "Not enough credit on this account. Top up at $BASE/dashboard/billing." ;;
  404) die "This gateway is not serving /v1/messages right now." ;;
  400) say "  The gateway answered, but refused the model:"
       printf '  %s\n' "$JSON"
       die "Pick another with:  VXB_MODEL=<slug> sh vx-install.sh" ;;
  *)   die "Unexpected response ($CODE): $JSON" ;;
esac
say ""

# The small/fast slot gets short background tasks on modest budgets. A REASONING
# model there spends the whole budget thinking and returns an EMPTY string — no
# error, just silence — which is what qwen3.7-flash did as the old default. One
# probe with a deliberately small budget catches exactly that, for any model.
say "  Checking $SMALL (the small/fast model) ..."
SBODY='{"model":"'"$SMALL"'","max_tokens":32,"messages":[{"role":"user","content":"Reply with a 3-word title for: a login bug"}]}'
SOUT=$(curl -s -X POST "$BASE/v1/chat/completions"   -H "Authorization: Bearer $KEY" -H 'content-type: application/json' -d "$SBODY" 2>/dev/null) || SOUT=""
case "$SOUT" in
  *'"content":null'*|*'"content":""'*)
    say "  !! $SMALL returned an EMPTY reply on a small budget."
    say "     That usually means it is a REASONING model, which is a poor fit for"
    say "     the small/fast slot — background tasks will silently come back blank."
    say "     Pick another with:  VXB_SMALL_MODEL=<slug> sh vx-install.sh"
    say "" ;;
  *'"content"'*) say "  OK — $SMALL replied." ;;
  *) say "  (could not check $SMALL — continuing)" ;;
esac
say ""

# Say the cost of this mode out loud, every time — including when the mode came
# from VXB_MODE and the menu never printed it.
say "  Heads up — while these are set, Claude Code is $MODEL, not Claude:"
say "    · claude.ai connectors are disabled"
say "    · 'claude --remote-control' will not work"
say "    · /model inside Claude Code will not reach Anthropic models"
say "  Undo any time with:  sh vx-install.sh   (choose 3)"
say ""

# ---- hand over the settings -------------------------------------------------
LINES="export ANTHROPIC_BASE_URL=$BASE
export ANTHROPIC_AUTH_TOKEN=$KEY
export ANTHROPIC_MODEL=$MODEL
export ANTHROPIC_SMALL_FAST_MODEL=$SMALL"

PROFILE=$(profile_for_shell)

say "  Add these to your shell, then start claude:"
say ""
printf '%s\n' "$LINES" | sed 's/^/    /'
say ""

# Appending to somebody's shell profile is a change to every future terminal
# they open, so it is opt-in and never the default.
if [ -n "$PROFILE" ] && [ -n "$TTY" ]; then
  printf '  Append them to %s? [y/N] ' "$PROFILE"
  read -r ANS < "$TTY" || ANS=n
  case "$ANS" in
    y|Y|yes|YES)
      # Replace a previous block rather than stacking duplicates that quietly
      # disagree with each other.
      if strip_block "$PROFILE"; then say "  (replaced the previous vx-build block)"; fi
      { printf '\n# >>> vx-build.ai\n%s\n# <<< vx-build.ai\n' "$LINES"; } >> "$PROFILE"
      say "  Written to $PROFILE — open a new terminal, or: . $PROFILE"
      ;;
    *) say "  Not written. Copy the lines above when you are ready." ;;
  esac
fi

say ""
say "  Then:  cd your-project && claude"
say "  Undo:  sh vx-install.sh   (choose 3) — removes the block AND this shell's"
say "         values are cleared with:"
say "         unset ANTHROPIC_BASE_URL ANTHROPIC_AUTH_TOKEN ANTHROPIC_MODEL ANTHROPIC_SMALL_FAST_MODEL"
say ""
