#!/usr/bin/env bash
# Point Claude Code (and penny-compare's NATIVE pane) at an OpenAI/Anthropic-compatible
# proxy, and put it back.
#
# This does two things together, since both are needed to fully switch over:
#
#  1. Writes ANTHROPIC_BASE_URL / ANTHROPIC_AUTH_TOKEN into ~/.claude/settings.json's
#     `env` block, which is what a bare `claude` invocation reads on startup. Every
#     other setting in that file is preserved -- the edit is a key-level merge, not a
#     rewrite.
#  2. Writes PENNY_COMPARE_NATIVE_BASE_URL / PENNY_COMPARE_NATIVE_AUTH_TOKEN into this
#     repo's .env file. penny-compare's pane.py reads those two vars from its own
#     process environment when run with --native-proxy, and injects them into the
#     NATIVE pane's subprocess as ANTHROPIC_BASE_URL / ANTHROPIC_AUTH_TOKEN. Without
#     this, --native-proxy has nothing to read and the NATIVE pane falls back to
#     Anthropic direct even with settings.json pointed at the proxy, because pane.py
#     strips ambient ANTHROPIC_* vars before optionally reinjecting its own.
#  3. Sets settings.json's disableClaudeAiConnectors to true while the proxy is on.
#     Once ANTHROPIC_BASE_URL/ANTHROPIC_AUTH_TOKEN override auth, Claude Code is no
#     longer authenticated against claude.ai, so claude.ai-hosted MCP connectors
#     (Gmail, Calendar, Drive, etc.) are unreachable regardless -- this just says so
#     up front instead of showing the "disabled because an auth source is set"
#     warning on every startup. Cleared on `off`.
#
#   claude-proxy.sh on [url]   point Claude Code (and --native-proxy) at the proxy
#   claude-proxy.sh off        remove it, back to Anthropic direct everywhere
#   claude-proxy.sh status     show what is set right now, for both
#
# The token is read from CLAUDE_PROXY_TOKEN, or prompted for without echo.
# It is never passed on the command line, where it would land in shell history.
#
# export CLAUDE_PROXY_URL=https://litellm.core.gtri.org
# export CLAUDE_PROXY_TOKEN=sk-example
# ./claude-proxy.sh on
#
# The first `on` backs up settings.json and .env once per invocation (whichever
# already exist), before any write. Recoverable from settings.json.bak.<timestamp>
# and .env.bak.<timestamp> in ~/.claude and this repo respectively.

set -euo pipefail

SCRIPT_DIR="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd)"
ENV_FILE="${PROXY_ENV_FILE:-$SCRIPT_DIR/.env}"
if [ -f "$ENV_FILE" ]; then
  set -a
  . "$ENV_FILE"
  set +a
fi

SETTINGS="${CLAUDE_SETTINGS:-$HOME/.claude/settings.json}"
# Falls back to whatever URL is already saved in .env from the last `on` run,
# so a bare `./claude-proxy.sh on` reuses it without retyping.
DEFAULT_URL="${CLAUDE_PROXY_URL:-${PENNY_COMPARE_NATIVE_BASE_URL:-}}"

NATIVE_URL_VAR="PENNY_COMPARE_NATIVE_BASE_URL"
NATIVE_TOKEN_VAR="PENNY_COMPARE_NATIVE_AUTH_TOKEN"

die() { echo "error: $*" >&2; exit 1; }

command -v python3 >/dev/null || die "python3 is required"

# Claude Code creates this on first run; an install that has never been launched
# has no file, and writing one here would guess at the rest of its contents.
[ -f "$SETTINGS" ] || die "no settings file at $SETTINGS (run claude once first)"

# One backup per run, before any write.
backup() {
  local dest="$SETTINGS.bak.$(date +%Y%m%dT%H%M%SZ)"
  cp "$SETTINGS" "$dest"
  echo "backed up settings to $dest"

  if [ -f "$ENV_FILE" ]; then
    local env_dest="$ENV_FILE.bak.$(date +%Y%m%dT%H%M%SZ)"
    cp "$ENV_FILE" "$env_dest"
    echo "backed up .env to $env_dest"
  fi
}

# Merge or delete the two env keys in settings.json. Reads the file, edits the
# parsed object, and writes to a temp file in the same directory before renaming
# over the original, so an interrupted run cannot leave a half-written file behind.
edit_settings() {
  ACTION="$1" URL="${2:-}" TOKEN="${3:-}" SETTINGS="$SETTINGS" python3 <<'PY'
import json, os, tempfile

path = os.environ["SETTINGS"]
action = os.environ["ACTION"]

with open(path) as fh:
    settings = json.load(fh)

env = settings.setdefault("env", {})
if action == "on":
    env["ANTHROPIC_BASE_URL"] = os.environ["URL"]
    env["ANTHROPIC_AUTH_TOKEN"] = os.environ["TOKEN"]
    # claude.ai connectors rely on claude.ai OAuth; a proxy auth source means
    # Claude Code is no longer authenticated against that account, so the
    # connectors would be unreachable anyway -- this just says so up front.
    settings["disableClaudeAiConnectors"] = True
else:
    env.pop("ANTHROPIC_BASE_URL", None)
    env.pop("ANTHROPIC_AUTH_TOKEN", None)
    # Leave no empty env block behind if this script put everything in it.
    if not env:
        settings.pop("env")
    settings.pop("disableClaudeAiConnectors", None)

directory = os.path.dirname(path) or "."
with tempfile.NamedTemporaryFile("w", dir=directory, delete=False) as tmp:
    json.dump(settings, tmp, indent=2)
    tmp.write("\n")
    temp_path = tmp.name
os.chmod(temp_path, 0o600)
os.replace(temp_path, path)
PY
}

# Remove any existing line for the given var name from .env.
strip_env_var() {
  local var="$1"
  [ -f "$ENV_FILE" ] || return 0
  grep -v -e "^${var}=" "$ENV_FILE" > "$ENV_FILE.tmp" || true
  mv "$ENV_FILE.tmp" "$ENV_FILE"
}

# Merge or delete the two penny-compare native-proxy keys in .env, preserving any
# other lines already in the file.
edit_native_env() {
  local action="$1" url="${2:-}" token="${3:-}"
  touch "$ENV_FILE"
  strip_env_var "$NATIVE_URL_VAR"
  strip_env_var "$NATIVE_TOKEN_VAR"
  if [ "$action" = "on" ]; then
    printf '%s=%s\n' "$NATIVE_URL_VAR" "$url" >> "$ENV_FILE"
    printf '%s=%s\n' "$NATIVE_TOKEN_VAR" "$token" >> "$ENV_FILE"
  fi
  chmod 600 "$ENV_FILE"
}

show_status() {
  SETTINGS="$SETTINGS" python3 <<'PY'
import json, os

with open(os.environ["SETTINGS"]) as fh:
    settings = json.load(fh)

env = settings.get("env", {})
url = env.get("ANTHROPIC_BASE_URL")
token = env.get("ANTHROPIC_AUTH_TOKEN")
if url:
    shown = f"{token[:6]}...{token[-4:]}" if token and len(token) > 12 else ("set" if token else "MISSING")
    connectors = "disabled" if settings.get("disableClaudeAiConnectors") else "enabled"
    print(f"claude (settings.json): ON\n  ANTHROPIC_BASE_URL:   {url}\n  ANTHROPIC_AUTH_TOKEN: {shown}\n  claude.ai connectors: {connectors}")
else:
    print("claude (settings.json): OFF (Claude Code talks to Anthropic directly)")
PY

  if [ ! -f "$ENV_FILE" ]; then
    echo "penny-compare --native-proxy (.env): OFF (no .env file)"
    return
  fi

  local url token
  url="$(grep -e "^${NATIVE_URL_VAR}=" "$ENV_FILE" | tail -n1 | cut -d= -f2-)"
  token="$(grep -e "^${NATIVE_TOKEN_VAR}=" "$ENV_FILE" | tail -n1 | cut -d= -f2-)"

  if [ -n "$url" ]; then
    local shown="MISSING"
    if [ -n "$token" ]; then
      if [ "${#token}" -gt 12 ]; then
        shown="${token:0:6}...${token: -4}"
      else
        shown="set"
      fi
    fi
    echo "penny-compare --native-proxy (.env): ON"
    echo "  $NATIVE_URL_VAR:   $url"
    echo "  $NATIVE_TOKEN_VAR: $shown"
  else
    echo "penny-compare --native-proxy (.env): OFF"
  fi
}

case "${1:-status}" in
  on)
    url="${2:-$DEFAULT_URL}"
    [ -n "$url" ] || die "usage: $(basename "$0") on <proxy-url>   (or set CLAUDE_PROXY_URL)"

    # Falls back to whatever token is already saved in .env from the last
    # `on` run, so a bare `./claude-proxy.sh on` reuses it without retyping.
    token="${CLAUDE_PROXY_TOKEN:-${PENNY_COMPARE_NATIVE_AUTH_TOKEN:-}}"
    if [ -z "$token" ]; then
      # -s so the key does not linger on screen or in scrollback.
      read -rsp "Proxy API key: " token < /dev/tty
      echo
    fi
    [ -n "$token" ] || die "no token given"

    backup
    edit_settings on "$url" "$token"
    edit_native_env on "$url" "$token"
    echo "Claude Code -> $url"
    echo "penny-compare --native-proxy -> $url"
    echo "Restart any running claude/penny-compare session to pick this up."
    ;;
  off)
    backup
    edit_settings off
    edit_native_env off
    echo "Claude Code -> Anthropic direct"
    echo "penny-compare --native-proxy -> Anthropic direct"
    echo "Restart any running claude/penny-compare session to pick this up."
    ;;
  status)
    show_status
    ;;
  *)
    die "usage: $(basename "$0") {on [url]|off|status}"
    ;;
esac
