#!/bin/sh
# Balladeer thin-client installer (central-served; decision_433e2108).
# Downloads the CLI, verifies its checksum, installs it, then tells you how to pair.
set -eu

BASE="https://get.balladeer.ai"
TGZ_URL="$BASE/install/client.tgz"
SHA_URL="$BASE/install/client.tgz.sha256"

echo "Installing the Balladeer client from $BASE"

if ! command -v node >/dev/null 2>&1; then
  echo "error: Node.js is required (v20+). Install it from https://nodejs.org and re-run." >&2
  exit 1
fi
if ! command -v npm >/dev/null 2>&1; then
  echo "error: npm is required (it ships with Node.js)." >&2
  exit 1
fi

fetch() {
  # $1 url, $2 out. curl if present, else wget.
  if command -v curl >/dev/null 2>&1; then curl -fsSL "$1" -o "$2";
  elif command -v wget >/dev/null 2>&1; then wget -qO "$2" "$1";
  else echo "error: need curl or wget." >&2; exit 1; fi
}

TMP="$(mktemp -d 2>/dev/null || mktemp -d -t balladeer)"
trap 'rm -rf "$TMP"' EXIT
TGZ="$TMP/balladeer-client.tgz"

fetch "$TGZ_URL" "$TGZ"
fetch "$SHA_URL" "$TMP/sha"

# A fresh interactive install launches agent selection after the binary is verified.
# Updates and headless installs stay non-interactive and print the setup command instead.
HAD_BALLADEER=0
if command -v balladeer >/dev/null 2>&1; then HAD_BALLADEER=1; fi

# Verify the checksum BEFORE running anything from the tarball (npm executes package scripts).
EXPECTED="$(awk '{print $1}' "$TMP/sha")"
if command -v sha256sum >/dev/null 2>&1; then ACTUAL="$(sha256sum "$TGZ" | awk '{print $1}')";
elif command -v shasum >/dev/null 2>&1; then ACTUAL="$(shasum -a 256 "$TGZ" | awk '{print $1}')";
else echo "error: need sha256sum or shasum to verify the download." >&2; exit 1; fi
if [ "$EXPECTED" != "$ACTUAL" ]; then
  echo "error: checksum mismatch; refusing to install a tampered download." >&2
  echo "  expected $EXPECTED" >&2
  echo "  actual   $ACTUAL" >&2
  exit 1
fi

echo "Checksum OK. Installing globally with npm…"
# Volta manages global installs by intercepting them through its npm shim. When this installer is
# launched BY a Volta-managed balladeer, however, Volta prepends its raw per-node bin to PATH and
# sets _VOLTA_TOOL_RECURSION. Merely unsetting the guard is insufficient: a bare npm still
# resolves to that raw binary and installs a stray copy the user's shim never serves. Capture the
# managed npm shim by absolute path before scrubbing the guard, then invoke that exact binary so
# the active package image flips (decision_c9c865f7b52f). Outside this Volta child state, keep the
# npm already selected by the user's shell.
VOLTA_ROOT="${VOLTA_HOME:-${HOME:-}/.volta}"
NPM_BIN="$(command -v npm)"
if [ -n "${_VOLTA_TOOL_RECURSION:-}" ] && [ -x "$VOLTA_ROOT/bin/npm" ]; then
  NPM_BIN="$VOLTA_ROOT/bin/npm"
fi
unset _VOLTA_TOOL_RECURSION 2>/dev/null || true
# --ignore-scripts: the client has no install hooks, and this keeps a compromised tarball from
# running arbitrary lifecycle scripts even after the checksum gate.
"$NPM_BIN" install -g --ignore-scripts "$TGZ"

# Verify against what the user's SHELL will actually run, resolved by ABSOLUTE PATH, not whatever
# our (possibly version-manager-prepended) PATH resolves. npm can exit 0 having put the binary
# somewhere the shell never looks (a version manager's per-runtime bin, a custom prefix, a PATH
# that needs a reload); and under Volta a bare 'balladeer' here can read a per-runtime stray the
# user's shim never serves and announce a success that did not happen. The managed shim is the
# source of truth, so probe IT directly when a version manager owns this tool.
VOLTA_SHIM="$VOLTA_ROOT/bin/balladeer"
BALLADEER_BIN="$(command -v balladeer 2>/dev/null || true)"
if command -v volta >/dev/null 2>&1 && [ -x "$VOLTA_SHIM" ]; then
  BALLADEER_BIN="$VOLTA_SHIM"
fi
if [ -z "$BALLADEER_BIN" ]; then
  echo "" >&2
  echo "error: balladeer installed, but it is not on your PATH, so nothing can run it." >&2
  echo "  npm put it in: $(npm prefix -g 2>/dev/null)/bin" >&2
  echo "  Add that directory to PATH (or open a fresh shell) and check: balladeer --version" >&2
  exit 1
fi

# What this build serves (injected by the server that shipped the tarball) vs. what the shell now
# runs. If a version manager is still serving an older managed copy, say so rather than print a
# false success line.
WANT="0.2.51"
GOT="$("$BALLADEER_BIN" --version 2>/dev/null | head -n1 || true)"
if [ -n "$GOT" ] && [ "$WANT" != "$GOT" ]; then
  echo "" >&2
  echo "error: installed balladeer $WANT, but your shell still runs $GOT." >&2
  echo "  A version manager (e.g. Volta) is serving an older managed copy at:" >&2
  echo "    $BALLADEER_BIN" >&2
  echo "  Open a fresh terminal and re-run this installer; if it persists, remove that copy" >&2
  echo "  (e.g. volta uninstall balladeer) and re-run." >&2
  exit 1
fi

echo ""
echo "✓ balladeer client installed (${GOT:-$WANT})"
if [ "$HAD_BALLADEER" = "0" ] && [ -t 0 ] && [ -t 1 ]; then
  echo ""
  echo "Choose which coding agents should receive Balladeer:"
  if ! "$BALLADEER_BIN" setup --url "https://balladeer-server.fly.dev"; then
    echo "agent setup did not complete; retry any time with: balladeer setup" >&2
  fi
fi
echo ""
# The closing block tells the truth about THIS machine (decision_1eb0cc963d62): a paired machine
# (credentials on disk) is mid-update, not mid-onboarding, and teaching it login and workspace
# creation reads as being logged out. Pairing state keys the branch (not an invoked-from-update
# flag), so the 426 [Y/n] path and a direct curl re-run behave identically.
if [ -s "${HOME:-}/.balladeer/credentials.json" ]; then
  echo "  this machine is already paired; your login and agent setup carry over"
  echo "  watch  balladeer recent · the Claude Code status line"
else
  echo "  next   balladeer login     # approve in your browser; no token to copy"
  echo "  setup  balladeer setup     # choose Claude Code, Codex, Cursor, or any combination"
  echo "  then   your coding agents gain get_context / consult / propose_decision"
  echo "  agent  or let your agent finish the wiring: paste into it -> Read https://get.balladeer.ai/agent and follow it."
  echo "  watch  balladeer recent · the Claude Code status line"
  echo ""
  echo "  no workspace yet? create one at https://decisions.balladeer.ai"
fi
