#!/bin/sh
# Butler installer — https://getbutler.dev
#
#   curl -fsSL https://getbutler.dev/install.sh | sh
#
# Fetches the single `butler` binary from the per-arch R2 bucket, verifies its
# sha256 against the published manifest, and drops it on your PATH. Mirrors the
# fetch+verify flow the daemon uses in services/daemon/downloads/, so a curl
# install and a `butler install` agree byte-for-byte.
#
# Knobs (all optional):
#   BUTLER_VERSION       pin a version, e.g. 0.1.0   (default: latest in manifest)
#   BUTLER_INSTALL_DIR   where the binary lands       (default: /usr/local/bin)
#   BUTLER_R2_BASE       bucket base URL              (default: https://r2.butler.dev)
#   BUTLER_NO_INIT=1     skip the `butler init` step at the end
set -eu

R2_BASE="${BUTLER_R2_BASE:-https://r2.butler.dev}"
INSTALL_DIR="${BUTLER_INSTALL_DIR:-/usr/local/bin}"

# --- pretty output ----------------------------------------------------------
if [ -t 1 ]; then
  B="$(printf '\033[1m')"; DIM="$(printf '\033[2m')"; GRN="$(printf '\033[32m')"
  RED="$(printf '\033[31m')"; YEL="$(printf '\033[33m')"; RST="$(printf '\033[0m')"
else
  B=''; DIM=''; GRN=''; RED=''; YEL=''; RST=''
fi
info() { printf '%s\n' "${DIM}›${RST} $*"; }
good() { printf '%s\n' "${GRN}✓${RST} $*"; }
warn() { printf '%s\n' "${YEL}!${RST} $*" >&2; }
die()  { printf '%s\n' "${RED}✗${RST} $*" >&2; exit 1; }

need() { command -v "$1" >/dev/null 2>&1 || die "required tool not found: $1"; }

# --- detect platform --------------------------------------------------------
os="$(uname -s | tr '[:upper:]' '[:lower:]')"
[ "$os" = "darwin" ] || die "Butler currently supports macOS only (got: $os)."

case "$(uname -m)" in
  arm64|aarch64) arch="arm64" ;;
  x86_64|amd64)  arch="x86_64" ;;
  *) die "unsupported architecture: $(uname -m)" ;;
esac
PLATFORM="${os}-${arch}"            # darwin-arm64 | darwin-x86_64
ARCH_BASE="${R2_BASE}/${PLATFORM}"

need curl
need tar
if command -v shasum >/dev/null 2>&1; then
  SHA() { shasum -a 256 "$1" | awk '{print $1}'; }
elif command -v sha256sum >/dev/null 2>&1; then
  SHA() { sha256sum "$1" | awk '{print $1}'; }
else
  die "need shasum or sha256sum for checksum verification"
fi

printf '\n%s\n' "${B}Butler${RST} ${DIM}— local dev environment manager${RST}"
info "platform   ${PLATFORM}"

tmp="$(mktemp -d "${TMPDIR:-/tmp}/butler-install.XXXXXX")"
trap 'rm -rf "$tmp"' EXIT INT TERM

# --- resolve version → (r2Key, sha256) --------------------------------------
# With BUTLER_VERSION we can address the artifact directly and trust the
# published .sha256 sidecar. Otherwise we read the manifest and pick the
# newest butler entry (the manifest carries forward older versions).
if [ -n "${BUTLER_VERSION:-}" ]; then
  VERSION="$BUTLER_VERSION"
  R2KEY="butler/butler-${VERSION}-${PLATFORM}.tar.gz"
  info "version    ${VERSION} ${DIM}(pinned)${RST}"
  curl -fsSL "${ARCH_BASE}/${R2KEY}.sha256" -o "$tmp/want.sha" \
    || die "no release found for butler ${VERSION} on ${PLATFORM}"
  WANT_SHA="$(tr -d '[:space:]' < "$tmp/want.sha")"
else
  info "manifest   ${ARCH_BASE}/manifest.json"
  curl -fsSL "${ARCH_BASE}/manifest.json" -o "$tmp/manifest.json" \
    || die "could not fetch manifest for ${PLATFORM}"

  # Walk the services array; for every {type:"butler"} object capture
  # version / r2Key / sha256, then `sort -V | tail -1` keeps the newest.
  line="$(awk '
    function qval(s){ sub(/^[^:]*:[ \t]*/,"",s); gsub(/[",\r\t ]/,"",s); return s }
    /"type"[ \t]*:[ \t]*"butler"/ { inb=1; next }
    inb && /"version"[ \t]*:/ { v=qval($0) }
    inb && /"r2Key"[ \t]*:/   { k=qval($0) }
    inb && /"sha256"[ \t]*:/  { print v "\t" k "\t" qval($0); inb=0 }
  ' "$tmp/manifest.json" | sort -V | tail -1)"

  [ -n "$line" ] || die "manifest has no butler entry for ${PLATFORM}"
  VERSION="$(printf '%s' "$line" | cut -f1)"
  R2KEY="$(printf '%s' "$line"   | cut -f2)"
  WANT_SHA="$(printf '%s' "$line" | cut -f3)"
  info "version    ${VERSION} ${DIM}(latest)${RST}"
fi

# --- download + verify ------------------------------------------------------
info "downloading ${R2KEY}"
curl -fSL# "${ARCH_BASE}/${R2KEY}" -o "$tmp/butler.tar.gz" \
  || die "download failed: ${ARCH_BASE}/${R2KEY}"

if [ -n "$WANT_SHA" ]; then
  GOT_SHA="$(SHA "$tmp/butler.tar.gz")"
  # lowercase both sides for a case-insensitive hex compare
  if [ "$(printf '%s' "$GOT_SHA" | tr 'A-F' 'a-f')" != "$(printf '%s' "$WANT_SHA" | tr 'A-F' 'a-f')" ]; then
    die "checksum mismatch
     got  ${GOT_SHA}
     want ${WANT_SHA}"
  fi
  good "checksum verified"
else
  warn "no checksum available — skipping verification"
fi

# --- extract ----------------------------------------------------------------
mkdir -p "$tmp/x"
tar -xzf "$tmp/butler.tar.gz" -C "$tmp/x" || die "extract failed"
BIN="$(find "$tmp/x" -type f -name butler 2>/dev/null | head -1)"
[ -n "$BIN" ] || die "no 'butler' binary inside the archive"
chmod +x "$BIN"

# --- install onto PATH ------------------------------------------------------
DEST="${INSTALL_DIR%/}/butler"
if [ -d "$INSTALL_DIR" ] && [ -w "$INSTALL_DIR" ]; then
  install -m 0755 "$BIN" "$DEST"
elif [ -w "$(dirname "$INSTALL_DIR")" ] || mkdir -p "$INSTALL_DIR" 2>/dev/null; then
  install -m 0755 "$BIN" "$DEST"
else
  warn "${INSTALL_DIR} is not writable — using sudo"
  sudo install -d -m 0755 "$INSTALL_DIR"
  sudo install -m 0755 "$BIN" "$DEST"
fi
good "installed ${B}butler ${VERSION}${RST} → ${DEST}"

# --- PATH hint --------------------------------------------------------------
case ":${PATH}:" in
  *":${INSTALL_DIR}:"*) ;;
  *) warn "${INSTALL_DIR} is not on your PATH — add it:
     ${DIM}export PATH=\"${INSTALL_DIR}:\$PATH\"${RST}" ;;
esac

# --- first-run setup --------------------------------------------------------
# `butler init` creates the support tree and installs the launchd jobs (the
# system router job needs admin). Only run it when we have a terminal to
# prompt on; a non-interactive `curl | sh` just prints the next step.
if [ "${BUTLER_NO_INIT:-0}" != "1" ] && [ -t 0 ]; then
  printf '\n'
  info "running ${B}butler init${RST}"
  "$DEST" init || warn "butler init exited non-zero — run it again with: butler init"
else
  printf '\n%s\n' "Next: finish setup with"
  printf '  %s\n' "${B}butler init${RST}"
fi

printf '\n%s\n\n' "${GRN}Done.${RST} Try ${B}butler status${RST} or ${B}butler --help${RST}."
