#!/bin/sh
set -eu

# rnx installer
#
# this installs the standalone rnx executable and hands machine and repository
# decisions to `rnx setup`. it does not require node, bun, npm, or sudo.
#
# inspect this entire script without running it:
#
#   curl -fsSL https://rnxsim.com/install.sh | less
#
# install:
#
#   curl -fsSL https://rnxsim.com/install.sh | sh
#
# options:
#   RNX_VERSION=0.1.377     install a specific cli version
#   RNX_CLI_PREFIX=...      choose the user-writable cli prefix
#   RNX_NO_MODIFY_PATH=1    do not update your shell profile

RNX_VERSION="${RNX_VERSION:-latest}"
CLI_PREFIX="${RNX_CLI_PREFIX:-$HOME/.local}"
CLI="$CLI_PREFIX/bin/rnx"
RELEASE_ORIGIN="https://r2.rnxsim.com/rnx-cli"

say() {
  printf '%s\n' "$*"
}

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

command_exists() {
  command -v "$1" >/dev/null 2>&1
}

command_exists curl || fail "curl is required to install rnx"

case "$(uname -s)" in
  Darwin)
    PLATFORM="darwin"
    PLATFORM_LABEL="macOS"
    ;;
  Linux)
    PLATFORM="linux"
    PLATFORM_LABEL="Linux"
    ;;
  *)
    fail "this installer currently supports macOS and Linux"
    ;;
esac

case "$(uname -m)" in
  arm64 | aarch64)
    ARCH="arm64"
    ;;
  x86_64 | amd64)
    ARCH="x64"
    ;;
  *)
    fail "rnx does not support this processor architecture"
    ;;
esac

if [ "$RNX_VERSION" = "latest" ]; then
  RNX_VERSION="$(curl -fsSL "$RELEASE_ORIGIN/channels/stable")" ||
    fail "could not resolve the current rnx version"
fi

printf '%s' "$RNX_VERSION" | grep -Eq '^[0-9]+\.[0-9]+\.[0-9]+(-[0-9A-Za-z.-]+)?$' ||
  fail "RNX_VERSION must be 'latest' or a semantic version"

ARTIFACT="rnx-$PLATFORM-$ARCH"
ARTIFACT_URL="$RELEASE_ORIGIN/$RNX_VERSION/$ARTIFACT"
TMP_DIR="$(mktemp -d "${TMPDIR:-/tmp}/rnx-install.XXXXXX")"
TMP_BIN="$TMP_DIR/rnx"
TMP_SHA="$TMP_DIR/rnx.sha256"
cleanup() {
  rm -rf "$TMP_DIR"
}
trap cleanup EXIT HUP INT TERM

say ""
say "rnx installer"
say ""
say "  platform: $PLATFORM_LABEL $ARCH"
say "  version:  $RNX_VERSION"
say "  command:  $CLI"
say ""
say "Downloading the standalone rnx executable..."
curl -fsSL "$ARTIFACT_URL" -o "$TMP_BIN" || fail "could not download rnx"
curl -fsSL "$ARTIFACT_URL.sha256" -o "$TMP_SHA" ||
  fail "could not download the rnx checksum"

EXPECTED_SHA="$(tr -d '[:space:]' <"$TMP_SHA")"
printf '%s' "$EXPECTED_SHA" | grep -Eq '^[a-f0-9]{64}$' ||
  fail "the rnx checksum is invalid"
if [ "$PLATFORM" = "darwin" ]; then
  ACTUAL_SHA="$(shasum -a 256 "$TMP_BIN" | awk '{print $1}')"
else
  ACTUAL_SHA="$(sha256sum "$TMP_BIN" | awk '{print $1}')"
fi
[ "$ACTUAL_SHA" = "$EXPECTED_SHA" ] || fail "the rnx executable failed verification"
chmod 755 "$TMP_BIN"
VERSION_OUTPUT="$("$TMP_BIN" --version 2>/dev/null)" ||
  fail "the downloaded rnx executable did not start"
REPORTED_VERSION="$(
  printf '%s\n' "$VERSION_OUTPUT" |
    sed -n '1s/^rnx v\([^[:space:]]*\).*$/\1/p'
)"
[ "$REPORTED_VERSION" = "$RNX_VERSION" ] ||
  fail "the downloaded rnx executable reported the wrong version"

mkdir -p "$CLI_PREFIX/bin"
NEXT_CLI="$CLI.next.$$"
cp "$TMP_BIN" "$NEXT_CLI"
mv -f "$NEXT_CLI" "$CLI"

update_shell_profile() {
  profile="$1"
  shell_name="$2"
  path_line="export PATH=\"$CLI_PREFIX/bin:\$PATH\""
  integration=""
  if [ -n "$shell_name" ]; then
    integration="eval \"\$(command rnx __shell-init $shell_name)\""
  fi

  add_path=0
  if ! { [ -f "$profile" ] && grep -F "$path_line" "$profile" >/dev/null 2>&1; }; then
    add_path=1
  fi

  add_integration=0
  if [ -n "$integration" ] &&
    ! { [ -f "$profile" ] && grep -F "$integration" "$profile" >/dev/null 2>&1; }; then
    add_integration=1
  fi

  if [ "$add_path" = 0 ] && [ "$add_integration" = 0 ]; then
    return 0
  fi

  {
    printf '\n'
    printf '# added by the rnx installer; delete this block to remove rnx from your shell\n'
    if [ "$add_path" = 1 ]; then
      printf '%s\n' "$path_line"
    fi
    if [ "$add_integration" = 1 ]; then
      printf '%s\n' "$integration"
    fi
  } >>"$profile"

  if [ "$add_path" = 1 ]; then
    say "Added $CLI_PREFIX/bin to PATH in $profile"
  fi
  if [ "$add_integration" = 1 ]; then
    say "Added RNX shell integration to $profile"
  fi
}

if [ "${RNX_NO_MODIFY_PATH:-0}" = "1" ]; then
  case ":$PATH:" in
    *":$CLI_PREFIX/bin:"*) ;;
    *)
      say ""
      say "$CLI_PREFIX/bin is not currently on PATH. Add it with:"
      say "  export PATH=\"$CLI_PREFIX/bin:\$PATH\""
      ;;
  esac
  case "${SHELL:-}" in
    */zsh) say "  eval \"\$(command rnx __shell-init zsh)\"" ;;
    */bash) say "  eval \"\$(command rnx __shell-init bash)\"" ;;
  esac
else
  case "${SHELL:-}" in
    */zsh) update_shell_profile "$HOME/.zshrc" zsh ;;
    */bash) update_shell_profile "$HOME/.bashrc" bash ;;
    *) update_shell_profile "$HOME/.profile" "" ;;
  esac
fi

PATH="$CLI_PREFIX/bin:$PATH"
export PATH

say ""
say "rnx is installed."
say ""
if [ -t 1 ] && [ -r /dev/tty ] && [ -w /dev/tty ]; then
  say "Continuing with guided setup..."
  "$CLI" setup </dev/tty
else
  say "Run this deterministic setup command in CI:"
  say ""
  say "  rnx setup --ci --yes --json"
fi
