#!/bin/sh

# Locate a usable node interpreter and write its path to $1 (or stdout).
#
# Resolution order, stopping at the first that satisfies the minimum version:
#   1. $NODE_DIR/bin/node     -- explicit override; authoritative (errors if too old)
#   2. node / nodejs on PATH  -- system node, and any version manager with shell
#                                integration (fnm, asdf, nodenv, volta, an active nvm)
#   3. a detected version manager, honouring .node-version -- the rescue path for
#      nvm's off-PATH default, or a manager without `cd` auto-switching
#
# The minimum version lives in .node-version (the same file version managers
# read) so there's a single source of truth. Newer versions are always fine.

set -e
OUT="$1"

# shellcheck disable=SC1007  # `CDPATH= cd` is the intended empty-assignment idiom
ROOT="$(CDPATH= cd -- "$(dirname "$0")/../.." && pwd -P)"
MIN="$(sed 's/^v//; q' "${ROOT}/.node-version")"
MIN_MAJOR="$(printf '%s' "${MIN}" | cut -d. -f1)"

# Full semver string for the node at $1 (without leading 'v'), or empty if it can't be run.
node_version() {
  "$1" --version 2>/dev/null | sed 's/^v//'
}

# Returns true (0) if semver $1 >= semver $2.
semver_ge() {
  awk -v a="$1" -v b="$2" 'BEGIN {
    n = split(a, A, "."); for (i = n + 1; i <= 3; i++) A[i] = 0
    m = split(b, B, "."); for (i = m + 1; i <= 3; i++) B[i] = 0
    if (A[1] + 0 != B[1] + 0) { exit (A[1] + 0 > B[1] + 0 ? 0 : 1) }
    if (A[2] + 0 != B[2] + 0) { exit (A[2] + 0 > B[2] + 0 ? 0 : 1) }
    exit (A[3] + 0 >= B[3] + 0 ? 0 : 1)
  }'
}

# True if $1 is an executable node satisfying the minimum version.
usable() {
  [ -n "$1" ] && [ -x "$1" ] || return 1
  _v="$(node_version "$1")"
  [ -n "${_v}" ] && semver_ge "${_v}" "${MIN}" 2>/dev/null
}

# Echo the first usable node discovered on PATH or via a version manager.
discover() {
  for _cand in node nodejs; do
    _p="$(command -v "${_cand}" 2>/dev/null || true)"
    if usable "${_p}"; then echo "${_p}"; return 0; fi
  done

  # nvm is a shell function, not a PATH binary, so source it and ask for the
  # version pinned by .node-version (a prefix like "22" resolves to the latest
  # installed 22.x). This is the case plain PATH discovery can't cover.
  _nvm="${NVM_DIR:-${HOME}/.nvm}/nvm.sh"
  if [ -s "${_nvm}" ]; then
    # shellcheck disable=SC1090  # nvm.sh location is user-dependent
    # shellcheck disable=SC2015  # || true is intentional: suppress exit from set -e in both paths
    _p="$(. "${_nvm}" >/dev/null 2>&1 && nvm which "${MIN_MAJOR}" 2>/dev/null || true)"
    if usable "${_p}"; then echo "${_p}"; return 0; fi
  fi

  # Other managers expose a binary that honours the local .node-version.
  if command -v fnm >/dev/null 2>&1; then
    _p="$(fnm exec --using "${MIN_MAJOR}" -- node -p 'process.execPath' 2>/dev/null || true)"
    if usable "${_p}"; then echo "${_p}"; return 0; fi
  fi
  if command -v nodenv >/dev/null 2>&1; then
    _p="$(nodenv which node 2>/dev/null || true)"
    if usable "${_p}"; then echo "${_p}"; return 0; fi
  fi
  if command -v asdf >/dev/null 2>&1; then
    _p="$(asdf which node 2>/dev/null || true)"
    if usable "${_p}"; then echo "${_p}"; return 0; fi
  fi

  return 1
}

fail() {
  echo >&2 "$@"
  echo >&2
  echo >&2 "Compiler Explorer needs node ${MIN} or newer (see .node-version)."
  echo >&2 "Install it from https://nodejs.org/, or:"
  echo >&2 "  - point NODE_DIR at an existing install (we use \$NODE_DIR/bin/node), or"
  echo >&2 "  - select it with your version manager (e.g. 'nvm use', 'fnm use')."
  exit 1
}

if [ -n "${NODE_DIR}" ]; then
  # Explicit override: honour it, and complain clearly if it's unusable rather
  # than silently falling back to something else.
  NODE="${NODE_DIR}/bin/node"
  if [ ! -x "${NODE}" ]; then
    fail "NODE_DIR is set to '${NODE_DIR}' but '${NODE}' is not executable."
  fi
  _v="$(node_version "${NODE}")"
  if [ -z "${_v}" ] || ! semver_ge "${_v}" "${MIN}" 2>/dev/null; then
    fail "NODE_DIR points at node v${_v:-?}, but ${MIN} or newer is required."
  fi
else
  NODE="$(discover || true)"
  if [ -z "${NODE}" ]; then
    fail "Unable to find node ${MIN} or newer on your PATH."
  fi
fi

if [ -n "${OUT}" ]; then
  echo "${NODE}" >"${OUT}"
else
  echo "${NODE}"
fi
