#!/bin/sh
# Praxos Claude Code shipper installer (also serves the other supported CLIs).
#
#   sh -c "$(curl -fsSL "https://app.mypraxos.com/claude-code/install.sh?$(date +%s)")"
#   curl -fsSL https://app.mypraxos.com/claude-code/install.sh | sh -s -- -t pxcc_...
#   curl -fsSL https://app.mypraxos.com/claude-code/install.sh | sh -s -- --tool gemini_cli -t pxgm_...
#
# The first form is THE one-line runner: command substitution keeps stdin on
# the terminal (so the auto-launched CLI's dialogs actually take input) and
# the ?timestamp busts any CDN cache. The piped forms work too but skip the
# auto-launch (a TUI child of a pipe never receives keystrokes).
#
# --tool PROVIDER (gemini_cli|codex_cli|cursor_cli) is passed through to
# `praxos_ship.py setup --tool` and installs that tool's hook instead of the
# Claude Code one; without it the classic Claude Code setup runs.
#
# POSIX sh (runs under dash): no bashisms — this is piped into whatever
# /bin/sh the user has. Stdin is the pipe, so the token prompt reads /dev/tty.
set -eu

BASE_URL="${PRAXOS_INSTALL_BASE:-https://app.mypraxos.com/claude-code}"
# Cache-buster for our own downloads, so a fresh installer never pulls stale
# shipper files through a CDN. http(s) only — file:// (local dev) breaks on
# query strings.
case "$BASE_URL" in
  http*) CB="?$(date +%s)" ;;
  *)     CB="" ;;
esac

usage() {
  echo "usage: install.sh [-t TOKEN] [--tool PROVIDER]" >&2
  exit 1
}

TOKEN=""
TOOL=""
while [ $# -gt 0 ]; do
  case "$1" in
    -t|--token) [ $# -ge 2 ] || usage; TOKEN="$2"; shift 2 ;;
    --tool)     [ $# -ge 2 ] || usage; TOOL="$2"; shift 2 ;;
    *) usage ;;
  esac
done

if ! command -v python3 >/dev/null 2>&1; then
  echo "error: python3 not found on PATH" >&2
  exit 1
fi

# The encrypted raw-transcript lane needs the 'cryptography' package.
# Best-effort: try to install it, but NEVER fail the install over it --
# the filtered lane is stdlib-only and works either way.
if python3 -c "import cryptography" >/dev/null 2>&1; then
  echo "cryptography present: encrypted raw-transcript lane will be active"
else
  echo "installing 'cryptography' (needed only for the encrypted raw-transcript lane)..."
  if python3 -m pip install --user cryptography >/dev/null 2>&1; then
    echo "cryptography installed: encrypted raw-transcript lane will be active"
  else
    echo "warning: could not install 'cryptography'; the raw-transcript lane stays disabled" >&2
    echo "         (filtered lane unaffected; enable later with: python3 -m pip install --user cryptography)" >&2
  fi
fi

INSTALL_DIR="$HOME/.claude/praxos-shipper"
mkdir -p "$INSTALL_DIR"

# If the host misroutes to the web app (SPA fallback), we get index.html,
# not Python. Never install that.
is_html() {
  head -c 200 "$1" | grep -qi "<!doctype\|<html"
}

echo "downloading praxos_ship.py..."
TMP_FILE="$INSTALL_DIR/praxos_ship.py.download"
curl -fsSL "$BASE_URL/praxos_ship.py$CB" -o "$TMP_FILE"
if is_html "$TMP_FILE"; then
  rm -f "$TMP_FILE"
  echo "error: download returned HTML instead of praxos_ship.py ($BASE_URL/praxos_ship.py)" >&2
  exit 1
fi
mv "$TMP_FILE" "$INSTALL_DIR/praxos_ship.py"

# Transcript adapters for the other CLIs (Gemini/Codex/Cursor). Required for
# --tool installs; best-effort otherwise (the Claude Code lane never needs
# them).
echo "downloading adapters..."
mkdir -p "$INSTALL_DIR/adapters"
ADAPTERS_OK=1
for f in __init__.py common.py codex.py cursor.py gemini.py; do
  TMP_A="$INSTALL_DIR/adapters/$f.download"
  if curl -fsSL "$BASE_URL/adapters/$f$CB" -o "$TMP_A" && ! is_html "$TMP_A"; then
    mv "$TMP_A" "$INSTALL_DIR/adapters/$f"
  else
    rm -f "$TMP_A"
    ADAPTERS_OK=0
  fi
done
if [ "$ADAPTERS_OK" -eq 0 ]; then
  if [ -n "$TOOL" ]; then
    echo "error: could not download the adapters package from $BASE_URL/adapters/ (required for --tool $TOOL)" >&2
    exit 1
  fi
  echo "warning: could not download the adapters package; multi-tool commands (--tool) will not work" >&2
fi

if [ -z "$TOKEN" ]; then
  # Prompt only when a real tty is usable; a failed open must not abort the
  # script (set -e) — tokenless install is valid (setup-via-Claude mode).
  if { : < /dev/tty; } 2>/dev/null; then
    printf "Praxos token (press Enter to skip and set up via Claude Code): " > /dev/tty
    read -r TOKEN < /dev/tty 2>/dev/null || TOKEN=""
  fi
fi
# Tokenless install is a SUPPORTED path: files + MCP land now, and the account
# + token get created conversationally — the user asks Claude Code to run
# praxos_setup, which bootstraps an account and re-runs this installer with
# the minted token.
SETUP_DONE=0
if [ -z "$TOKEN" ]; then
  if [ -n "$TOOL" ]; then
    echo "error: --tool $TOOL requires a token (-t px..._...)" >&2
    exit 1
  fi
  echo "No token provided — installing in setup-via-Claude mode."
else
  if [ -n "$TOOL" ]; then
    python3 "$INSTALL_DIR/praxos_ship.py" setup --tool "$TOOL" --token "$TOKEN"
  else
    python3 "$INSTALL_DIR/praxos_ship.py" setup --token "$TOKEN"
  fi
  SETUP_DONE=1
fi

# --- Praxos MCP server (zero-config: reuses the shipper's stored token) ---
# Registered via each CLI's own `mcp add` command (never by editing their
# config files directly). With several CLIs installed and a usable tty, the
# user picks which ones; Enter (and headless runs) = all detected.
register_mcp() {
  case "$1" in
    claude)
      claude mcp remove --scope user praxos >/dev/null 2>&1 || true
      if claude mcp add --scope user praxos -- python3 "$INSTALL_DIR/praxos_mcp.py" >/dev/null 2>&1; then
        echo "Praxos MCP registered with Claude Code."
      else
        echo "note: Claude Code MCP registration failed; run manually:"
        echo "  claude mcp add --scope user praxos -- python3 $INSTALL_DIR/praxos_mcp.py"
      fi ;;
    gemini)
      gemini mcp remove -s user praxos >/dev/null 2>&1 || true
      if gemini mcp add -s user praxos python3 "$INSTALL_DIR/praxos_mcp.py" >/dev/null 2>&1; then
        echo "Praxos MCP registered with Gemini CLI."
      else
        echo "note: Gemini CLI MCP registration failed; run manually:"
        echo "  gemini mcp add -s user praxos python3 $INSTALL_DIR/praxos_mcp.py"
      fi ;;
    codex)
      codex mcp remove praxos >/dev/null 2>&1 || true
      if codex mcp add praxos -- python3 "$INSTALL_DIR/praxos_mcp.py" >/dev/null 2>&1; then
        echo "Praxos MCP registered with Codex CLI."
      else
        echo "note: Codex CLI MCP registration failed; run manually:"
        echo "  codex mcp add praxos -- python3 $INSTALL_DIR/praxos_mcp.py"
      fi ;;
    *)
      echo "note: unknown CLI '$1' skipped" ;;
  esac
}

if curl -fsSL "$BASE_URL/praxos_mcp.py$CB" -o "$INSTALL_DIR/praxos_mcp.py" 2>/dev/null    && head -c 15 "$INSTALL_DIR/praxos_mcp.py" | grep -q "#!/usr/bin/env"; then
  # MCP runtime is python3 — already a hard requirement above (node is NOT
  # guaranteed: Claude Code's native install bundles its own runtime).
  if true; then
    # WSL trap: Windows-side CLIs leak onto the WSL PATH via interop
    # (/mnt/c/...). Registering there writes a WSL path into the WINDOWS
    # CLI's config — its Windows python can't see it -> CONNECTION_CLOSED.
    IS_WSL=""
    if grep -qi microsoft /proc/version 2>/dev/null; then IS_WSL=1; fi
    is_windows_binary() {
      case "$(command -v "$1" 2>/dev/null)" in
        /mnt/*) return 0 ;;
        *) return 1 ;;
      esac
    }
    HAVE=""
    for C in claude gemini codex; do
      command -v "$C" >/dev/null 2>&1 || continue
      if [ -n "$IS_WSL" ] && is_windows_binary "$C"; then
        echo "note: '$C' on PATH is the WINDOWS install ($(command -v "$C")) — skipping its MCP registration."
        echo "      A Windows $C cannot launch the WSL-side Praxos MCP. Install $C inside WSL, then re-run this installer."
        continue
      fi
      HAVE="$HAVE $C"
    done
    # shellcheck disable=SC2086
    HAVE=$(echo $HAVE)
    if [ -z "$HAVE" ]; then
      echo "note: no supported CLI found (claude/gemini/codex); register the MCP later with e.g.:"
      echo "  claude mcp add --scope user praxos -- python3 $INSTALL_DIR/praxos_mcp.py"
    else
      PICK="$HAVE"
      # shellcheck disable=SC2086
      NWORDS=$(set -- $HAVE; echo $#)
      if [ "$NWORDS" -gt 1 ] && { : < /dev/tty; } 2>/dev/null; then
        printf "Register the Praxos MCP with which CLIs? [%s] (Enter = all): " "$HAVE" > /dev/tty
        read -r PICK_IN < /dev/tty 2>/dev/null || PICK_IN=""
        if [ -n "$PICK_IN" ]; then
          PICK=$(echo "$PICK_IN" | tr ',' ' ')
        fi
      fi
      # shellcheck disable=SC2086
      for CLI in $PICK; do
        register_mcp "$CLI"
      done
    fi
  fi
else
  rm -f "$INSTALL_DIR/praxos_mcp.py"
  echo "warning: could not download praxos_mcp.py (MCP tools skipped)" >&2
fi

if [ "$SETUP_DONE" -eq 0 ]; then
  # Offer to launch a registered CLI right now with the setup prompt
  # pre-submitted — the CLI takes over the tty and Praxos setup continues
  # conversationally. Claude preferred; else the first CLI that got the MCP.
  # Names the MCP server explicitly so that if the server is gated or failed
  # to connect, the agent says so instead of guessing what "Praxos" means.
  SETUP_PROMPT="set up Praxos for me, using the 'praxos' MCP server's praxos_setup tool (if the praxos MCP is disabled or not connected, tell me to run /mcp and enable it first)"
  # Launch ONLY when stdin is the real terminal (the sh -c "$(curl ...)"
  # invocation): a TUI launched with stdin rebound to /dev/tty from a
  # curl|sh pipe renders but never receives keystrokes (every dialog
  # freezes). In piped mode we print the command instead.
  LAUNCH=""
  if [ -t 0 ] && [ -n "${PICK:-}" ] && { : < /dev/tty; } 2>/dev/null; then
    for CLI in $PICK; do
      if [ "$CLI" = "claude" ]; then LAUNCH="claude"; fi
    done
    if [ -z "$LAUNCH" ]; then
      for CLI in $PICK; do
        case "$CLI" in
          gemini|codex) LAUNCH="$CLI"; break ;;
        esac
      done
    fi
  fi
  if [ -n "$LAUNCH" ]; then
    printf "Launch %s now and have it set up Praxos? [Y/n] " "$LAUNCH" > /dev/tty
    read -r GO < /dev/tty 2>/dev/null || GO="n"
    case "$GO" in
      n|N|no|NO) LAUNCH="" ;;
    esac
  fi
  if [ -n "$LAUNCH" ]; then
    echo "launching $LAUNCH..."
    case "$LAUNCH" in
      claude) claude "$SETUP_PROMPT" || true ;;
      gemini) gemini -i "$SETUP_PROMPT" || true ;;
      codex)  codex "$SETUP_PROMPT" || true ;;
    esac
  else
    echo ""
    echo "Almost done. Open a NEW Claude Code session and say:"
    echo "    set up Praxos for me"
    echo "Claude will create your account (no signup needed) and finish the install."
    echo "(If Claude says the praxos MCP is disabled or unavailable, run /mcp and enable it.)"
  fi
fi
