#!/bin/sh
# Install the arbe CLI.
#
#   curl -fsSL https://arbe.0sk.ar/install.sh | sh
#
# Env vars:
#   ARBE_INSTALL_DIR   target directory (default: $HOME/.local/bin)
#   ARBE_TARGET        force a target instead of auto-detecting
#                      (darwin-arm64 | darwin-x64 | linux-x64)
#   ARBE_VERSION       pin to a specific release (e.g. 0.1.0)
#                      default: latest
#
# Release contract: docs/system/ops/releases.md in github.com/oskarrough/arbe.

set -eu

DOWNLOADS="https://downloads.arbe.0sk.ar"
ARBE_VERSION="${ARBE_VERSION:-latest}"
MANIFEST_URL="$DOWNLOADS/arbe/$ARBE_VERSION.json"
INSTALL_DIR="${ARBE_INSTALL_DIR:-$HOME/.local/bin}"

err() {
	printf 'arbe-install: %s\n' "$1" >&2
	exit 1
}

need() {
	command -v "$1" >/dev/null 2>&1 || err "missing required command: $1"
}

need uname
need tar
need mkdir
need mv
need rm

if command -v curl >/dev/null 2>&1; then
	fetch() { command curl -fsSL "$@"; }
elif command -v wget >/dev/null 2>&1; then
	fetch() {
		# Translate the curl flags we use (-fsSL, -o) into wget equivalents.
		out=-
		while [ "$#" -gt 0 ]; do
			case "$1" in
				-o) out="$2"; shift 2 ;;
				-fsSL) shift ;;
				*) break ;;
			esac
		done
		wget -qO "$out" "$@"
	}
else
	err "need curl or wget"
fi

if command -v sha256sum >/dev/null 2>&1; then
	sha256() { sha256sum "$1" | awk '{print $1}'; }
elif command -v shasum >/dev/null 2>&1; then
	sha256() { shasum -a 256 "$1" | awk '{print $1}'; }
else
	err "need sha256sum or shasum"
fi

detect_target() {
	if [ -n "${ARBE_TARGET:-}" ]; then
		printf '%s' "$ARBE_TARGET"
		return
	fi
	os=$(uname -s)
	arch=$(uname -m)
	case "$os" in
		Darwin)
			case "$arch" in
				arm64) printf 'darwin-arm64' ;;
				x86_64) printf 'darwin-x64' ;;
				*) err "unsupported macOS arch: $arch" ;;
			esac
			;;
		Linux)
			case "$arch" in
				x86_64) printf 'linux-x64' ;;
				aarch64|arm64) err "linux-arm64 is not yet published; track docs/system/ops/releases.md" ;;
				*) err "unsupported Linux arch: $arch" ;;
			esac
			;;
		*) err "unsupported OS: $os" ;;
	esac
}

# Pull a "key": "value" pair out of latest.json without depending on jq.
# Scoped to a JSON object containing the given target key, so we don't
# accidentally grab another target's sha or url.
extract() {
	target="$1"
	field="$2"
	json="$3"
	# Shrink JSON onto one line, then awk between the target key and the next
	# top-level "}", and pluck the field.
	printf '%s' "$json" \
		| tr -d '\n\t' \
		| awk -v t="\"$target\"" -v f="\"$field\"" '
			{
				i = index($0, t)
				if (!i) exit 1
				rest = substr($0, i)
				j = index(rest, f)
				if (!j) exit 1
				rest = substr(rest, j + length(f))
				# rest now starts at: : "value", ...
				k = index(rest, "\"")
				if (!k) exit 1
				rest = substr(rest, k + 1)
				e = index(rest, "\"")
				if (!e) exit 1
				print substr(rest, 1, e - 1)
			}
		'
}

target=$(detect_target)
printf 'arbe-install: target %s\n' "$target"

printf 'arbe-install: fetching %s\n' "$MANIFEST_URL"
metadata=$(fetch "$MANIFEST_URL") || err "could not fetch $MANIFEST_URL"


url=$(extract "$target" "url" "$metadata") || err "no url for $target in $MANIFEST_URL"
want_sha=$(extract "$target" "sha256" "$metadata") || err "no sha256 for $target in $MANIFEST_URL"
commit=$(printf '%s' "$metadata" | tr -d '\n\t' | awk '{
	i = index($0, "\"commit\"")
	if (!i) exit
	rest = substr($0, i + 8)
	k = index(rest, "\"")
	if (!k) exit
	rest = substr(rest, k + 1)
	e = index(rest, "\"")
	if (!e) exit
	print substr(rest, 1, e - 1)
}')

tmpdir=$(mktemp -d 2>/dev/null || mktemp -d -t arbe-install)
trap 'rm -rf "$tmpdir"' EXIT INT TERM
archive="$tmpdir/arbe.tar.gz"

printf 'arbe-install: downloading %s\n' "$url"
fetch -o "$archive" "$url" || err "download failed: $url"

got_sha=$(sha256 "$archive")
if [ "$got_sha" != "$want_sha" ]; then
	err "sha256 mismatch for $target
  want: $want_sha
  got:  $got_sha"
fi

tar -xzf "$archive" -C "$tmpdir"
[ -f "$tmpdir/arbe" ] || err "archive did not contain an 'arbe' binary"
chmod +x "$tmpdir/arbe"

mkdir -p "$INSTALL_DIR"
mv "$tmpdir/arbe" "$INSTALL_DIR/arbe"

short_commit=$(printf '%s' "$commit" | cut -c1-7)
printf '\narbe-install: installed arbe %s -> %s/arbe\n' "$short_commit" "$INSTALL_DIR"

case ":$PATH:" in
	*":$INSTALL_DIR:"*) ;;
	*)
		printf '\n%s is not on your PATH. Add it with:\n\n' "$INSTALL_DIR"
		case "${SHELL:-}" in
			*/fish)
				printf '  fish_add_path -U %s\n\n' "$INSTALL_DIR"
				;;
			*/zsh)
				printf '  echo '\''export PATH="%s:$PATH"'\'' >> ~/.zshrc && source ~/.zshrc\n\n' "$INSTALL_DIR"
				;;
			*)
				printf '  echo '\''export PATH="%s:$PATH"'\'' >> ~/.bashrc && source ~/.bashrc\n\n' "$INSTALL_DIR"
				;;
		esac
		;;
esac

printf 'Run `arbe --version` to confirm.\n'
