#!/bin/sh
# Dropper CLI installer — served by https://dropp.sh/install.sh
#
#   curl -fsSL https://dropp.sh/install.sh | sh
#
# Downloads the prebuilt `dropper` binary for this machine from https://dropp.sh/cli/,
# verifies its SHA-256 against the published checksums.txt, and installs it into
# $HOME/.local/bin (or $DROPPER_INSTALL_DIR). It never escalates privileges.
#
# Supported: macOS (Apple Silicon and Intel) and glibc Linux (x64, arm64).
# Anywhere else — Windows, Alpine/musl — build from source:
#   git clone https://gitlab.tolbbox.com/tolbnet/dropper.git
#   cd dropper/cli && npm ci && npm run build && node dist/index.js --help
# macOS can also use Homebrew: brew install --cask dtolb/tap/dropper
#
# Environment overrides (local dev and tests):
#   DROPPER_INSTALL_DIR    target directory (default: $HOME/.local/bin)
#   DROPPER_INSTALL_BASE   origin to download from (default: https://dropp.sh). The default
#                          must be https and is fetched with curl's TLS-only flags; a plain
#                          http:// or file:// base is honoured ONLY through this override,
#                          and then those flags are skipped.
#   DROPPER_OS             stands in for `uname -s` (Darwin, Linux)
#   DROPPER_ARCH           stands in for `uname -m` (x86_64, amd64, arm64, aarch64)
#   TMPDIR                 where the download is staged (default: /tmp); removed on exit
set -eu

BASE_DEFAULT='https://dropp.sh'
ALT_BREW='brew install --cask dtolb/tap/dropper'
ALT_SOURCE='git clone https://gitlab.tolbbox.com/tolbnet/dropper.git && cd dropper/cli && npm ci && npm run build'

die() {
  printf 'dropper install: %s\n' "$*" >&2
  exit 1
}

# A platform the prebuilt binaries do not cover: say so and name the alternatives.
unsupported() {
  printf 'dropper install: %s\n' "$1" >&2
  printf '  macOS via Homebrew:  %s\n' "$ALT_BREW" >&2
  printf '  from source (Node 20+ on any platform):  %s\n' "$ALT_SOURCE" >&2
  exit 1
}

for tool in uname curl tar mktemp install sed grep; do
  command -v "$tool" >/dev/null 2>&1 || die "$tool is required but was not found on PATH"
done
command -v sha256sum >/dev/null 2>&1 || command -v shasum >/dev/null 2>&1 || die "sha256sum or shasum is required to verify the download"

# --- platform: the os/arch pairs cli/scripts/lib/targets.ts publishes (TARGETS) ---
OS_RAW="${DROPPER_OS:-$(uname -s)}"
ARCH_RAW="${DROPPER_ARCH:-$(uname -m)}"
case "$OS_RAW" in
  Darwin) OS=darwin ;;
  Linux) OS=linux ;;
  MINGW*|MSYS*|CYGWIN*|Windows*) unsupported "Windows is not supported by the prebuilt binaries" ;;
  *) unsupported "unsupported operating system: $OS_RAW" ;;
esac
case "$ARCH_RAW" in
  x86_64|amd64) ARCH=x64 ;;
  arm64|aarch64) ARCH=arm64 ;;
  *) unsupported "unsupported CPU architecture: $ARCH_RAW" ;;
esac

# The Linux binaries link against glibc; Alpine and other musl systems cannot run them.
if [ "$OS" = linux ] && command -v ldd >/dev/null 2>&1 && ldd --version 2>&1 | grep -qi musl; then
  unsupported "musl-based Linux (Alpine) is not supported by the prebuilt binaries — they need glibc"
fi

# --- download base: https only, unless overridden for local dev / tests ---
BASE="${DROPPER_INSTALL_BASE:-$BASE_DEFAULT}"
BASE="${BASE%/}"
case "$BASE" in
  https://*) TLS_ONLY=1 ;;
  http://*|file://*)
    [ -n "${DROPPER_INSTALL_BASE:-}" ] || die "refusing to download over $BASE — set DROPPER_INSTALL_BASE to override"
    TLS_ONLY=0
    ;;
  *) die "unsupported download base: $BASE" ;;
esac

fetch() { # fetch <url> <destination>
  if [ "$TLS_ONLY" = 1 ]; then
    curl -fsSL --proto '=https' --tlsv1.2 -o "$2" "$1"
  else
    curl -fsSL -o "$2" "$1"
  fi
}

# Prints the tool's verdict lines; LC_ALL=C keeps the "OK" untranslated for the check below.
verify_checksum() { # verify_checksum <dir> <checksum file inside dir>
  if command -v sha256sum >/dev/null 2>&1; then
    (cd "$1" && LC_ALL=C sha256sum -c "$2")
  else
    (cd "$1" && LC_ALL=C shasum -a 256 -c "$2")
  fi
}

# --- where it goes: settled before any download so an unwritable target fails fast ---
if [ -n "${DROPPER_INSTALL_DIR:-}" ]; then
  INSTALL_DIR="$DROPPER_INSTALL_DIR"
elif [ -n "${HOME:-}" ]; then
  INSTALL_DIR="$HOME/.local/bin"
else
  die "HOME is not set — choose a directory with DROPPER_INSTALL_DIR"
fi
mkdir -p "$INSTALL_DIR" 2>/dev/null || die "cannot create $INSTALL_DIR — choose a writable directory with DROPPER_INSTALL_DIR (this installer never escalates privileges)"
[ -w "$INSTALL_DIR" ] || die "$INSTALL_DIR is not writable — choose a writable directory with DROPPER_INSTALL_DIR (this installer never escalates privileges)"

TMP=''
cleanup() {
  if [ -n "$TMP" ]; then rm -rf "$TMP"; fi
}
trap cleanup EXIT
trap 'exit 1' INT TERM HUP
# An explicit template under $TMPDIR (else /tmp): macOS's bare `mktemp -d` ignores TMPDIR
# and always uses the per-user /var/folders/…/T dir, so a caller's TMPDIR would be ignored
# there and the tests' private TMPDIR (asserted empty after every run) would never see it.
TMP="$(mktemp -d "${TMPDIR:-/tmp}/dropper-install.XXXXXX")" || die "could not create a temp directory under ${TMPDIR:-/tmp}"

# --- which version does this deployment serve? ---
fetch "$BASE/cli/manifest.json" "$TMP/manifest.json" || die "could not download $BASE/cli/manifest.json"
VERSION="$(sed -n 's/.*"version"[[:space:]]*:[[:space:]]*"\([^"]*\)".*/\1/p' "$TMP/manifest.json" | head -n 1)"
case "$VERSION" in
  ''|*[!A-Za-z0-9.-]*) die "$BASE/cli/manifest.json has no usable version" ;;
esac

NAME="dropper-$OS-$ARCH.tar.gz"
NAME_RE="dropper-$OS-${ARCH}[.]tar[.]gz" # NAME as a grep -E pattern: the dots made literal
DIR_URL="$BASE/cli/v$VERSION"
echo "Downloading dropper $VERSION for $OS/$ARCH from $DIR_URL"
fetch "$DIR_URL/$NAME" "$TMP/$NAME" || die "download failed: $DIR_URL/$NAME"
fetch "$DIR_URL/checksums.txt" "$TMP/checksums.txt" || die "download failed: $DIR_URL/checksums.txt"

# checksums.txt lists every archive; keep only this one's line, and only when it is
# well-formed ("<64 lowercase hex>  <name>", exactly what the release script writes).
# macOS's BSD sha256sum exits 0 with a mere warning on a line it cannot parse, so a
# malformed line must never reach the tool.
grep -E "^[0-9a-f]{64}  $NAME_RE\$" "$TMP/checksums.txt" | head -n 1 > "$TMP/expected.txt"
if [ ! -s "$TMP/expected.txt" ]; then
  if grep -qF "$NAME" "$TMP/checksums.txt"; then
    die "checksums.txt entry for $NAME is malformed — not installing it"
  fi
  die "checksums.txt has no entry for $NAME"
fi
# Both the tool's exit status and its "<name>: OK" verdict are required: one line went
# in, so exactly that verdict must come out. Never trust an exit status alone.
VERDICT="$(verify_checksum "$TMP" expected.txt)" || die "checksum verification failed for $NAME — not installing it"
[ "$VERDICT" = "$NAME: OK" ] || die "checksum verification failed for $NAME — not installing it"
echo "$VERDICT"

tar -xzf "$TMP/$NAME" -C "$TMP" dropper || die "could not extract $NAME"
[ -f "$TMP/dropper" ] || die "$NAME does not contain dropper"

# --- install ---
install -m 0755 "$TMP/dropper" "$INSTALL_DIR/dropper" || die "could not install into $INSTALL_DIR"

echo "Installed $INSTALL_DIR/dropper"
"$INSTALL_DIR/dropper" --version
case ":$PATH:" in
  *":$INSTALL_DIR:"*) ;;
  *)
    echo "$INSTALL_DIR is not on your PATH. Add it with:"
    echo "  export PATH=\"$INSTALL_DIR:\$PATH\""
    ;;
esac
