#!/bin/sh
# Installs the Custodian connector for coding assistants.
#
#   curl -LsSf https://custodian.health/install.sh | sh
#
# It downloads downloads/custodian-connect.tgz from the site, checks the
# SHA-256 published in downloads/connect-info.json, unpacks the program into
# ~/.custodian/connect and writes a launcher at ~/.local/bin/custodian.
# Nothing runs, and nothing is sent, until you run "custodian login" and
# "custodian on". Node.js 18 or newer is needed.
#
#   CUSTODIAN_SITE   another site to download from (default https://custodian.health)
#   CUSTODIAN_HOME   where the program lives (default ~/.custodian)
#   CUSTODIAN_BIN    where the launcher goes (default ~/.local/bin)
set -eu

SITE="${CUSTODIAN_SITE:-https://custodian.health}"
HOME_DIR="${CUSTODIAN_HOME:-$HOME/.custodian}"
BIN_DIR="${CUSTODIAN_BIN:-$HOME/.local/bin}"
INSTALL_DIR="$HOME_DIR/connect"

# Brand teal (#5097a6) on a terminal; nothing on a pipe or a file.
if [ -t 1 ] && [ "${TERM:-dumb}" != "dumb" ]; then
  TEAL="$(printf '\033[38;2;80;151;166m')"
  DIM="$(printf '\033[2m')"
  RESET="$(printf '\033[0m')"
  TTY=1
else
  TEAL=""; DIM=""; RESET=""; TTY=0
fi
case "${LC_ALL:-${LC_CTYPE:-${LANG:-}}}" in
  *UTF-8*|*utf-8*|*utf8*|*UTF8*) UTF8=1 ;;
  *) UTF8=0 ;;
esac

say() { printf '%s\n' "$*"; }
fail() { printf '\n%scustodian install:%s %s\n' "$TEAL" "$RESET" "$*" >&2; exit 1; }

banner() {
  printf '%s' "$TEAL"
  cat <<'ART'

      .-----.                      _            _ _
    /         \      ___ _   _ ___| |_ ___   __| (_) __ _ _ __
   |           |    / __| | | / __| __/ _ \ / _` | |/ _` | '_ \
    \         /    | (__| |_| \__ \ || (_) | (_| | | (_| | | | |
  ___  | | |  ___   \___|\__,_|___/\__\___/ \__,_|_|\__,_|_| |_|
       | | |
ART
  printf '%s%s       AI at work, with the people in view.%s\n\n' "$RESET" "$DIM" "$RESET"
}

frame() {
  if [ "$UTF8" = 1 ]; then
    case "$1" in
      0) printf '⠋' ;; 1) printf '⠙' ;; 2) printf '⠹' ;; 3) printf '⠸' ;; 4) printf '⠼' ;;
      5) printf '⠴' ;; 6) printf '⠦' ;; 7) printf '⠧' ;; 8) printf '⠇' ;; *) printf '⠏' ;;
    esac
  else
    case "$1" in 0|4|8) printf '|' ;; 1|5|9) printf '/' ;; 2|6) printf '-' ;; *) printf '\\' ;; esac
  fi
}
tick() { if [ "$UTF8" = 1 ]; then printf '✓'; else printf 'ok'; fi; }
cross() { if [ "$UTF8" = 1 ]; then printf '✗'; else printf 'x'; fi; }

# step "what is happening" command...: a spinner while the command runs, then
# a tick; on failure the command's output, then exit.
step() {
  label="$1"; shift
  if [ "$TTY" = 1 ]; then
    "$@" >"$TMP/step.log" 2>&1 &
    pid=$!
    i=0
    while kill -0 "$pid" 2>/dev/null; do
      printf '\r  %s%s%s %s' "$TEAL" "$(frame $i)" "$RESET" "$label"
      i=$(( (i + 1) % 10 ))
      sleep 0.1
    done
    if wait "$pid"; then
      printf '\r  %s%s%s %s\n' "$TEAL" "$(tick)" "$RESET" "$label"
    else
      printf '\r  %s%s%s %s\n' "$TEAL" "$(cross)" "$RESET" "$label"
      cat "$TMP/step.log" >&2
      fail "the step above failed. Nothing was installed."
    fi
  else
    say "- $label"
    "$@"
  fi
}

command -v curl >/dev/null 2>&1 || fail "curl is needed."
command -v tar >/dev/null 2>&1 || fail "tar is needed."

if command -v node >/dev/null 2>&1; then
  NODE_MAJOR="$(node -p 'process.versions.node.split(".")[0]' 2>/dev/null || echo 0)"
else
  NODE_MAJOR=0
fi
if [ "$NODE_MAJOR" -lt 18 ]; then
  fail "Node.js 18 or newer is needed (the coding assistants need it too). Install it from https://nodejs.org and run this again."
fi

if command -v sha256sum >/dev/null 2>&1; then
  sha256_of() { sha256sum "$1" | cut -d' ' -f1; }
elif command -v shasum >/dev/null 2>&1; then
  sha256_of() { shasum -a 256 "$1" | cut -d' ' -f1; }
else
  fail "sha256sum or shasum is needed to check the download."
fi

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

download() {
  curl -LsSf -o "$TMP/connect-info.json" "$SITE/downloads/connect-info.json"
  curl -LsSf -o "$TMP/custodian-connect.tgz" "$SITE/downloads/custodian-connect.tgz"
}

verify() {
  EXPECTED="$(sed -n 's/.*"sha256"[[:space:]]*:[[:space:]]*"\([0-9a-f]*\)".*/\1/p' "$TMP/connect-info.json" | head -n 1)"
  ACTUAL="$(sha256_of "$TMP/custodian-connect.tgz")"
  [ -n "$EXPECTED" ] || { echo "connect-info.json carries no checksum."; return 1; }
  [ "$EXPECTED" = "$ACTUAL" ] || { echo "checksum mismatch: expected $EXPECTED, got $ACTUAL."; return 1; }
}

install_files() {
  mkdir -p "$HOME_DIR" "$BIN_DIR"
  chmod 700 "$HOME_DIR"
  rm -rf "$INSTALL_DIR"
  mkdir -p "$INSTALL_DIR"
  tar -xzf "$TMP/custodian-connect.tgz" -C "$INSTALL_DIR"
  cat > "$BIN_DIR/custodian" <<LAUNCHER
#!/bin/sh
exec node "$INSTALL_DIR/bin/custodian.mjs" "\$@"
LAUNCHER
  chmod 755 "$BIN_DIR/custodian"
}

banner
step "Downloading the connector from $SITE" download
step "Checking the checksum" verify
VERSION="$(sed -n 's/.*"version"[[:space:]]*:[[:space:]]*"\([^"]*\)".*/\1/p' "$TMP/connect-info.json" | head -n 1)"
step "Installing custodian ${VERSION:-} into $INSTALL_DIR" install_files

say ""
case ":$PATH:" in
  *":$BIN_DIR:"*) ;;
  *)
    say "Add $BIN_DIR to your PATH, for example:"
    say "  echo 'export PATH=\"$BIN_DIR:\$PATH\"' >> ~/.zshrc && source ~/.zshrc"
    say ""
    ;;
esac
say "Next:"
say "  ${TEAL}custodian login${RESET}      sign in with your Custodian account"
say "  ${TEAL}custodian on${RESET}         start capture: sends once a day from now on"
say "  ${TEAL}custodian status${RESET}     see what it found and what waits"
