#!/bin/bash
# Nodebase client one-line installer, served from the download host:
#
#   curl -fsSL https://dl.nodebase.com/install.sh | bash
#
# Published to /srv/nodebase-dl by the release flow (make release-client puts
# it in dist/ next to the binaries). It must stay dependency-free beyond curl
# and the stock sha256 tool, because it runs on machines that have nothing
# else installed yet.
#
# Env overrides:
#   NODEBASE_DL_URL      download server; when unset the installer tries the
#                        servers below in order (CN origin first)
#   NODEBASE_INSTALL_DIR install target (default /usr/local/bin)
set -euo pipefail

# Download servers, tried in order: the CN-routed origin first (brand then
# u24.net backup), US mirror last (brand then backup). CN-first because the US
# mirror delivers this binary at 16-33 KB/s from inside CN — slow enough that
# the download times out and the user ends up on a stale version. The u24.net
# pair is the fallback for when the brand domain is DNS-poisoned. Keep this in
# step with downloadCandidates in cmd/nodebase/endpoints.go.
SERVERS=(https://dl.nodebase.com https://dl.u24.net https://dl-us.nodebase.com https://dl-us.u24.net)
INSTALL_DIR=${NODEBASE_INSTALL_DIR:-/usr/local/bin}

# Only the published build targets exist (Makefile CLIENT_PLATFORMS); anything
# else must fail here rather than 404 confusingly later. Windows has its own
# installer (install.ps1) because this script's world — uname, sha256sum,
# /usr/local/bin — does not exist there outside of git-bash.
OS=$(uname -s) ARCH=$(uname -m)
case "$OS/$ARCH" in
  Darwin/arm64)          ASSET=nodebase-darwin-arm64 ;;
  Darwin/x86_64)         ASSET=nodebase-darwin-amd64 ;;
  Linux/x86_64)          ASSET=nodebase-linux-amd64 ;;
  Linux/aarch64)         ASSET=nodebase-linux-arm64 ;;
  MINGW*|MSYS*|CYGWIN*)  echo "on Windows, run this from an Administrator PowerShell instead:" >&2
                         echo "  irm https://dl.nodebase.com/install.ps1 | iex" >&2
                         exit 1 ;;
  *) echo "unsupported platform: $OS $ARCH (builds exist for macOS arm64/amd64, Linux amd64/arm64, and Windows x64)" >&2
     exit 1 ;;
esac

fetch() { curl -fsSL --connect-timeout 8 "$@"; }

# resume_fetch downloads a large asset that must survive a dropped connection:
# CN links routinely cut mid-transfer, and without resume a 35 MB download
# restarts from zero each time and may never complete. It reads the total from
# a HEAD, then resumes with -C - until the local file reaches it, capped at a
# few attempts. The dl origin serves Range (tools/nbdl); if a server does not,
# -C - simply refetches from the front and the size check still terminates.
resume_fetch() {
  url=$1; out=$2
  headers=$(fetch -I "$url" 2>/dev/null) || return 1
  total=$(printf '%s\n' "$headers" | awk 'tolower($1)=="content-length:"{print $2}' | tr -d '\r' | tail -1)
  for i in 1 2 3 4 5 6; do
    have=0; [ -f "$out" ] && have=$(wc -c <"$out")
    if [ -n "$total" ] && [ "$have" -ge "$total" ] 2>/dev/null; then return 0; fi
    ok=0
    if curl -fsSL --connect-timeout 8 -C - -o "$out" "$url"; then ok=1; fi
    have=0; [ -f "$out" ] && have=$(wc -c <"$out")
    if [ -n "$total" ] && [ "$have" -ge "$total" ] 2>/dev/null; then return 0; fi
    if [ -z "$total" ] && [ "$ok" = 1 ]; then return 0; fi
    echo "  connection dropped at ${have}B${total:+/$total} — resuming ($i) …" >&2
    sleep 2
  done
  return 1
}

# An explicit NODEBASE_DL_URL is honored with no fallback — if the user picked
# a server, silently switching to another would be surprising. Otherwise try
# each server in order and take the first that answers. Output never names the
# server: which host served the download must not travel in a shared log.
if [ -n "${NODEBASE_DL_URL:-}" ]; then
  SERVERS=("$NODEBASE_DL_URL")
fi
DL=""
for s in "${SERVERS[@]}"; do
  if VERSION=$(fetch "$s/version.txt" 2>/dev/null); then
    DL=$s
    break
  fi
done
if [ -z "$DL" ]; then
  echo "no download server reachable; check your network" >&2
  exit 1
fi

echo "installing nodebase $VERSION ($ASSET)"

TMP=$(mktemp -d)
trap 'rm -rf "$TMP"' EXIT

# Prefer the host whose version probe succeeded, then every backup. Releases
# live under an immutable version path so a cache cannot combine a new version
# marker with an old binary. The root path is a compatibility fallback for a
# private server or a release published before versioned paths existed.
ORDERED_SERVERS=("$DL")
for s in "${SERVERS[@]}"; do
  [ "$s" = "$DL" ] || ORDERED_SERVERS+=("$s")
done

verify_asset() {
  cd "$TMP"
  if command -v sha256sum >/dev/null; then
    grep " $ASSET\$" SHA256SUMS | sha256sum -c - >/dev/null
  else
    grep " $ASSET\$" SHA256SUMS | shasum -a 256 -c - >/dev/null
  fi
}

downloaded=0
echo "downloading $ASSET …"
for prefix in "cli/$VERSION" ""; do
  for s in "${ORDERED_SERVERS[@]}"; do
    base=${s%/}${prefix:+/$prefix}
    if [ -z "$prefix" ]; then
      candidate_version=$(fetch "${s%/}/version.txt" 2>/dev/null | tr -d '[:space:]' || true)
      [ "$candidate_version" = "$VERSION" ] || continue
    fi
    rm -f "$TMP/$ASSET" "$TMP/SHA256SUMS"
    if fetch -o "$TMP/SHA256SUMS" "$base/SHA256SUMS" 2>/dev/null &&
       grep -q " $ASSET\$" "$TMP/SHA256SUMS" &&
       resume_fetch "$base/$ASSET" "$TMP/$ASSET" &&
       verify_asset; then
      downloaded=1
      break 2
    fi
  done
done
if [ "$downloaded" != 1 ]; then
  echo "no download server supplied a complete, checksum-valid $ASSET" >&2
  exit 1
fi

cd "$TMP"
echo "checksum OK"

chmod 755 "$ASSET"
# /usr/local/bin may not exist on a fresh Apple Silicon Mac, and is usually
# root-owned; escalate only for the final placement so the download and
# verification never run as root.
SUDO=""
if [ ! -w "$INSTALL_DIR" ] || [ ! -d "$INSTALL_DIR" ]; then
  if [ "$(id -u)" -ne 0 ]; then
    SUDO="sudo"
    echo
    echo "placing nodebase into $INSTALL_DIR needs admin rights, so sudo may now ask"
    echo "for a password — that's this computer's own login password (nothing is"
    echo "sent anywhere; only this one file is installed)"
  fi
fi
$SUDO mkdir -p "$INSTALL_DIR"
$SUDO mv "$ASSET" "$INSTALL_DIR/nodebase"

echo
echo "installed: $INSTALL_DIR/nodebase ($("$INSTALL_DIR/nodebase" --version 2>/dev/null || echo "$VERSION"))"
echo
echo "next steps:"
echo "  1. nodebase login          # sign in — we'll email you a one-time code"
echo "  2. sudo nodebase connect   # join your workspace's private network"
echo "     (sudo asks for this computer's login password; creating the tunnel"
echo "      interface needs admin rights)"
echo "  3. nodebase                # the menu takes it from there: SSH into"
echo "                             # your worker, tune network and routing"
echo
# One line, because the alternative is a user deleting the binary by hand and
# leaving the boot registration behind (2026-07-29 field case).
echo "to remove it later: sudo nodebase uninstall"
