#!/bin/sh
# Halo installer: https://memcode.ai/halo
#
#   curl -fsSL https://memcode.ai/halo/install.sh | sh
#
# Detects your OS/arch, downloads the matching Halo client, verifies its checksum
# against the release's signed checksums.txt, and installs the `halo` binary.
#
#   HALO_VERSION=vX.Y.Z    install a specific release (default: latest)
#   HALO_INSTALL_DIR=...   install location (default: /usr/local/bin or ~/.local/bin)
#   HALO_DL_BASE=...       a mirror of the release files (the signature is still checked)
#   HALO_REQUIRE_SIGNATURE=1  fail instead of continuing when this machine cannot
#                          check the release signature (needs OpenSSL 3)
#
set -eu

BINARY="halo"
BASE="${HALO_DL_BASE:-https://memcode.ai/halo/dl}"
RELEASE_KEY=""

if [ -t 1 ]; then
  BOLD="$(printf '\033[1m')"; DIM="$(printf '\033[2m')"
  RED="$(printf '\033[31m')"; GOLD="$(printf '\033[38;2;245;213;138m')"; RESET="$(printf '\033[0m')"
else
  BOLD=""; DIM=""; RED=""; GOLD=""; RESET=""
fi
info() { printf '%s\n' "${DIM}›${RESET} $*"; }
ok()   { printf '%s\n' "${GOLD}◯${RESET} $*"; }
err()  { printf '%s\n' "${RED}✗ $*${RESET}" >&2; }
die()  { err "$@"; exit 1; }

[ -n "$RELEASE_KEY" ] || die "Halo has no public release yet. See https://memcode.ai/product/halo"

if command -v curl >/dev/null 2>&1; then DL="curl -fsSL"
elif command -v wget >/dev/null 2>&1; then DL="wget -qO-"
else die "need curl or wget to download Halo"
fi
command -v tar >/dev/null 2>&1 || die "need tar to extract the archive"

case "$(uname -s)" in
  Linux)  OS="Linux" ;;
  Darwin) OS="Darwin" ;;
  *) die "Halo runs on macOS and Linux for now" ;;
esac
case "$(uname -m)" in
  x86_64|amd64) ARCH="x86_64" ;;
  arm64|aarch64) ARCH="arm64" ;;
  *) die "unsupported architecture: $(uname -m)" ;;
esac

VERSION="${HALO_VERSION:-}"
if [ -z "$VERSION" ]; then
  VERSION="$($DL "$BASE/latest" | tr -d '[:space:]')" || die "could not find the latest release"
fi
case "$VERSION" in v[0-9]*) ;; *) die "unexpected release name: $VERSION" ;; esac

ARCHIVE="${BINARY}_${OS}_${ARCH}.tar.gz"
info "Installing ${BOLD}halo${RESET} $VERSION ($OS/$ARCH)"
TMP="$(mktemp -d)"
trap 'rm -rf "$TMP"' EXIT INT TERM

$DL "$BASE/$VERSION/checksums.txt" > "$TMP/checksums.txt" && [ -s "$TMP/checksums.txt" ] || die "could not download checksums.txt"
$DL "$BASE/$VERSION/checksums.txt.sig" > "$TMP/checksums.txt.sig" && [ -s "$TMP/checksums.txt.sig" ] || die "could not download the release signature"

# The signature proves checksums.txt came from Halo's release key, so a tampered
# bucket or mirror cannot ship a binary. It needs OpenSSL 3 (ed25519 with -rawin);
# macOS's bundled LibreSSL cannot check it.
signed=no
if command -v openssl >/dev/null 2>&1 && openssl version 2>/dev/null | grep -q '^OpenSSL 3'; then
  printf '%b\n' "$RELEASE_KEY" > "$TMP/release.pem"
  openssl base64 -d -A -in "$TMP/checksums.txt.sig" -out "$TMP/sig.bin" 2>/dev/null || die "unreadable release signature"
  if openssl pkeyutl -verify -pubin -inkey "$TMP/release.pem" -rawin -in "$TMP/checksums.txt" -sigfile "$TMP/sig.bin" >/dev/null 2>&1; then
    signed=yes
  else
    die "the release checksums are NOT signed by Halo's release key; not installing"
  fi
fi
if [ "$signed" = yes ]; then
  ok "Release signature verified"
elif [ "${HALO_REQUIRE_SIGNATURE:-}" = 1 ]; then
  die "cannot check the release signature here (needs OpenSSL 3)"
else
  info "Release signature not checked here (needs OpenSSL 3); halo upgrade always checks it"
fi

$DL "$BASE/$VERSION/$ARCHIVE" > "$TMP/$ARCHIVE" && [ -s "$TMP/$ARCHIVE" ] || die "download failed: $ARCHIVE"
if command -v sha256sum >/dev/null 2>&1; then SHA="sha256sum"
elif command -v shasum >/dev/null 2>&1; then SHA="shasum -a 256"
else die "need sha256sum or shasum to verify the download"
fi
expected="$(grep " $ARCHIVE\$" "$TMP/checksums.txt" | awk '{print $1}')"
[ -n "$expected" ] || die "checksums.txt has no entry for $ARCHIVE"
actual="$(cd "$TMP" && $SHA "$ARCHIVE" | awk '{print $1}')"
[ "$expected" = "$actual" ] || die "checksum mismatch for $ARCHIVE"
ok "Checksum verified"

tar -xzf "$TMP/$ARCHIVE" -C "$TMP" || die "failed to extract the archive"
[ -f "$TMP/$BINARY" ] || die "the archive holds no halo binary"
chmod +x "$TMP/$BINARY"

if [ -n "${HALO_INSTALL_DIR:-}" ]; then DIR="$HALO_INSTALL_DIR"
elif [ -w "/usr/local/bin" ] 2>/dev/null; then DIR="/usr/local/bin"
else DIR="$HOME/.local/bin"
fi
mkdir -p "$DIR"
DEST="$DIR/$BINARY"
mv "$TMP/$BINARY" "$DEST" 2>/dev/null || die "cannot write to $DIR; set HALO_INSTALL_DIR to a writable folder"
ok "Installed ${BOLD}halo${RESET} $("$DEST" version 2>/dev/null | awk '{print $2}') to $DEST"

case ":$PATH:" in
  *":$DIR:"*) ;;
  *) printf '%s\n' "${DIM}  $DIR is not on your PATH; add it:${RESET}"
     printf '%s\n' "    export PATH=\"$DIR:\$PATH\"" ;;
esac
printf '\n'
ok "Run ${BOLD}halo${RESET} to start. Already use the Memcode CLI? You're signed in. Otherwise: ${BOLD}halo login${RESET}"
