#!/bin/sh
# Bootstrap the rystic CLI on a machine that does not have one yet:
#
#   curl -fsSL https://www.rystic.ai/install.sh | sh
#
# The CLI is not license-gated — it is the downloader, not the product — so this
# needs no license and no GitHub credential. Twin binaries stay behind
# /api/twin-release, which `rystic pull` calls once you have activated a
# license. Exit codes match the CLI's vocabulary (ingest/cmd/rystic/exit.go):
# 1 error, 4 network, 6 integrity.
#
# Source of truth: rystic-sim/install.sh. rystic-site serves a copy at
# /install.sh and ships this file with every release.
set -eu

ENDPOINT="${RYSTIC_CLI_ENDPOINT:-https://www.rystic.ai/api/cli-release}"
VERSION="${RYSTIC_VERSION:-latest}"

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

need() { command -v "$1" >/dev/null 2>&1 || die 1 "$1 is required but not installed"; }
need uname
need tar
command -v curl >/dev/null 2>&1 || command -v wget >/dev/null 2>&1 ||
  die 1 "curl or wget is required but neither is installed"

fetch() { # fetch <url> [outfile]
  if command -v curl >/dev/null 2>&1; then
    if [ $# -gt 1 ]; then curl -fsSL "$1" -o "$2"; else curl -fsSL "$1"; fi
  else
    if [ $# -gt 1 ]; then wget -qO "$2" "$1"; else wget -qO- "$1"; fi
  fi
}

case "$(uname -s)" in
  Darwin) OS=darwin ;;
  Linux) OS=linux ;;
  *) die 1 "unsupported OS $(uname -s) — download a build by hand from your Rystic contact" ;;
esac
case "$(uname -m)" in
  x86_64 | amd64) ARCH=amd64 ;;
  arm64 | aarch64) ARCH=arm64 ;;
  *) die 1 "unsupported architecture $(uname -m)" ;;
esac

say "resolving rystic $VERSION for $OS/$ARCH..."
# source/licensed/ci only label the site's install count; the answer is the same.
RESP="$(fetch "$ENDPOINT?os=$OS&arch=$ARCH&version=$VERSION&source=install${RYSTIC_LICENSE_EMBEDDED:+&licensed=1}${CI:+&ci=1}")" ||
  die 4 "cannot reach the release endpoint $ENDPOINT"

# One JSON field, without assuming jq is installed. Whitespace around the colon
# is optional and the body may be pretty-printed, so flatten it first rather
# than depending on how the server happens to serialize.
RESP="$(printf '%s' "$RESP" | tr -d '\n\r')"
field() { printf '%s' "$RESP" | sed -n 's/.*"'"$1"'"[[:space:]]*:[[:space:]]*"\([^"]*\)".*/\1/p'; }
REL_VERSION="$(field version)"
ASSET="$(field asset)"
SHA256="$(field sha256)"
URL="$(field url)"
[ -n "$URL" ] || die 4 "release endpoint answered without a download URL: $RESP"

TMP="$(mktemp -d)"
trap 'rm -rf "$TMP"' EXIT INT TERM

say "downloading $ASSET..."
fetch "$URL" "$TMP/$ASSET" || die 4 "download failed (the URL is short-lived — re-run)"

# Verify before unpacking, the same rule `rystic pull` follows.
if [ -n "$SHA256" ]; then
  if command -v sha256sum >/dev/null 2>&1; then
    GOT="$(sha256sum "$TMP/$ASSET" | cut -d' ' -f1)"
  elif command -v shasum >/dev/null 2>&1; then
    GOT="$(shasum -a 256 "$TMP/$ASSET" | cut -d' ' -f1)"
  else
    GOT=""
    say "warning: no sha256 tool found; installing unverified"
  fi
  [ -z "$GOT" ] || [ "$GOT" = "$SHA256" ] ||
    die 6 "checksum mismatch for $ASSET: want $SHA256, got $GOT"
else
  say "warning: release carried no checksum for this asset; installing unverified"
fi

tar -xzf "$TMP/$ASSET" -C "$TMP" || die 6 "cannot unpack $ASSET"
[ -f "$TMP/rystic" ] || die 6 "$ASSET does not contain a rystic binary"
chmod +x "$TMP/rystic"

# Install where the caller asked, else the first writable standard place. Never
# sudo without being told to: a script that escalates on its own is a script
# nobody should pipe into sh.
if [ -n "${RYSTIC_BIN:-}" ]; then
  BIN="$RYSTIC_BIN"
elif [ -w /usr/local/bin ] 2>/dev/null; then
  BIN=/usr/local/bin
else
  BIN="$HOME/.local/bin"
fi
mkdir -p "$BIN" || die 1 "cannot create $BIN"
[ -w "$BIN" ] || die 1 "$BIN is not writable — set RYSTIC_BIN=~/.local/bin, or re-run under sudo"
mv "$TMP/rystic" "$BIN/rystic" || die 1 "cannot install into $BIN"

say ""
say "installed rystic $REL_VERSION → $BIN/rystic"
case ":$PATH:" in
  *":$BIN:"*) ;;
  *) say "note: $BIN is not on your PATH — add it, or run $BIN/rystic directly" ;;
esac
if [ -z "${RYSTIC_LICENSE_EMBEDDED:-}" ]; then
  say ""
  # Only on a terminal with no credential: `rystic login` opens the browser sign-in,
  # reading /dev/tty because under `curl | sh` stdin is this script. (RYS-1224)
  if [ -z "${RYSTIC_NO_LOGIN:-}" ] && [ -z "${CI:-}" ] && [ -t 1 ] && (: < /dev/tty) 2>/dev/null \
    && "$BIN/rystic" status --offline 2>/dev/null | grep -q '^credential: none'; then
    if "$BIN/rystic" login < /dev/tty; then exit 0; fi
    say ""
  fi
  say "next:"
  say "  rystic login                    # sign up or sign in in the browser; a new account starts on Free"
  say "  rystic login rys_…              # or a key from https://www.rystic.ai/account/api-keys"
  say "  rystic list                     # the simulators your plan covers"
  say "  rystic run <product> -d         # pulls it, starts it, prints its URL"
  say "  rystic login <license.json>     # Enterprise license file"
fi
