#!/bin/sh
# =============================================================================
# ParalleliX Node CLI · one-command installer
# =============================================================================
#
# Usage:
#     curl -fsSL https://parallelix.io/install.sh | sh
#
# On LINUX this bootstraps the WHOLE operator stack so a fresh GPU box goes
# from nothing to "ready to register" in one command:
#   1. Node.js (LTS) if it's missing or older than 18.
#   2. Ollama (the local GPU inference runtime) via its official installer,
#      which auto-detects the GPU and sets up the service.
#   3. The default model (llama3.2) pulled and ready.
#   4. parallelix-node + its crypto deps (@noble/curves, @noble/hashes), and a
#      shim at /usr/local/bin/parallelix-node.
#
# On macOS it installs Node + the CLI and (if Homebrew is present) Ollama;
# otherwise it points you at the Ollama desktop app, which can't be installed
# from a shell. Windows: use WSL2 and run this inside it.
#
# Overrides (env): PARALLELIX_MODEL=<name>   pick a different default model
#                  PARALLELIX_SKIP_MODEL=1   skip the model pull (do it later)
#
# Powerful by nature (installs system packages with sudo) — same shape as the
# Ollama / Homebrew / rustup installers. Read it first if you like.
# =============================================================================

set -eu

CLI_URL="https://parallelix.io/cli/parallelix-node.mjs"
INSTALL_DIR="/usr/local/lib/parallelix"
SHIM_PATH="/usr/local/bin/parallelix-node"
NODE_MIN=18
NODE_LTS_LINE="22"
DEFAULT_MODEL="${PARALLELIX_MODEL:-llama3.2}"
SKIP_MODEL="${PARALLELIX_SKIP_MODEL:-0}"

TOXIC=$(printf '\033[38;2;215;255;1m')
DIM=$(printf '\033[2m')
RED=$(printf '\033[38;2;255;90;90m')
RESET=$(printf '\033[0m')
ok()   { printf '%s✓%s %s\n' "$TOXIC" "$RESET" "$1"; }
info() { printf '%s·%s %s\n' "$DIM" "$RESET" "$1"; }
warn() { printf '%s!%s %s\n' "$RED" "$RESET" "$1" >&2; }
die()  { printf '%sparallelix-installer:%s %s\n' "$RED" "$RESET" "$1" >&2; exit 1; }

OS="$(uname -s)"
ARCH="$(uname -m)"

# Root vs sudo. System installs (Node, Ollama, the shim) need write to /usr/local.
if [ "$(id -u)" -eq 0 ]; then
  SUDO=""
elif command -v sudo >/dev/null 2>&1; then
  SUDO="sudo"
else
  die "need root or sudo to install Node / Ollama / the CLI into /usr/local"
fi

banner() {
  printf '\n%s// parallelix node cli · one-command installer%s\n' "$TOXIC" "$RESET"
  printf '%s   Node.js + Ollama + model + parallelix-node%s\n\n' "$DIM" "$RESET"
}

# ── Node.js ───────────────────────────────────────────────────────────────────
node_ok() {
  command -v node >/dev/null 2>&1 || return 1
  [ "$(node -p 'process.versions.node.split(".")[0]' 2>/dev/null || echo 0)" -ge "$NODE_MIN" ]
}
install_node() {
  if node_ok; then ok "Node $(node -v) present"; return; fi
  info "installing Node.js ${NODE_LTS_LINE} LTS ..."
  case "$ARCH" in
    x86_64 | amd64) narch=x64 ;;
    aarch64 | arm64) narch=arm64 ;;
    *) die "unsupported CPU arch for auto Node install: $ARCH" ;;
  esac
  case "$OS" in
    Linux) plat=linux ;;
    Darwin) plat=darwin ;;
    *) die "unsupported OS for auto Node install: $OS (install Node 18+ manually)" ;;
  esac
  base="https://nodejs.org/dist/latest-v${NODE_LTS_LINE}.x"
  fname=$(curl -fsSL "$base/SHASUMS256.txt" 2>/dev/null | grep -oE "node-v[0-9.]+-${plat}-${narch}\.tar\.gz" | head -1) || true
  [ -n "${fname:-}" ] || die "could not resolve a Node build for ${plat}-${narch}"
  tmp=$(mktemp -d)
  info "downloading ${fname}"
  curl -fsSL "$base/$fname" -o "$tmp/node.tar.gz" || die "Node download failed"
  $SUDO mkdir -p /usr/local
  $SUDO tar -xzf "$tmp/node.tar.gz" -C /usr/local --strip-components=1 || die "Node extract failed"
  rm -rf "$tmp"
  export PATH="/usr/local/bin:$PATH"
  if node_ok; then ok "Node $(node -v) installed"; else die "Node installed but not on PATH; add /usr/local/bin to PATH and re-run"; fi
}

# ── Ollama (the GPU inference runtime) ────────────────────────────────────────
ollama_up() { curl -fsS http://127.0.0.1:11434/api/version >/dev/null 2>&1; }
install_ollama() {
  if command -v ollama >/dev/null 2>&1 || ollama_up; then ok "Ollama present"; return; fi
  case "$OS" in
    Linux)
      info "installing Ollama via its official installer (auto-detects your GPU) ..."
      curl -fsSL https://ollama.com/install.sh | sh || die "Ollama install failed (see https://ollama.com/download)"
      ;;
    Darwin)
      if command -v brew >/dev/null 2>&1; then
        info "installing Ollama via Homebrew ..."
        brew install ollama || die "brew install ollama failed"
      else
        warn "On macOS, install the Ollama app: https://ollama.com/download"
        warn "Install it, start it, then re-run this command."
        exit 1
      fi
      ;;
    *) die "unsupported OS for Ollama auto-install: $OS (see https://ollama.com/download)" ;;
  esac
}
start_ollama() {
  if ollama_up; then ok "Ollama running"; return; fi
  if command -v systemctl >/dev/null 2>&1 && systemctl list-unit-files 2>/dev/null | grep -q '^ollama.service'; then
    $SUDO systemctl enable --now ollama >/dev/null 2>&1 || true
  else
    info "starting Ollama in the background ..."
    nohup ollama serve >/dev/null 2>&1 &
  fi
  i=0
  while [ "$i" -lt 30 ]; do
    if ollama_up; then break; fi
    i=$((i + 1))
    sleep 1
  done
  if ollama_up; then ok "Ollama running"; else warn "Ollama not reachable on :11434 yet (it may still be starting)"; fi
}
pull_model() {
  if [ "$SKIP_MODEL" = "1" ]; then info "skipping model pull (PARALLELIX_SKIP_MODEL=1)"; return; fi
  if ! ollama_up; then warn "Ollama not running; skipping model pull. Later: ollama pull $DEFAULT_MODEL"; return; fi
  info "pulling default model ${DEFAULT_MODEL} (first run downloads it, ~2 GB) ..."
  if ollama pull "$DEFAULT_MODEL"; then ok "model ${DEFAULT_MODEL} ready"; else warn "model pull failed; 'parallelix-node start' will pull it on demand"; fi
}

# ── parallelix-node CLI ───────────────────────────────────────────────────────
install_cli() {
  info "installing parallelix-node + crypto deps (@noble/curves, @noble/hashes) ..."
  $SUDO mkdir -p "$INSTALL_DIR"
  $SUDO tee "$INSTALL_DIR/package.json" >/dev/null <<'EOF'
{
  "name": "parallelix-node-runtime",
  "version": "1.0.0",
  "private": true,
  "type": "module",
  "dependencies": { "@noble/curves": "^1.6.0", "@noble/hashes": "^1.5.0" }
}
EOF
  if ! (cd "$INSTALL_DIR" && $SUDO npm install --silent --no-audit --no-fund --omit=dev); then
    die "npm install (crypto deps) failed in $INSTALL_DIR"
  fi
  info "downloading parallelix-node.mjs ..."
  $SUDO curl -fsSL "$CLI_URL" -o "$INSTALL_DIR/parallelix-node.mjs" || die "failed to download $CLI_URL"
  $SUDO chmod 644 "$INSTALL_DIR/parallelix-node.mjs"
  $SUDO tee "$SHIM_PATH" >/dev/null <<EOF
#!/bin/sh
# parallelix-node shim — runs the CLI with cwd at its install dir so the local
# node_modules (@noble/curves, @noble/hashes) resolves.
cd "$INSTALL_DIR" && exec node "$INSTALL_DIR/parallelix-node.mjs" "\$@"
EOF
  $SUDO chmod 755 "$SHIM_PATH"
  ok "parallelix-node installed"
}

next_steps() {
  printf '\n%s✓ ready%s  %sNode + Ollama + %s + parallelix-node installed%s\n' \
    "$TOXIC" "$RESET" "$DIM" "$DEFAULT_MODEL" "$RESET"
  printf '\n'
  printf '  next:  parallelix-node setup   %sone command: identity -> stake link -> auto-detect -> 24/7 service%s\n' \
    "$DIM" "$RESET"
  printf '  docs:  https://docs.parallelix.io/node-cli\n'
  printf '\n'

  # Offer to run setup right now when we have a real terminal to ask on.
  # Piped installs (curl | sh) read the script itself from stdin, so [ -t 0 ]
  # is false there and we never steal the stream.
  if [ -t 0 ] && [ -t 1 ]; then
    printf '%s' "run setup now? [Y/n] "
    read -r ans || ans=n
    case "$ans" in n* | N*) : ;; *) exec parallelix-node setup ;; esac
  fi
}

main() {
  banner
  command -v curl >/dev/null 2>&1 || die "missing required command: curl"
  install_node
  install_ollama
  start_ollama
  pull_model
  install_cli
  next_steps
}

main "$@"
