#!/bin/sh
# Native infimal installer: https://infimal.ai/install.sh
# INFIMAL_INSTALL_BASE: release directory (default: https://infimal.ai/cli)
# INFIMAL_INSTALL_VERSION: pin a version; otherwise read the release directory's latest file
# INFIMAL_INSTALL_DIR: destination (default: ~/.local/bin)
# The pre-rename AIRGLOW_INSTALL_* names are read for this release only, with a warning (D360).
# No sudo, Python, shell-profile changes, or credential writes.
set -eu

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

for setting in BASE VERSION DIR; do
  eval "current=\${INFIMAL_INSTALL_$setting:-} previous=\${AIRGLOW_INSTALL_$setting:-}"
  if [ -z "$current" ] && [ -n "$previous" ]; then
    say "warning: AIRGLOW_INSTALL_$setting is deprecated; set INFIMAL_INSTALL_$setting instead (the old name is read in this release only)"
    eval "INFIMAL_INSTALL_$setting=\$previous"
  fi
done

release_base=${INFIMAL_INSTALL_BASE:-https://infimal.ai/cli}
release_base=${release_base%/}
install_dir=${INFIMAL_INSTALL_DIR:-${HOME:?HOME is unset; set HOME or INFIMAL_INSTALL_DIR}/.local/bin}
case "$install_dir" in /*) ;; *) install_dir="$PWD/$install_dir" ;; esac
case "$release_base" in
  https://*) ;;
  http://localhost:*|http://127.0.0.1:*) ;; # local packaging tests
  *) die 'INFIMAL_INSTALL_BASE must use HTTPS (HTTP is allowed for a loopback test server).' ;;
esac

for utility in curl tar mktemp chmod mv mkdir uname; do
  command -v "$utility" >/dev/null 2>&1 || die "Required utility is missing: $utility"
done
if command -v sha256sum >/dev/null 2>&1; then hash_tool=sha256sum
elif command -v shasum >/dev/null 2>&1; then hash_tool=shasum
elif command -v openssl >/dev/null 2>&1; then hash_tool=openssl
else die 'Install sha256sum, shasum, or openssl to verify the download.'
fi

system=$(uname -s)
machine=$(uname -m)
case "$system" in
  Darwin)
    # A shell running under Rosetta can still install the native Apple Silicon binary.
    if [ "$machine" = x86_64 ] && [ "$(sysctl -in sysctl.proc_translated 2>/dev/null || true)" = 1 ]; then machine=arm64; fi
    case "$machine" in arm64|aarch64) target=aarch64-apple-darwin ;; x86_64|amd64) target=x86_64-apple-darwin ;; *) die "Unsupported macOS architecture: $machine" ;; esac ;;
  Linux)
    case "$machine" in arm64|aarch64) target=aarch64-unknown-linux-musl ;; x86_64|amd64) target=x86_64-unknown-linux-musl ;; *) die "Unsupported Linux architecture: $machine (64-bit ARM or x86 is required)." ;; esac ;;
  *) die "Unsupported operating system: $system. Use macOS, Linux, or a Linux terminal in WSL." ;;
esac

# Allow redirects to HTTPS, never to plaintext. Loopback fixtures do not redirect.
fetch() {
  fetch_url=$1
  fetch_destination=$2
  if fetch_status=$(curl --fail --silent --location --proto '=https,http' --proto-redir '=https' --connect-timeout 15 --max-time 300 --retry 2 --write-out '%{http_code}' "$fetch_url" -o "$fetch_destination"); then
    return 0
  fi
  # A CDN can retain a 404 from before a release was published. Retry that response
  # with a unique query; the archive still has to pass its checksum and version checks.
  if [ "$fetch_status" = 404 ]; then
    case "$fetch_url" in *\?*) fetch_separator='&' ;; *) fetch_separator='?' ;; esac
    curl --fail --silent --show-error --location --proto '=https,http' --proto-redir '=https' --connect-timeout 15 --max-time 300 --retry 2 "${fetch_url}${fetch_separator}infimal_retry=${scratch##*/}" -o "$fetch_destination"
  else
    say "Download failed (HTTP $fetch_status): $fetch_url"
    return 1
  fi
}
scratch=$(mktemp -d "${TMPDIR:-/tmp}/infimal-install.XXXXXXXX")
staged=''
cleanup() { rm -rf "$scratch"; if [ -n "$staged" ]; then rm -f "$staged"; fi; }
trap cleanup 0
trap 'exit 130' INT
trap 'exit 143' TERM

version=${INFIMAL_INSTALL_VERSION:-}
if [ -z "$version" ]; then
  fetch "$release_base/latest" "$scratch/latest" || die 'No native release could be downloaded. See https://infimal.ai/cli/ for setup and release information.'
  version=$(cat "$scratch/latest")
fi
# A release identifier is a path component, never a command, option, or relative path.
case "$version" in ''|*[!0-9A-Za-z.+-]*|[!0-9]*) die 'Invalid release version.' ;; esac
case "$version" in *.*.*) ;; *) die 'Invalid release version; expected MAJOR.MINOR.PATCH.' ;; esac

# A release published before the rename (D360) carries the binary's old name; one that has no
# infimal archive is installed exactly as it was built, under that name.
name=infimal
legacy_name=airglow
archive="$name-$version-$target.tar.gz"
say "Installing $name $version for $target..."
if ! fetch "$release_base/$version/$archive" "$scratch/archive.tar.gz" 2>/dev/null; then
  name=$legacy_name
  archive="$name-$version-$target.tar.gz"
  fetch "$release_base/$version/$archive" "$scratch/archive.tar.gz" || die "Native release $version is not available for $target. Your existing CLI has not been changed."
  say "Release $version predates the rename; installing it as $name."
fi
fetch "$release_base/$version/$archive.sha256" "$scratch/checksum" || die 'Could not download the release checksum. Your existing CLI has not been changed.'
expected=$(awk 'NR == 1 { print $1 }' "$scratch/checksum")
case "$expected" in ''|*[!0-9a-fA-F]*) die 'Malformed SHA-256 checksum.' ;; esac
[ "${#expected}" -eq 64 ] || die 'Malformed SHA-256 checksum.'
case "$hash_tool" in
  sha256sum) actual=$(sha256sum "$scratch/archive.tar.gz" | awk '{print $1}') ;;
  shasum) actual=$(shasum -a 256 "$scratch/archive.tar.gz" | awk '{print $1}') ;;
  openssl) actual=$(openssl dgst -sha256 "$scratch/archive.tar.gz" | awk '{print $NF}') ;;
esac
expected=$(printf '%s' "$expected" | tr 'A-F' 'a-f')
[ "$expected" = "$actual" ] || die 'Checksum mismatch. Download rejected; your existing CLI has not been changed.'

# Only one regular file is expected. Extract to stdout, never unpack server paths onto disk.
entries=$(tar -tzf "$scratch/archive.tar.gz") || die 'The release archive could not be read.'
[ "$entries" = "$name" ] || die 'Unexpected files in the release archive.'
mkdir -p "$install_dir" || die "Cannot create $install_dir. Choose a writable INFIMAL_INSTALL_DIR."
[ ! -d "$install_dir/$name" ] || die "$install_dir/$name is a directory. Choose another INFIMAL_INSTALL_DIR."
staged=$(mktemp "$install_dir/.$name.XXXXXXXX") || die "Cannot write to $install_dir. Choose a writable INFIMAL_INSTALL_DIR."
tar -xOzf "$scratch/archive.tar.gz" "$name" > "$staged" || die 'The binary could not be extracted.'
chmod 755 "$staged"
reported=$("$staged" --version) || die 'The downloaded binary cannot run on this machine. Your existing CLI has not been changed.'
[ "$reported" = "$name $version" ] || die 'The binary version does not match the release. Your existing CLI has not been changed.'
mv -f "$staged" "$install_dir/$name" || die 'Could not replace the installed CLI.'
staged=''
say "Installed $reported -> $install_dir/$name"
if [ "$name" != "$legacy_name" ] && [ -f "$install_dir/$legacy_name" ]; then
  say "The pre-rename $install_dir/$legacy_name is still installed; delete it once you use $name."
fi
case ":$PATH:" in
  *":$install_dir:"*) ;;
  *)
    # Single-quote a custom path without allowing shell expansions in the printed command.
    quoted_dir=$(printf '%s' "$install_dir" | sed "s/'/'\\\\''/g")
    say ''
    say 'Add this directory to your PATH (and your shell profile to keep it):'
    say "  export PATH='$quoted_dir':\"\$PATH\""
    ;;
esac
say ''
say 'Next:'
say '  1. Open https://infimal.ai/cli/ and create a CLI key.'
say "  2. Run $name login --key and paste it at the hidden prompt."
say "  3. Run $name to open the dashboard."
say ''
say "Explore offline: $name tui --demo"
say "Read the guide: $name guide"
