#!/usr/bin/env sh
# runtly installer.
#
#   curl -fsSL https://dl.runtly.flatium.com/install.sh | sh
#
# Installs the `runtly` CLI and its shim, then puts them on your PATH. Use this
# for servers and CI, or if you would rather not download a .dmg — it installs
# the same signed, notarised binaries the app ships.
#
# Environment overrides, all optional:
#   RUNTLY_VERSION    install a specific version instead of the latest
#   RUNTLY_BASE_URL   a different host (a mirror, or a staging build)
#   RUNTLY_PREFIX     where the binaries go (default ~/.runtly/libexec)
#   RUNTLY_NO_SETUP   install the binaries but do not touch the shell config
set -eu

BASE_URL="${RUNTLY_BASE_URL:-https://dl.runtly.flatium.com}"
PREFIX="${RUNTLY_PREFIX:-$HOME/.runtly/libexec}"

die()  { printf '\033[1;31merror:\033[0m %s\n' "$*" >&2; exit 1; }
say()  { printf '\033[1m==>\033[0m %s\n' "$*"; }
warn() { printf '\033[1;33mwarning:\033[0m %s\n' "$*" >&2; }

[ "$(uname -s)" = "Darwin" ] || die "runtly is macOS only (this is $(uname -s))."
command -v curl >/dev/null || die "curl is required."
command -v shasum >/dev/null || die "shasum is required."

# One field out of a flat JSON object, without needing jq on the machine.
json_field() {
  # $1 = json, $2 = key
  printf '%s' "$1" \
    | tr -d '\n' \
    | sed -n "s/.*\"$2\"[[:space:]]*:[[:space:]]*\"\([^\"]*\)\".*/\1/p"
}

if [ -n "${RUNTLY_VERSION:-}" ]; then
  VERSION="$RUNTLY_VERSION"
  ARCHIVE="runtly-$VERSION-macos-universal.tar.gz"
  URL="$BASE_URL/download/$ARCHIVE"
  EXPECTED=""
  APP_URL="$BASE_URL/download/runtly-$VERSION-universal.dmg"
  APP_SHA=""
else
  say "Looking up the latest version"
  MANIFEST="$(curl -fsSL "$BASE_URL/latest.json")" \
    || die "could not reach $BASE_URL — check your connection, or set RUNTLY_VERSION."
  VERSION="$(json_field "$MANIFEST" version)"
  URL="$(json_field "$MANIFEST" cli_url)"
  EXPECTED="$(json_field "$MANIFEST" cli_sha256)"
  APP_URL="$(json_field "$MANIFEST" app_url)"
  APP_SHA="$(json_field "$MANIFEST" app_sha256)"
  [ -n "$VERSION" ] && [ -n "$URL" ] || die "$BASE_URL/latest.json is malformed."
  ARCHIVE="$(basename "$URL")"
fi

# Refuse to overwrite a newer install with an older one by accident.
if [ -x "$PREFIX/runtly" ]; then
  CURRENT="$("$PREFIX/runtly" --version 2>/dev/null | awk '{print $2}')" || CURRENT=""
  if [ "$CURRENT" = "$VERSION" ]; then
    say "runtly $VERSION is already installed"
    [ -n "${RUNTLY_NO_SETUP:-}" ] || "$PREFIX/runtly" setup
    exit 0
  fi
  [ -n "$CURRENT" ] && say "Upgrading runtly $CURRENT → $VERSION"
fi

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

say "Downloading runtly $VERSION"
curl -fsSL "$URL" -o "$TMP/$ARCHIVE" || die "download failed: $URL"

# Verify before unpacking, never after. The same rule runtly itself applies to
# every runtime it installs.
if [ -z "$EXPECTED" ]; then
  EXPECTED="$(curl -fsSL "$URL.sha256" 2>/dev/null | awk '{print $1}')" || EXPECTED=""
fi
if [ -n "$EXPECTED" ]; then
  say "Verifying checksum"
  ACTUAL="$(shasum -a 256 "$TMP/$ARCHIVE" | awk '{print $1}')"
  [ "$EXPECTED" = "$ACTUAL" ] \
    || die "checksum mismatch — the download was discarded.
  expected: $EXPECTED
  actual:   $ACTUAL"
else
  warn "no published checksum for this download; it was not verified."
fi

say "Installing to $PREFIX"
mkdir -p "$PREFIX"
tar -xzf "$TMP/$ARCHIVE" -C "$PREFIX"
chmod +x "$PREFIX/runtly" "$PREFIX/runtly-shim"

# Signed builds carry a Developer ID signature; say so when one is missing,
# rather than letting an unsigned binary install silently.
if command -v codesign >/dev/null; then
  codesign --verify --strict "$PREFIX/runtly" 2>/dev/null \
    || warn "this build is not signed by a known developer."
fi

# The desktop app, only when asked for. A one-liner that silently drops a GUI
# application into /Applications is more than anyone expects from `curl | sh`.
install_app() {
  APP_DIR="${RUNTLY_APP_DIR:-/Applications}"
  [ -n "$APP_URL" ] || die "this release has no app download."

  say "Downloading the app"
  curl -fsSL "$APP_URL" -o "$TMP/runtly.dmg" || die "download failed: $APP_URL"

  if [ -n "$APP_SHA" ]; then
    say "Verifying the app checksum"
    ACTUAL="$(shasum -a 256 "$TMP/runtly.dmg" | awk '{print $1}')"
    [ "$APP_SHA" = "$ACTUAL" ] || die "app checksum mismatch — the download was discarded."
  else
    warn "no published checksum for the app; it was not verified."
  fi

  say "Installing runtly.app to $APP_DIR"
  MOUNT="$TMP/mnt"
  mkdir -p "$MOUNT"
  hdiutil attach "$TMP/runtly.dmg" -nobrowse -quiet -mountpoint "$MOUNT" \
    || die "could not mount the disk image."
  # Detach even when the copy fails, or the image stays mounted for good.
  CP_FAILED=""
  cp -R "$MOUNT/runtly.app" "$APP_DIR/runtly.app.new" 2>/dev/null || CP_FAILED=1
  hdiutil detach "$MOUNT" -quiet || true

  [ -z "$CP_FAILED" ] \
    || die "could not write to $APP_DIR — try again with sudo, or set RUNTLY_APP_DIR."

  rm -rf "$APP_DIR/runtly.app"
  mv "$APP_DIR/runtly.app.new" "$APP_DIR/runtly.app"
  APP_INSTALLED="$APP_DIR/runtly.app"
}

[ -n "${RUNTLY_WITH_APP:-}" ] && install_app

if [ -n "${RUNTLY_NO_SETUP:-}" ]; then
  say "Installed. Shell setup skipped."
  printf 'Run this when you are ready:\n  %s setup\n' "$PREFIX/runtly"
else
  say "Installing shims and configuring your shell"
  "$PREFIX/runtly" setup
fi

# Be explicit about what this did and did not install. Someone who ran a
# one-liner called "install runtly" and got no application deserves to be told
# so here, rather than going looking for a window that was never put on their
# machine.
printf '\n\033[1;32mruntly %s installed.\033[0m\n' "$VERSION"
printf '  command line:  %s\n' "$PREFIX/runtly"
if [ -n "${APP_INSTALLED:-}" ]; then
  printf '  desktop app:   %s\n' "$APP_INSTALLED"
else
  printf '  desktop app:   not installed — this installer is the command line only.\n'
  printf '                 Add it with: curl -fsSL %s/install.sh | RUNTLY_WITH_APP=1 sh\n' "$BASE_URL"
fi
[ -n "${RUNTLY_NO_SETUP:-}" ] || printf '\nOpen a new terminal, then:\n  rt doctor\n'
