#!/bin/sh
#
# AegisSole for Linux — one-line installer.
#
#     wget -nv -O- https://static.aegoralab.com/linux/install.sh | sudo sh
#     curl -fsSL  https://static.aegoralab.com/linux/install.sh | sudo sh
#
# `-nv` rather than `-q`, and `-fsS` rather than `-s`: a download that
# fails must say so. With `wget -q` a name that will not resolve produces
# an empty script, `sh` reads nothing, succeeds, and the user is told
# precisely nothing about why the program is not installed.
#
# Written in POSIX sh, not bash: this is the first thing that runs on a
# machine we know nothing about, and Alpine and several container images
# ship no bash at all. It picks a download source, fetches the tarball for
# the machine's architecture, verifies it against the published checksum,
# and hands off to the installer inside it.
#
# Environment overrides (all optional):
#     AEGISSOLE_BASE_URL   a single source to use, skipping selection
#     AEGISSOLE_ARCH       force amd64 or arm64 instead of detecting

set -eu

# Two sources, deliberately on different networks. The first is the same
# object store the Mac, Windows and Android builds are served from. The
# second is a mirror on our own Hong Kong host, which reaches mainland
# China far more reliably than the CDN's free tier does at peak hours.
PRIMARY="https://static.aegoralab.com/linux/latest"
MIRROR="https://hk.pridelory.com/dl/linux/latest"

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

# ── preconditions ───────────────────────────────────────────────────

# Everything below writes to /usr and talks to systemd, so failing here is
# much kinder than failing halfway through.
[ "$(id -u)" = 0 ] || die "this must run as root — pipe it into 'sudo sh' instead of 'sh'"

command -v systemctl >/dev/null 2>&1 \
    || die "systemd is required, and systemctl was not found"

# The installer inside the tarball is bash. Checking for it now turns a
# confusing 'exec format error' into a sentence.
command -v bash >/dev/null 2>&1 \
    || die "bash is required to run the installer (install it and retry)"

# Minimal cloud images sometimes ship without it. Failing here names the
# one package to install; failing at the unpack step three steps later
# looks like a corrupt download.
command -v tar >/dev/null 2>&1 \
    || die "tar is required to unpack the release (install it and retry)"

if command -v curl >/dev/null 2>&1; then
    fetch() { curl -fsSL --connect-timeout "$3" --max-time "$4" "$1" -o "$2"; }
elif command -v wget >/dev/null 2>&1; then
    fetch() { wget -q --connect-timeout="$3" --timeout="$4" -O "$2" "$1"; }
else
    die "neither curl nor wget is available"
fi

# Any of the three is fine; distributions disagree on which they ship.
if command -v sha256sum >/dev/null 2>&1; then
    sha256() { sha256sum "$1" | cut -d' ' -f1; }
elif command -v shasum >/dev/null 2>&1; then
    sha256() { shasum -a 256 "$1" | cut -d' ' -f1; }
elif command -v openssl >/dev/null 2>&1; then
    sha256() { openssl dgst -sha256 "$1" | sed 's/.*= *//'; }
else
    die "no SHA-256 tool found (install coreutils or openssl)"
fi

# ── architecture ────────────────────────────────────────────────────

if [ -n "${AEGISSOLE_ARCH:-}" ]; then
    ARCH="$AEGISSOLE_ARCH"
else
    case "$(uname -m)" in
        x86_64|amd64)   ARCH=amd64 ;;
        aarch64|arm64)  ARCH=arm64 ;;
        *) die "unsupported architecture: $(uname -m) (amd64 and arm64 are available)" ;;
    esac
fi

WORK="$(mktemp -d)"
# Runs on every exit path, so a failed install never leaves a
# half-downloaded tarball behind.
trap 'rm -rf "$WORK"' EXIT INT TERM

# ── source selection ────────────────────────────────────────────────

# The index is a few hundred bytes, so fetching it doubles as the
# reachability probe: whichever source serves it within the budget can
# serve a 6 MB tarball without the user staring at a dead terminal. The
# budget is deliberately tight — a healthy source answers in well under a
# second, and "reachable but crawling" is exactly the mainland-China case
# the mirror exists for.
try_index() {
    fetch "$1/SHA256SUMS" "${WORK}/SHA256SUMS" 4 8 2>/dev/null
}

if [ -n "${AEGISSOLE_BASE_URL:-}" ]; then
    BASE_URL="$AEGISSOLE_BASE_URL"
    try_index "$BASE_URL" || die "could not reach ${BASE_URL}"
elif try_index "$PRIMARY"; then
    BASE_URL="$PRIMARY"
elif try_index "$MIRROR"; then
    BASE_URL="$MIRROR"
    say "the main source was slow, using the mirror"
else
    die "could not reach any download source — check the machine's connectivity"
fi

# ── download and verify ─────────────────────────────────────────────

# The index is the source of truth for the file name, so no version is
# hard-coded here: publishing a release is replacing the files behind
# these URLs, and this script keeps working untouched.
NAME="$(awk -v a="-${ARCH}.tar.gz" '$2 ~ a {print $2}' "${WORK}/SHA256SUMS" | head -n1)"
[ -n "$NAME" ] || die "the release index lists no build for ${ARCH}"

WANT="$(awk -v n="$NAME" '$2 == n {print $1}' "${WORK}/SHA256SUMS" | head -n1)"
[ -n "$WANT" ] || die "the release index has no checksum for ${NAME}"

say "downloading ${NAME}"
if ! fetch "${BASE_URL}/${NAME}" "${WORK}/${NAME}" 10 600; then
    # A source that served the index can still fail on the payload
    # (partial object, mid-transfer reset). Falling back costs one retry
    # and saves the user from re-running the whole command.
    if [ "$BASE_URL" = "$PRIMARY" ] && try_index "$MIRROR"; then
        say "the download failed, retrying from the mirror"
        BASE_URL="$MIRROR"
        WANT="$(awk -v n="$NAME" '$2 == n {print $1}' "${WORK}/SHA256SUMS" | head -n1)"
        fetch "${BASE_URL}/${NAME}" "${WORK}/${NAME}" 10 600 || die "download failed"
    else
        die "download failed"
    fi
fi

GOT="$(sha256 "${WORK}/${NAME}")"
if [ "$GOT" != "$WANT" ]; then
    # Deliberately fatal and deliberately loud: a mismatch is either a
    # corrupted transfer or a tampered file, and there is no safe way to
    # tell them apart from here.
    die "checksum mismatch for ${NAME}
       expected ${WANT}
       received ${GOT}"
fi
say "checksum verified"

# ── install ─────────────────────────────────────────────────────────

mkdir -p "${WORK}/pkg"
tar -xzf "${WORK}/${NAME}" -C "${WORK}/pkg"

# `< /dev/null` matters: when this script is itself being piped into sh,
# stdin is the remainder of the pipe, and anything the installer reads
# would eat it.
bash "${WORK}/pkg/install.sh" "${WORK}/pkg" < /dev/null
