#!/usr/bin/env sh
# Installer for the gkit CLI binary.
# Usage:
#   curl -fsSL https://get.gkit.dev | sh
#   GKIT_VERSION=v0.2.0 curl -fsSL https://get.gkit.dev | sh
#   GKIT_INSTALL_DIR=$HOME/.local/bin curl -fsSL https://get.gkit.dev | sh
#   NO_COLOR=1 curl -fsSL https://get.gkit.dev | sh
#
# Source: https://github.com/Gorillized/gorillakit
# Verify the script by inspecting it before piping to sh:
#   curl -fsSL https://get.gkit.dev > install.sh && less install.sh
set -eu

# Repo is referenced only by the cosign identity regex and the docs URL.
# The actual binary, checksums, and signature files are fetched from the
# CDN below — that decoupling lets the source repository stay private
# while distribution remains anonymous, and it pins us to a CloudFront
# edge cache instead of github.com's anonymous rate limits.
REPO="Gorillized/gorillakit"
CDN="https://get.gkit.dev"
VERSION="${GKIT_VERSION:-latest}"
INSTALL_DIR="${GKIT_INSTALL_DIR:-/usr/local/bin}"

# ---------------------------------------------------------------------------
# Presentation. ANSI escapes are enabled only when stdout is a TTY and
# NO_COLOR (https://no-color.org) is unset. The status glyphs are plain
# Unicode (U+2713, U+26A0, U+2717, U+2192) — not emoji — so they render
# in any monospace font, including older Linux consoles.
# ---------------------------------------------------------------------------

if [ -t 1 ] && [ -z "${NO_COLOR:-}" ]; then
  C_RESET=$(printf '\033[0m')
  C_BOLD=$(printf '\033[1m')
  C_DIM=$(printf '\033[2m')
  C_RED=$(printf '\033[31m')
  C_GREEN=$(printf '\033[32m')
  C_YELLOW=$(printf '\033[33m')
  C_CYAN=$(printf '\033[36m')
else
  C_RESET=""; C_BOLD=""; C_DIM=""
  C_RED=""; C_GREEN=""; C_YELLOW=""; C_CYAN=""
fi

TOTAL_STEPS=4
CURRENT_STEP=0

banner() {
  printf '\n'
  printf '  %sGorilla Kit Installer%s\n' "$C_BOLD" "$C_RESET"
  printf '  %s─────────────────────%s\n' "$C_DIM" "$C_RESET"
  printf '\n'
}

step() {
  CURRENT_STEP=$((CURRENT_STEP + 1))
  printf '\n%s[%d/%d]%s %s%s%s\n' \
    "$C_CYAN$C_BOLD" "$CURRENT_STEP" "$TOTAL_STEPS" "$C_RESET" \
    "$C_BOLD" "$1" "$C_RESET"
}
ok()   { printf '      %s✓%s %s\n' "$C_GREEN" "$C_RESET" "$1"; }
note() { printf '      %s%s%s\n' "$C_DIM" "$1" "$C_RESET"; }
warn() { printf '      %s⚠%s %s\n' "$C_YELLOW" "$C_RESET" "$1"; }
err()  {
  printf '\n  %s✗ error:%s %s\n\n' "$C_RED$C_BOLD" "$C_RESET" "$1" >&2
  exit 1
}

# ---------------------------------------------------------------------------

banner

# --- step 1: detect platform ------------------------------------------------
step "Detecting platform"

OS="$(uname -s | tr '[:upper:]' '[:lower:]')"
case "$OS" in
  linux|darwin) ;;
  *) err "unsupported OS: $OS (linux and darwin only)" ;;
esac

ARCH="$(uname -m)"
case "$ARCH" in
  x86_64|amd64) ARCH=amd64 ;;
  arm64|aarch64) ARCH=arm64 ;;
  *) err "unsupported arch: $ARCH (amd64 and arm64 only)" ;;
esac

ok "$OS / $ARCH"

# --- step 2: resolve version ------------------------------------------------
step "Resolving version"

if [ "$VERSION" = "latest" ]; then
  # ${CDN}/latest.txt is a single-line file (`v<x.y.z>\n`) written by
  # host-release.yml's mirror step after each successful release. It
  # carries a 5-min Cache-Control on the edge — racing a brand-new
  # release will see the previous version for up to that window.
  note "resolving latest release"
  VERSION="$(curl -fsSL "${CDN}/latest.txt" 2>/dev/null | head -n1 | tr -d '[:space:]' || true)"
  [ -n "$VERSION" ] || err "could not fetch ${CDN}/latest.txt; pin GKIT_VERSION=v<x.y.z>"
fi
case "$VERSION" in
  v*) ;;
  *)  err "GKIT_VERSION must start with 'v' (got '$VERSION')" ;;
esac

ok "$VERSION"

# Versioned path is immutable on the CDN (1y immutable Cache-Control set
# by the mirror step), so once a release lands at the edge there's no
# revalidation cost on subsequent installs.
BASE="${CDN}/releases/${VERSION}"
TAR="gkit_${VERSION}_${OS}_${ARCH}.tar.gz"

# --- step 3: download + verify ---------------------------------------------
step "Downloading + verifying"
note "$BASE/$TAR"

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

curl -fsSL "${BASE}/${TAR}"        -o "${TMP}/${TAR}"        || err "download failed: ${TAR}"
curl -fsSL "${BASE}/checksums.txt" -o "${TMP}/checksums.txt" || err "download failed: checksums.txt"
ok "downloaded ${TAR}"

# sha256 verify (mandatory). The release's checksums.txt holds entries
# for every platform; grep narrows to ours so the -c check is exact.
(
  cd "$TMP"
  # -F treats the pattern as a fixed string so the dots in the filename
  # don't match arbitrary chars; -- guards against a tarball name that
  # somehow starts with a dash. Each line of checksums.txt is exactly
  # `<hash>  <filename>`, one filename per line, so no anchor is needed.
  grep -F -- "  ${TAR}" checksums.txt > "${TAR}.sha256" \
    || err "no checksum entry for ${TAR} in checksums.txt"
  if command -v sha256sum >/dev/null 2>&1; then
    sha256sum -c "${TAR}.sha256" >/dev/null
  else
    shasum -a 256 -c "${TAR}.sha256" >/dev/null
  fi
) || err "sha256 verification failed for ${TAR}"
ok "sha256 verified"

# cosign verify (best-effort). Only runs if cosign is on PATH; we don't
# pull cosign down ourselves because that would expand the trust
# boundary of this script (now you'd be trusting a second installer).
if command -v cosign >/dev/null 2>&1; then
  curl -fsSL "${BASE}/checksums.txt.sig" -o "${TMP}/checksums.txt.sig"
  curl -fsSL "${BASE}/checksums.txt.pem" -o "${TMP}/checksums.txt.pem"
  # Identity is pinned to tag-triggered runs only. workflow_dispatch
  # from a branch (e.g. main) would mint a cert with @refs/heads/main,
  # which this regex rejects. The supported re-cut path is "Run
  # workflow" with the tag selected as the source ref — that yields
  # github.ref = refs/tags/v<x>, which matches.
  cosign verify-blob \
    --certificate "${TMP}/checksums.txt.pem" \
    --signature   "${TMP}/checksums.txt.sig" \
    --certificate-identity-regexp "^https://github.com/${REPO}/.github/workflows/host-release.yml@refs/tags/v" \
    --certificate-oidc-issuer     "https://token.actions.githubusercontent.com" \
    "${TMP}/checksums.txt" >/dev/null \
    || err "cosign verification failed"
  ok "cosign signature verified"
else
  warn "cosign not on PATH — signature verification skipped"
  note "install cosign for end-to-end Sigstore verification: https://docs.sigstore.dev/cosign/installation"
fi

# --- step 4: install --------------------------------------------------------
step "Installing"

tar -xzf "${TMP}/${TAR}" -C "${TMP}"
[ -f "${TMP}/gkit" ] || err "tarball missing 'gkit' binary"

if [ -w "$INSTALL_DIR" ] || { mkdir -p "$INSTALL_DIR" 2>/dev/null && [ -w "$INSTALL_DIR" ]; }; then
  install -m 0755 "${TMP}/gkit" "${INSTALL_DIR}/gkit"
elif command -v sudo >/dev/null 2>&1 && [ -e /dev/tty ]; then
  # Under `curl … | sh`, stdin is the script pipe — sudo's password
  # prompt would have nowhere to read from. Redirecting from /dev/tty
  # restores interactivity. Falls through to err() if there's no
  # controlling terminal at all (daemonised installs, CI without a TTY).
  warn "$INSTALL_DIR not writeable — escalating with sudo"
  sudo install -m 0755 "${TMP}/gkit" "${INSTALL_DIR}/gkit" </dev/tty
elif command -v sudo >/dev/null 2>&1; then
  err "$INSTALL_DIR is not writeable and no controlling TTY is available for sudo. Re-run as: curl -fsSL https://get.gkit.dev > install.sh && sudo sh install.sh"
else
  err "$INSTALL_DIR is not writeable and sudo is unavailable; set GKIT_INSTALL_DIR to a writeable path"
fi

ok "${INSTALL_DIR}/gkit"

# --- done -------------------------------------------------------------------

printf '\n'
printf '  %s%s gkit %s installed%s\n' "$C_GREEN$C_BOLD" "→" "$VERSION" "$C_RESET"
printf '\n'
printf '  %sGet started:%s\n' "$C_BOLD" "$C_RESET"
printf '    %s$%s gkit host init --domain <your-domain>\n' "$C_DIM" "$C_RESET"
printf '    %s$%s sudo gkit host service install\n' "$C_DIM" "$C_RESET"
printf '    %s$%s gkit add survey:latest\n' "$C_DIM" "$C_RESET"
printf '\n'
printf '  %sDocs:%s    %shttps://github.com/%s%s\n' "$C_BOLD" "$C_RESET" "$C_DIM" "$REPO" "$C_RESET"
printf '\n'
