#!/usr/bin/env bash
# GridShare Node Agent — AMD ROCm Installer
# Supports: RX 400/500/Vega (GCN), RX 5000 (RDNA1), RX 6000/7000 (RDNA2/3),
#           Radeon PRO W6000/W7000, AMD Instinct MI100/MI200/MI300 (CDNA)
# NOT yet:  RX 9000 series (RDNA 4) — needs ROCm 6.4+; installer stops with a
#           clear message rather than installing a stack the card cannot use.
#   GCN / RDNA1 / Ubuntu 20.04  → ROCm 5.7 (auto-detected)
#   RDNA2 / RDNA3 / Ubuntu 22.04 → ROCm 6.1 (best performance)
# Run as root or with sudo:
#   curl -fsSL https://gridshare.in/dl/install-amd.sh | sudo bash
# ─────────────────────────────────────────────────────────────────────────────
set -euo pipefail

CENTRAL_URL="${GRIDSHARE_CENTRAL_URL:-https://relay.gridshare.in}"
NODE_VERSION="1.3.93"
# Account-first install: set GRIDSHARE_TOKEN (a gsp_… provision token minted in the
# portal) to install with ZERO prompts. Priority: TOKEN > KEY > interactive.
GRIDSHARE_TOKEN="${GRIDSHARE_TOKEN:-}"
GRIDSHARE_KEY="${GRIDSHARE_KEY:-}"
GRIDSHARE_LABEL="${GRIDSHARE_LABEL:-$(hostname)}"
GRIDSHARE_CITY="${GRIDSHARE_CITY:-}"
SERVICE_NAME="gridshare-node"
INSTALL_DIR="/opt/gridshare"
VENV_DIR="$INSTALL_DIR/venv"
CONFIG_DIR="/etc/gridshare"
LOG_DIR="/var/log/gridshare"
ROCM_VERSION="6.1"   # may be overridden to 5.7.1 based on GPU arch / OS
GPU_ARCH="rdna2"     # gcn | rdna1 | rdna2 | rdna3

# ── Colour helpers ────────────────────────────────────────────────────────────
_bold=$(tput bold 2>/dev/null || true)
_reset=$(tput sgr0 2>/dev/null || true)
_green='\033[0;32m'
_yellow='\033[1;33m'
_red='\033[0;31m'
_cyan='\033[0;36m'
_nc='\033[0m'

step()  { echo -e "\n${_cyan}${_bold}── $1${_reset}"; }
ok()    { echo -e "  ${_green}[✓]${_nc} $1"; }
info()  { echo -e "      ${_yellow}$1${_nc}"; }
warn()  { echo -e "  ${_yellow}[!]${_nc} $1"; }
fail()  { echo -e "  ${_red}[✗]${_nc} $1"; exit 1; }
# Prompt goes to STDERR, not stdout — otherwise `x=$(ask "…")` captures the
# prompt text itself into x. That is exactly how gpu_name once became the string
# "Enter your GPU model manually…" on a detached install (found 2026-07-23).
ask()   { local prompt="$1" default="${2:-}"; { printf "      %s" "$prompt"; [ -n "$default" ] && printf " [%s]" "$default"; printf ": "; } >&2; read -r _ans; echo "${_ans:-$default}"; }

# ── Banner ────────────────────────────────────────────────────────────────────
clear
echo ""
echo "  ╔══════════════════════════════════════════════════════════════╗"
echo "  ║     GridShare — AMD ROCm Node Agent Installer               ║"
echo "  ║     Earn INR from your AMD GPU  •  gridshare.in             ║"
echo "  ║     RX 400-7000 · Radeon PRO · Instinct MI100-MI300         ║"
echo "  ║     RX 9000 (RDNA 4) not yet supported                      ║"
echo "  ╚══════════════════════════════════════════════════════════════╝"
echo ""

# ── Root check ────────────────────────────────────────────────────────────────
if [[ $EUID -ne 0 ]]; then
  fail "Please run as root: sudo bash install-amd.sh"
fi

# ── OS check ─────────────────────────────────────────────────────────────────
# ROCm 6.1  → requires Ubuntu 22.04+ (RDNA2/3 GPUs)
# ROCm 5.7  → works on Ubuntu 20.04 and 22.04 (GCN/RDNA1 GPUs, legacy path)
step "Checking OS compatibility"
if [ -f /etc/os-release ]; then
  . /etc/os-release
  OS_NAME="$NAME"
  OS_VER="$VERSION_ID"
else
  OS_NAME="Unknown"
  OS_VER="0"
fi

if [[ "$OS_NAME" != *"Ubuntu"* ]]; then
  fail "AMD ROCm requires Ubuntu. Detected: $OS_NAME. Use Ubuntu 20.04 or 22.04."
fi

OS_CODENAME="jammy"  # Ubuntu 22.04 default
if [[ "$OS_VER" == "20.04" ]]; then
  OS_CODENAME="focal"
  warn "Ubuntu 20.04 detected — will use ROCm 5.7 legacy path (Ubuntu 22.04 recommended for best performance)"
elif [[ "$OS_VER" < "20.04" ]]; then
  fail "Ubuntu $OS_VER is too old. Minimum: Ubuntu 20.04 (ROCm 5.7) or 22.04 (ROCm 6.1)"
fi
ok "OS: Ubuntu $OS_VER ($OS_CODENAME) ✓"

# ── Detect AMD GPU ────────────────────────────────────────────────────────────
step "Detecting AMD GPU"

AMD_GPU_NAME=""
AMD_VRAM_GB=0

# Method 1: lspci. NOTE the class list must include "Processing accelerators" —
# AMD Instinct (MI100/MI200/MI300) reports as an accelerator, NOT as VGA/3D/
# Display, so a real MI300X went completely undetected here (found live
# 2026-07-28 on a 129.212.x box: the card was present as
# "Processing accelerators: [AMD/ATI] Device 74b5" and we saw nothing).
if command -v lspci &>/dev/null; then
  AMD_GPU_NAME=$(lspci | grep -iE "VGA|3D|Display|Processing accelerator" \
                 | grep -iE "AMD|Radeon|RX |Instinct" \
                 | head -1 | sed 's/.*: //' | sed 's/ (rev .*//' || true)
fi

# Method 1b: Instinct cards often expose no marketing name before ROCm is
# installed — lspci yields e.g. "Advanced Micro Devices, Inc. [AMD/ATI] Device
# 74b5". Map the PCI device id to a real model so arch detection can work and
# the provider sees a sane name instead of a hex id.
if echo "$AMD_GPU_NAME" | grep -qiE "device [0-9a-f]{4}"; then
  _did=$(echo "$AMD_GPU_NAME" | grep -oiE "device [0-9a-f]{4}" | tail -1 | awk '{print tolower($2)}')
  case "$_did" in
    74a0|74a1|74a5|74a9|74b5|74b9) AMD_GPU_NAME="AMD Instinct MI300X" ;;
    7408|740c|740f|7410)           AMD_GPU_NAME="AMD Instinct MI250X" ;;
    738c|738e)                     AMD_GPU_NAME="AMD Instinct MI100"  ;;
  esac
fi

# Method 2: /sys/class/drm
if [ -z "$AMD_GPU_NAME" ]; then
  for card in /sys/class/drm/card*/device; do
    vendor=$(cat "$card/vendor" 2>/dev/null || echo "")
    if [ "$vendor" = "0x1002" ]; then  # AMD vendor ID
      AMD_GPU_NAME=$(cat "$card/product" 2>/dev/null || echo "AMD Radeon GPU")
      break
    fi
  done
fi

# Method 3: rocminfo (if already installed)
if [ -z "$AMD_GPU_NAME" ] && command -v rocminfo &>/dev/null; then
  AMD_GPU_NAME=$(rocminfo 2>/dev/null | grep "Marketing Name:" | head -1 | sed 's/.*Marketing Name: *//' || true)
fi

if [ -z "$AMD_GPU_NAME" ]; then
  warn "No AMD GPU detected automatically."
  warn "Supported: RX 6700 XT, RX 6800 XT, RX 6900 XT, RX 7800 XT, RX 7900 XTX"
  # Only prompt when a human is actually at the keyboard (stdin is a TTY). On a
  # detached install (curl | bash, token/fleet mode) there is no one to answer —
  # prompting there is what let the prompt text get captured as gpu_name. Fail
  # CLOSED to an UNKNOWN GPU instead of guessing: register with no name, and let
  # the node agent's GPU-usability probe decide availability once ROCm is up
  # (a truly-dead GPU registers UNAVAILABLE; a working one is re-detected on the
  # agent's first heartbeat). Never a hard fail — that would abort a fleet roll.
  if [ -t 0 ]; then
    AMD_GPU_NAME=$(ask "Enter your GPU model manually (or leave blank to auto-detect later)" "")
  else
    warn "Non-interactive install — leaving GPU model UNKNOWN; the node agent will re-detect and gate on real GPU health."
  fi
fi

if [ -n "$AMD_GPU_NAME" ]; then
  ok "GPU detected: $AMD_GPU_NAME"
else
  warn "GPU model unknown — registering as UNKNOWN; the agent verifies GPU health before earning."
fi

# ── Detect GPU architecture → pick ROCm version ───────────────────────────────
# GCN    (Polaris)  RX 4xx, RX 5xx (3-digit), Vega 56/64, Radeon VII  → ROCm 5.7
# RDNA1  (Navi 10)  RX 5500/5600/5700 (4-digit)                        → ROCm 5.7
# RDNA2  (Navi 2x)  RX 6xxx                                             → ROCm 6.1
# RDNA3  (Navi 3x)  RX 7xxx                                             → ROCm 6.1
GPU_LOWER_ARCH=$(echo "$AMD_GPU_NAME" | tr '[:upper:]' '[:lower:]')

if echo "$GPU_LOWER_ARCH" | grep -qE "radeon vii|radeon-vii"; then
  GPU_ARCH="gcn"      # Radeon VII = Vega 20 GCN
elif echo "$GPU_LOWER_ARCH" | grep -qE "vega.?(5[0-9]|6[0-9]|64|56)|rx vega"; then
  GPU_ARCH="gcn"      # Vega 56 / Vega 64
elif echo "$GPU_LOWER_ARCH" | grep -qE "rx[[:space:]_-]*[45][0-9]{2}([^0-9]|$)"; then
  # RX 470/480/570/580/590 (3-digit). The trailing ([^0-9]|$) alternation is
  # load-bearing: the old pattern demanded a non-digit AFTER the digits, so a
  # name ENDING in the model — "AMD Radeon RX 580", which is exactly how lspci
  # reports it — matched nothing and fell through to the rdna2 default, i.e. the
  # wrong ROCm on one of the most common older AMD cards. Caught 2026-07-28 by
  # test_amd_installer_detection.py on its first run.
  GPU_ARCH="gcn"
elif echo "$GPU_LOWER_ARCH" | grep -qE "rx[[:space:]_-]*5[0-9]{3}|rx5[0-9]{3}"; then
  GPU_ARCH="rdna1"    # RX 5500/5600/5700 XT (4-digit starting with 5)
elif echo "$GPU_LOWER_ARCH" | grep -qE "rx[[:space:]_-]*7[0-9]{3}|rx7[0-9]{3}"; then
  GPU_ARCH="rdna3"    # RX 7000 series
elif echo "$GPU_LOWER_ARCH" | grep -qE "rx[[:space:]_-]*9[0-9]{3}|rx9[0-9]{3}"; then
  GPU_ARCH="rdna4"    # RX 9000 series (Navi 4x — 9070 XT/9070/9060 XT/9060)
elif echo "$GPU_LOWER_ARCH" | grep -qE "rx[[:space:]_-]*6[0-9]{3}|rx6[0-9]{3}"; then
  GPU_ARCH="rdna2"    # RX 6000 series — EXPLICIT, no longer a silent default
elif echo "$GPU_LOWER_ARCH" | grep -qE "instinct|mi[[:space:]_-]?(100|210|250|300|325|355)"; then
  GPU_ARCH="cdna"     # Instinct MI-series (CDNA) — ROCm 6.1 covers MI100→MI300X
elif echo "$GPU_LOWER_ARCH" | grep -qE "w7[0-9]{3}|pro[[:space:]_-]*w7"; then
  GPU_ARCH="rdna3"    # Radeon PRO W7900/W7800 (RDNA3 workstation)
elif echo "$GPU_LOWER_ARCH" | grep -qE "w6[0-9]{3}|pro[[:space:]_-]*w6"; then
  GPU_ARCH="rdna2"    # Radeon PRO W6800 etc (RDNA2 workstation)
else
  GPU_ARCH="unknown"  # nothing matched — do NOT guess (see the guard below)
fi

# ── Unsupported / unrecognised architecture → FAIL LOUDLY ────────────────────
# Until 2026-07-27 anything unmatched silently fell through to the rdna2 default
# and installed ROCm 6.1. An RX 9060 XT (RDNA 4, needs ROCm 6.4+) therefore
# "installed successfully" onto a stack its GPU cannot use — the card was dead
# but the installer said OK, and the provider only found out when jobs failed.
# A wrong install is worse than a refused one: fail here with a real reason.
# ── UNSUPPORTED / UNKNOWN AMD ARCH — INSTALL ANYWAY, BUT NEVER SERVE BUYERS ──
# Policy (Vamsi, 2026-07-28): "don't block install on any card... let them be
# listed but make sure it won't launch for the buyer... once the AMD node is
# there mail me and we start building." Turning a provider away loses them
# permanently AND loses us the only hardware we could develop against. So we
# install, register, and let the agent's GPU-usability probe hold the node OUT
# of the rental pool (fail-closed) until we ship real support. The provider
# keeps their setup; we get a real card to build against.
if [[ "$GPU_ARCH" == "rdna4" ]] || [[ "$GPU_ARCH" == "unknown" ]]; then
  echo ""
  warn "This GPU (${AMD_GPU_NAME:-unknown model}) is NOT fully supported yet."
  warn "Installing anyway so your machine is registered and we can build support"
  warn "for it — but it will NOT be rented to buyers until that work lands, so"
  warn "you will not earn from it in the meantime. We are notified automatically"
  warn "when a card like yours joins, and we'll email you the moment it's live."
  warn "Questions: support@gridshare.in"
  GRIDSHARE_UNSUPPORTED_GPU=1
  # RDNA 4 needs ROCm 6.4+; our 6.1 packages cannot drive it. Install the newest
  # stack we have so the box is otherwise ready, and let the probe gate it.
  [[ "$GPU_ARCH" == "rdna4" ]] && ROCM_VERSION="6.1"
fi

# Force ROCm 5.7 for GCN / RDNA1 — ROCm 6.x dropped support for these
if [[ "$GPU_ARCH" == "rdna4" ]]; then
  # Only reachable with GRIDSHARE_AMD_EXPERIMENTAL=1 (the guard above stops
  # everyone else). RDNA 4 = gfx1200/gfx1201, first supported in ROCm 6.4.
  # UNVERIFIED on real hardware — we have no RDNA 4 card in-house.
  ROCM_VERSION="6.4"
  warn "EXPERIMENTAL: RDNA 4 path selected (ROCm 6.4) — UNVERIFIED on real hardware."
  warn "If the GPU probe fails after install, the node self-delists rather than"
  warn "accepting jobs it can't run. Please report what you see to support@gridshare.in."
  if [[ "$OS_CODENAME" == "focal" ]]; then
    warn "RDNA 4 wants Ubuntu 22.04+; on 20.04 the GPU will stay unavailable."
  fi
elif [[ "$GPU_ARCH" == "gcn" ]] || [[ "$GPU_ARCH" == "rdna1" ]]; then
  ROCM_VERSION="5.7.1"
  warn "Legacy GPU architecture detected ($GPU_ARCH) — using ROCm 5.7 compatibility path"
  info "Supported: RX 470/480/570/580/590, Vega 56/64, Radeon VII, RX 5000 series"
  info "PyTorch, TensorFlow and inference workloads work on ROCm 5.7"
elif [[ "$OS_CODENAME" == "focal" ]]; then
  # Ubuntu 20.04 + RDNA2/3: ROCm 6.1 doesn't support focal; drop to 5.7
  ROCM_VERSION="5.7.1"
  warn "Ubuntu 20.04 + RDNA2 detected — ROCm 6.1 requires Ubuntu 22.04"
  warn "Using ROCm 5.7 fallback. Upgrade to Ubuntu 22.04 for best RDNA2/3 performance."
else
  ok "$GPU_ARCH architecture on Ubuntu 22.04 — using ROCm 6.1 (optimal)"
fi

# ── AMD GPU tier pricing (72% of equivalent NVIDIA tier) ─────────────────────
# AMD ROCm nodes are priced at 72% of equivalent NVIDIA VRAM because ROCm has
# a smaller buyer pool than CUDA. Rates will increase as ROCm adoption grows.
#
# Tier mapping:
#   RX 7900 XTX / XTX (24GB)   → ₹32/hr  (vs RTX 4090 ₹44/hr)
#   RX 7900 GRE / XT (16GB)    → ₹27/hr  (vs RTX 3090 ₹34/hr)
#   RX 7800 XT (16GB)           → ₹20/hr  (vs RTX 3080 ₹24/hr)
#   RX 6900 XT (16GB)           → ₹27/hr  (vs RTX 3090 ₹34/hr)
#   RX 6800 XT (16GB)           → ₹18/hr  (vs RTX 3080 Ti ₹28/hr)
#   RX 6800 (16GB)              → ₹16/hr  (vs RTX 3080 ₹24/hr)
#   RX 6750 XT / 6700 XT (12GB) → ₹14/hr  (vs RTX 3080 ₹24/hr, less VRAM)
#   Other / Unknown AMD         → ₹10/hr  (Starter equivalent)

GPU_LOWER=$(echo "$AMD_GPU_NAME" | tr '[:upper:]' '[:lower:]')
PRICE_PER_HOUR=10
TIER_NAME="AMD Starter"
AMD_VRAM_GB=0

if echo "$GPU_LOWER" | grep -qE "7900 xtx|rx 7900xtx"; then
  PRICE_PER_HOUR=32; TIER_NAME="AMD Ultra (ROCm)"; AMD_VRAM_GB=24
elif echo "$GPU_LOWER" | grep -qE "7900 gre|7900 xt|6900 xt"; then
  PRICE_PER_HOUR=27; TIER_NAME="AMD Pro Max (ROCm)"; AMD_VRAM_GB=16
elif echo "$GPU_LOWER" | grep -qE "7800 xt|6800 xt"; then
  PRICE_PER_HOUR=20; TIER_NAME="AMD Pro (ROCm)"; AMD_VRAM_GB=16
elif echo "$GPU_LOWER" | grep -qE "rx 6800[^x]|6800$"; then
  PRICE_PER_HOUR=16; TIER_NAME="AMD Standard+ (ROCm)"; AMD_VRAM_GB=16
elif echo "$GPU_LOWER" | grep -qE "6750 xt|6700 xt"; then
  PRICE_PER_HOUR=14; TIER_NAME="AMD Standard (ROCm)"; AMD_VRAM_GB=12
elif echo "$GPU_LOWER" | grep -qE "7700 xt|7600 xt|6650 xt|6600 xt"; then
  PRICE_PER_HOUR=12; TIER_NAME="AMD Plus (ROCm 6.1)"; AMD_VRAM_GB=8
# ── GCN / RDNA1 tiers (ROCm 5.7 path) ────────────────────────────────────────
elif echo "$GPU_LOWER" | grep -qE "radeon vii|radeon-vii"; then
  PRICE_PER_HOUR=14; TIER_NAME="AMD Radeon VII (ROCm 5.7)"; AMD_VRAM_GB=16
elif echo "$GPU_LOWER" | grep -qE "vega.?64|rx vega 64|vega64"; then
  PRICE_PER_HOUR=8;  TIER_NAME="AMD Vega 64 (ROCm 5.7)"; AMD_VRAM_GB=8
elif echo "$GPU_LOWER" | grep -qE "vega.?56|rx vega 56|vega56"; then
  PRICE_PER_HOUR=7;  TIER_NAME="AMD Vega 56 (ROCm 5.7)"; AMD_VRAM_GB=8
elif echo "$GPU_LOWER" | grep -qE "rx.?5700.?xt|rx5700xt"; then
  PRICE_PER_HOUR=8;  TIER_NAME="AMD RX 5700 XT (ROCm 5.7)"; AMD_VRAM_GB=8
elif echo "$GPU_LOWER" | grep -qE "rx.?5700[^x ]|rx.?5700$"; then
  PRICE_PER_HOUR=7;  TIER_NAME="AMD RX 5700 (ROCm 5.7)"; AMD_VRAM_GB=8
elif echo "$GPU_LOWER" | grep -qE "rx.?56[0-9]{2}|rx.?55[0-9]{2}"; then
  PRICE_PER_HOUR=5;  TIER_NAME="AMD RDNA1 (ROCm 5.7)"; AMD_VRAM_GB=6
elif echo "$GPU_LOWER" | grep -qE "rx.?5[89][0-9]|rx.?48[0-9]|rx.?47[0-9]"; then
  PRICE_PER_HOUR=5;  TIER_NAME="AMD GCN Polaris (ROCm 5.7)"; AMD_VRAM_GB=8
else
  # Unmatched / unknown AMD card — don't guess a rate (a high-VRAM Instinct
  # would be under-floored) OR a VRAM size. A fabricated 8 GB is exactly what
  # once registered as a MI300X's VRAM (real: 192 GB). Leave VRAM UNKNOWN (0);
  # the agent re-detects the real size and central assigns the authoritative
  # rate on the first heartbeat.
  PRICE_PER_HOUR=""; TIER_NAME="set on registration"; AMD_VRAM_GB=0
fi

echo ""
echo "  ┌─────────────────────────────────────────────────────────────┐"
echo "  │  GPU:    $AMD_GPU_NAME"
echo "  │  Tier:   $TIER_NAME"
if [ -n "$PRICE_PER_HOUR" ]; then
NET_EARNINGS=$(echo "scale=2; $PRICE_PER_HOUR * 0.7" | bc 2>/dev/null || echo "?")
echo "  │  Rate:   ~₹${PRICE_PER_HOUR}/hr (buyers pay; final set on registration)"
echo "  │  Yours:  ~₹${NET_EARNINGS}/hr (70% share, free monthly bank payout)"
else
echo "  │  Rate:   set by GridShare on registration — see your dashboard"
echo "  │  Yours:  70% of every rented hour it is booked (free monthly bank payout)"
fi
echo "  └─────────────────────────────────────────────────────────────┘"
echo ""

# ── Install ROCm 6.1 ──────────────────────────────────────────────────────────
step "Installing AMD ROCm $ROCM_VERSION"

# Add AMD ROCm APT repository (version-specific URL)
if [ ! -f /etc/apt/sources.list.d/rocm.list ]; then
  info "Adding AMD ROCm $ROCM_VERSION repository..."
  apt-get update -qq
  apt-get install -y -qq curl gnupg wget

  # AMD ROCm signing key (same for all versions)
  curl -fsSL https://repo.radeon.com/rocm/rocm.gpg.key | \
    gpg --dearmor -o /usr/share/keyrings/rocm.gpg

  # ROCm repo — version-specific URL, OS-specific codename
  echo "deb [arch=amd64 signed-by=/usr/share/keyrings/rocm.gpg] \
    https://repo.radeon.com/rocm/apt/${ROCM_VERSION} ${OS_CODENAME} main" \
    > /etc/apt/sources.list.d/rocm.list

  # amdgpu driver repo — needed by every arch that may require the DKMS kernel
  # driver. GCN/RDNA1 ship an in-tree amdgpu module, so they are the ONLY ones
  # that can skip it. cdna (Instinct) and rdna4 were missing here, which is why
  # a real MI300X could not get amdgpu-dkms at all: the repo was never added, so
  # `apt-get install amdgpu-dkms` reported "Unable to locate package" and the
  # card stayed permanently unusable (found live 2026-07-28).
  if [[ "$GPU_ARCH" == "rdna2" ]] || [[ "$GPU_ARCH" == "rdna3" ]] \
     || [[ "$GPU_ARCH" == "rdna4" ]] || [[ "$GPU_ARCH" == "cdna" ]]; then
    echo "deb [arch=amd64 signed-by=/usr/share/keyrings/rocm.gpg] \
      https://repo.radeon.com/amdgpu/latest/ubuntu ${OS_CODENAME} main" \
      > /etc/apt/sources.list.d/amdgpu.list
    info "RDNA2/3: amdgpu driver repo added"
  else
    info "GCN/RDNA1: using built-in kernel amdgpu driver (no proprietary driver needed)"
  fi

  # APT PIN — without this ROCm CANNOT install on Ubuntu (found live 2026-07-28
  # on a real MI300X). Ubuntu's own universe repo ships ancient ROCm 5.0.0-1
  # packages (rocminfo, rocm-device-libs, rocm-cmake). apt prefers those over
  # repo.radeon.com's 6.1 builds, so the dependency set is unsatisfiable:
  #   rocm-hip-runtime : Depends: rocminfo (= 1.0.0.60100-82~22.04)
  #                      but 5.0.0-1 is to be installed
  # …and the whole install dies with "held broken packages". Because every
  # apt-get here ends in `|| true`, that failure used to be SILENT: the script
  # reported success while /opt/rocm did not exist and the GPU was unusable.
  # Pinning repo.radeon.com above the distro fixes resolution outright.
  cat > /etc/apt/preferences.d/rocm-pin-600 <<'PIN'
Package: *
Pin: release o=repo.radeon.com
Pin-Priority: 600
PIN
  ok "ROCm $ROCM_VERSION repository added (repo pinned above distro packages)"
fi

info "Installing ROCm packages (this may take 5–10 minutes)..."
apt-get update -qq

# ── apt helpers: never swallow the error that explains the failure ───────────
# `apt-get ... 2>/dev/null || true` cost us two full install runs on an MI300X
# (2026-07-28). apt printed the answer immediately — `E: Unable to locate
# package python3-rocm` — and both halves of that idiom threw it away: the
# redirect binned the message, `|| true` binned the exit code. Worse,
# `apt-get install` is all-or-nothing at the RESOLUTION stage, so ONE bogus
# package name made the whole transaction a no-op. dpkg was never invoked,
# nothing appeared in /var/log/apt/history.log, no ROCm userspace landed, and
# the installer printed a green tick and registered the node into a PAID
# marketplace with a GPU that could not run a single buyer workload.
#
# Rules now: keep stderr, wait for locks instead of racing them, retry only
# genuinely transient failures, and make REQUIRED packages fatal.
GRIDSHARE_APT_LOG=/var/log/gridshare-apt.log
export NEEDRESTART_MODE=a          # don't let needrestart prompt/defer on us

_apt_run() {
  local tries=0 rc=0 out
  while :; do
    tries=$((tries+1))
    out=$(apt-get -o DPkg::Lock::Timeout=300 -o Acquire::Retries=3 "$@" 2>&1) && rc=0 || rc=$?
    printf '%s\n' "$out" >> "$GRIDSHARE_APT_LOG" 2>/dev/null || true
    [ $rc -eq 0 ] && return 0
    # Resolution errors are permanent — fail fast and SHOW them.
    if printf '%s' "$out" | grep -qiE 'Unable to locate package|has no installation candidate|held broken packages|Unable to correct problems'; then
      printf '%s' "$out" | grep -iE '^E:' >&2
      return "$rc"
    fi
    if [ $tries -ge 4 ]; then
      printf '%s' "$out" | grep -iE '^E:' >&2
      return "$rc"
    fi
    sleep $((tries * 15))
  done
}
# Optional packages: warn, keep going.
_apt_try() { _apt_run install -y --no-install-recommends "$@" \
  || warn "optional packages failed: $* (see $GRIDSHARE_APT_LOG)"; }
# Required packages: the GPU cannot work without these.
_apt_need() { _apt_run install -y --no-install-recommends "$@" \
  || fail "REQUIRED ROCm packages failed to install: $*
  The apt errors are above and in $GRIDSHARE_APT_LOG.
  NOT registering this machine as rentable supply."; }
# Catch a bogus package NAME before it silently no-ops the whole transaction.
_apt_assert_exists() {
  local missing=()
  for p in "$@"; do apt-cache show "$p" >/dev/null 2>&1 || missing+=("$p"); done
  [ ${#missing[@]} -eq 0 ] || fail "package(s) not in the configured repos: ${missing[*]}
  This is a bug in the installer's package list, not your machine."
}

if [[ "$ROCM_VERSION" == "5.7.1" ]]; then
  # ── ROCm 5.7 packages — GCN / RDNA1 / Ubuntu 20.04 legacy path ──────────────
  # hipblaslt not in 5.7 → use hipblas
  _apt_assert_exists rocm-hip-sdk rocm-smi-lib
  _apt_need rocm-hip-sdk rocm-smi-lib
  _apt_try  rocm-developer-tools hipblas miopen-hip
else
  # ── ROCm 6.1 packages — RDNA2/3/4 + CDNA path ──────────────────────────────
  # NO python3-rocm: it does not exist in ANY AMD repo (verified live on an
  # MI300X, 2026-07-28 — `apt-cache search python3-rocm` is empty). The python
  # bindings ship INSIDE amd-smi-lib at /opt/rocm/share/amd_smi and are
  # pip-installed into the agent venv below.
  _apt_assert_exists rocm-hip-sdk rocm-smi-lib amd-smi-lib rocminfo
  _apt_need rocm-hip-sdk rocm-smi-lib amd-smi-lib rocminfo
  _apt_try  rocm-developer-tools hipblaslt miopen-hip
fi

# ── amdsmi python bindings — REQUIRED by PyTorch-ROCm ────────────────────────
# torch>=2.4 +rocm does `import amdsmi` inside torch.cuda.device_count() and
# get_device_name(). Without it the GPU still COMPUTES fine (a raw matmul works)
# but every standard call blows up with:
#     NameError: name 'amdsmi' is not defined
# device_count() appears in almost every real ML script (DDP init, device
# selection, HF accelerate), so the node looks healthy and rentable while buyer
# workloads crash on line one. Found live 2026-07-28 on the MI300X: matmul OK,
# device_count FAILED. `amd-smi-lib` (added above) ships the python package
# under /opt/rocm*/share/amd_smi — install it into the agent venv.
_AMDSMI_SRC=$(ls -d /opt/rocm*/share/amd_smi 2>/dev/null | head -1 || true)
if [ -n "$_AMDSMI_SRC" ] && [ -x "$VENV_DIR/bin/pip" ]; then
  if "$VENV_DIR/bin/pip" install -q "$_AMDSMI_SRC" 2>/dev/null; then
    ok "amdsmi python bindings installed (PyTorch device queries will work)"
  else
    warn "Could not install amdsmi from $_AMDSMI_SRC — torch.cuda.device_count()"
    warn "may fail for buyers on this node. Report to support@gridshare.in."
  fi
elif [ -z "$_AMDSMI_SRC" ]; then
  warn "amd-smi python bindings not found under /opt/rocm*/share/amd_smi —"
  warn "PyTorch device queries (device_count/get_device_name) may fail."
fi

# ── Did ROCm USERSPACE actually install? ─────────────────────────────────────
# Every apt-get above ends in `2>/dev/null || true`, which keeps a fleet roll
# alive but also SWALLOWS a total failure: on the 2026-07-28 MI300X the ROCm
# packages never installed at all (no /opt/rocm), yet the installer printed a
# clean success and the provider had no idea. Say it out loud instead. We do NOT
# abort — the node is still useful for CPU work and the agent's GPU-health probe
# already refuses to rent out a card it cannot verify.
ROCM_USERSPACE_OK=0
if [ ! -d /opt/rocm ] && ! ls -d /opt/rocm-* >/dev/null 2>&1; then
  warn "ROCm userspace did NOT install (no /opt/rocm) — the GPU will not be usable."
  warn "This node will stay OUT of the GPU rental pool until ROCm is working."
  warn "Re-run this installer, or contact support@gridshare.in with the log above."
else
  ROCM_USERSPACE_OK=1
  ok "ROCm userspace present: $(ls -d /opt/rocm* 2>/dev/null | head -1)"
fi

# ── Kernel driver (amdgpu) — the piece ROCm userspace CANNOT work without ────
# We install rocm-hip-sdk etc. above, but that is all USERSPACE. On a normal
# desktop Radeon the in-tree `amdgpu` module is already loaded, so nothing more
# is needed. On a bare cloud VM with a passed-through card (e.g. an Instinct
# MI300X) there is NO driver: /dev/kfd never appears and every ROCm call fails,
# while the install still "succeeds" (found live 2026-07-28 — the MI300X node
# came up and was correctly held out of supply as gpu_unusable, but it could
# never have recovered on its own). Install the DKMS driver in that case and be
# explicit that a reboot is required to load it.
if [ ! -e /dev/kfd ] && ! lsmod 2>/dev/null | grep -q '^amdgpu'; then
  warn "No amdgpu kernel driver loaded — installing amdgpu-dkms (kernel module)."
  info "This compiles against your kernel and can take several minutes..."
  # amdgpu-dkms is built WITH RDMA peer-memory support, so the module imports
  # ib_register_peer_memory_client / ib_unregister_peer_memory_client from
  # ib_core. On a stock cloud/VM kernel ib_core is in linux-modules-extra and is
  # NOT loaded, so `modprobe amdgpu` dies with:
  #     amdgpu: Unknown symbol ib_register_peer_memory_client (err -2)
  # …even after a reboot. Found live 2026-07-28 on an MI300X: the driver built
  # fine, the card stayed dark, and nothing in the install said why. Pull the
  # extras and load ib_core FIRST.
  apt-get install -y -qq "linux-modules-extra-$(uname -r)" 2>/dev/null || true
  if apt-get install -y -qq amdgpu-dkms 2>/dev/null; then
    ok "amdgpu-dkms installed"
    modprobe ib_core 2>/dev/null || true
    modprobe amdgpu 2>/dev/null || true
    # Persist the load order so the GPU survives a reboot without hand-holding.
    printf 'ib_core\namdgpu\n' > /etc/modules-load.d/gridshare-amd.conf 2>/dev/null || true
    # The reboot warning and the flag BOTH belong inside the else. Previously
    # `GRIDSHARE_NEEDS_REBOOT=1` sat outside the `if`, so it overwrote the 0 set
    # two lines above and every install demanded a reboot — including the ones
    # where /dev/kfd was already live and the GPU worked immediately (verified
    # on an MI300X 2026-07-28: kfd appeared the moment modprobe ran, and no
    # reboot was ever needed). Crying wolf here trains providers to reboot
    # rented cloud boxes for nothing and buries the times it genuinely matters.
    if [ -e /dev/kfd ]; then
      ok "GPU driver live now (/dev/kfd present) — no reboot needed"
      GRIDSHARE_NEEDS_REBOOT=0
    else
      warn "A REBOOT IS REQUIRED before the GPU can be used: sudo reboot"
      warn "Until then this node stays listed as unavailable (gpu_unusable) — it"
      warn "will never be rented out with a non-working GPU, and re-lists itself"
      warn "automatically once the driver is live."
      GRIDSHARE_NEEDS_REBOOT=1
    fi
  else
    warn "amdgpu-dkms install failed — the GPU will remain unusable."
    warn "The node stays out of the rental pool until the driver works."
  fi
fi

# Add user to video/render groups (required for GPU access)
usermod -aG video,render root 2>/dev/null || true

# Report the TRUTH, not the fact that we reached the end of the section. This
# said `ok "ROCm 6.1 installed"` unconditionally, printing four lines after
# "ROCm userspace did NOT install — the GPU will not be usable" (seen live on
# the 2026-07-28 MI300X). A provider reading the tail of the log saw a green
# tick and reasonably assumed it worked.
if [ "${ROCM_USERSPACE_OK:-0}" = "1" ]; then
  ok "ROCm $ROCM_VERSION installed"
else
  warn "ROCm $ROCM_VERSION step finished, but ROCm userspace is NOT present — GPU unusable (see above)."
fi

# Verify ROCm
if command -v rocminfo &>/dev/null; then
  ROCM_DETECTED=$(rocminfo 2>/dev/null | grep "Agent " | grep -c "gfx" || echo "0")
  if [ "$ROCM_DETECTED" -gt 0 ]; then
    ok "ROCm GPU agents detected: $ROCM_DETECTED"
  else
    warn "ROCm installed but no GPU agents detected — check driver compatibility"
  fi
fi

# ── Docker with ROCm support ──────────────────────────────────────────────────
step "Setting up Docker with ROCm"
if ! command -v docker &>/dev/null; then
  info "Installing Docker..."
  curl -fsSL https://get.docker.com | sh
  systemctl enable docker
  systemctl start docker
fi

# Add ROCm device access to Docker (no special runtime needed for ROCm — uses --device)
ok "Docker ready for ROCm (uses --device=/dev/kfd --device=/dev/dri)"

# ── Python & PyTorch ROCm ────────────────────────────────────────────────────
step "Checking Python 3.10+"
if ! python3 --version 2>/dev/null | grep -qE "3\.(10|11|12)"; then
  apt-get install -y -qq python3.11 python3.11-venv python3-pip
  update-alternatives --install /usr/bin/python3 python3 /usr/bin/python3.11 1
fi
ok "Python: $(python3 --version)"

# ── System deps ───────────────────────────────────────────────────────────────
step "Installing system dependencies"
# python3-venv/pip REQUIRED for the agent venv — generic pkg matches the system python3
# (minimal Ubuntu 24.04 has python3 3.12 but not python3.12-venv → "ensurepip" failure).
apt-get install -y -qq bc curl jq lm-sensors net-tools pciutils fuse3 python3-venv python3-pip
ok "System deps ready"

# ── rclone — required for Network Volume mounting ────────────────────────────
step "Installing rclone (Network Volume support)"
if ! command -v rclone &>/dev/null; then
  info "Downloading rclone..."
  RCLONE_ARCH="amd64"
  case "$(uname -m)" in arm64|aarch64) RCLONE_ARCH="arm64" ;; esac
  curl -fsSL "https://downloads.rclone.org/rclone-current-linux-${RCLONE_ARCH}.zip" \
      -o /tmp/rclone.zip 2>/dev/null && \
  unzip -q /tmp/rclone.zip -d /tmp/rclone-extract 2>/dev/null && \
  cp /tmp/rclone-extract/rclone-*/rclone /usr/local/bin/rclone && \
  chmod +x /usr/local/bin/rclone && \
  rm -rf /tmp/rclone.zip /tmp/rclone-extract && \
  ok "rclone installed: $(rclone --version 2>/dev/null | head -1)" || \
  warn "rclone install failed — Network Volumes won't mount. Install manually."
else
  ok "rclone already installed: $(rclone --version 2>/dev/null | head -1)"
fi

# ── Open DDP/NCCL inter-node port ────────────────────────────────────────────
# Gaming cafe cluster mode: when a buyer requests multiple GPUs across LAN nodes,
# PyTorch DistributedDataParallel uses NCCL for gradient sync.
# NCCL's rendezvous listens on port 29500 (configurable via master_port).
# We open this port from the LAN subnet so rank-0 (master) can accept connections
# from worker nodes without firewall blocks.
# Ref: https://pytorch.org/docs/stable/distributed.html#initialization
step "Opening NCCL inter-node port (29500) for cluster mode"
if command -v ufw &>/dev/null; then
  UFW_STATUS=$(ufw status 2>/dev/null | grep "Status:" | awk '{print $2}')
  if [ "$UFW_STATUS" = "active" ]; then
    ufw allow 29500/tcp comment "GridShare NCCL cluster DDP" 2>/dev/null && \
      ok "ufw: port 29500/tcp opened for NCCL" || \
      warn "ufw rule failed — open port 29500/tcp manually if using cluster mode"
  else
    info "ufw is inactive — no firewall rule needed for NCCL"
  fi
else
  info "ufw not found — skipping NCCL firewall rule (iptables-based systems: open port 29500/tcp manually)"
fi
ok "Cluster mode: NCCL port configured"

# ── Provider Registration ─────────────────────────────────────────────────────
step "Provider Registration"

OWNER_NAME=""
UPI_ID=""
CITY="$GRIDSHARE_CITY"
EMAIL_OPT=""
MACHINE_LABEL="$GRIDSHARE_LABEL"
API_KEY=""
NODE_ID=""
LOCAL_ID=""

if [ -n "$GRIDSHARE_TOKEN" ]; then
  # ── Account-first mode: single tokenized exchange, ZERO prompts ─────────────
  # Name/UPI/city were saved at portal signup; central reads them off the
  # provider record and burns the single-use token. A stale token sends the
  # provider back to the portal — we do NOT fall back to prompting for PII.
  echo ""
  echo "  ── Account-First Install ──────────────────────────────────────────────"
  echo "      Provision token found — no questions asked."
  echo ""

  LOCAL_ID="amd-$(hostname | tr '[:upper:]' '[:lower:]')-$(cat /proc/sys/kernel/random/uuid 2>/dev/null | head -c 8 || date +%s | tail -c 8)"

  CPU_MODEL=$(grep -m1 "model name" /proc/cpuinfo 2>/dev/null | sed 's/.*: //' | xargs || echo "unknown")
  CPU_CORES=$(nproc 2>/dev/null || echo 1)
  RAM_GB=$(free -g 2>/dev/null | awk '/^Mem:/{print $2}' || echo 0)
  GPU_JSON="[{\"name\":\"$AMD_GPU_NAME\",\"vram_total_mb\":$(( ${AMD_VRAM_GB:-0} * 1024 ))}]"
  HW_JSON="{\"cpu_model\":\"$CPU_MODEL\",\"cpu_cores\":$CPU_CORES,\"ram_total_gb\":$RAM_GB,\"gpu_count\":1,\"gpus\":$GPU_JSON,\"gpu_backend\":\"rocm\",\"platform\":\"linux\"}"

  info "Provisioning with GridShare ..."
  # NOTE: -sSL, NOT -fsSL. `-f` makes curl exit non-zero on any HTTP >= 400,
  # which triggers the `|| echo 000` fallback below and REPLACES the real status
  # code with 000 — so every 401/409/410 below was unreachable and a used or
  # expired install token reported as "Could not reach GridShare server", sending
  # the provider off to debug their network. Hit live on 2026-07-28.
  PROV_HTTP=$(curl -sSL -w "\n%{http_code}" -X POST "$CENTRAL_URL/nodes/provision" \
    -H "Content-Type: application/json" \
    -d "{\"token\":\"$GRIDSHARE_TOKEN\",\"local_id\":\"$LOCAL_ID\",\"machine_label\":\"$MACHINE_LABEL\",\"hardware\":$HW_JSON,\"version\":\"$NODE_VERSION\"}" \
    2>/dev/null || echo $'\n000')
  PROV_CODE=$(echo "$PROV_HTTP" | tail -1)
  PROV_RESP=$(echo "$PROV_HTTP" | sed '$d')

  case "$PROV_CODE" in
    200|201) : ;;
    401) fail "That install link is invalid. Copy a fresh one from your dashboard." ;;
    409) fail "This install link was already used. Mint a new one per machine." ;;
    410) fail "This install link expired. Generate a new one from your dashboard." ;;
    000) fail "Could not reach GridShare server. Check your connection and re-run." ;;
    *)   fail "Provisioning failed (HTTP $PROV_CODE). Mint a fresh install link and re-run." ;;
  esac

  NODE_ID=$(echo "$PROV_RESP"    | python3 -c "import sys,json; d=json.load(sys.stdin); print(d.get('node_id') or '')" 2>/dev/null || echo "")
  API_KEY=$(echo "$PROV_RESP"    | python3 -c "import sys,json; d=json.load(sys.stdin); print(d.get('api_key') or '')" 2>/dev/null || echo "")
  OWNER_NAME=$(echo "$PROV_RESP" | python3 -c "import sys,json; d=json.load(sys.stdin); print(d.get('owner_name') or '')" 2>/dev/null || echo "")
  UPI_ID=$(echo "$PROV_RESP"     | python3 -c "import sys,json; d=json.load(sys.stdin); print(d.get('upi_id') or '')" 2>/dev/null || echo "")
  CITY=$(echo "$PROV_RESP"       | python3 -c "import sys,json; d=json.load(sys.stdin); print(d.get('city') or '')" 2>/dev/null || echo "")

  if [ -z "$API_KEY" ] || [ -z "$NODE_ID" ]; then
    fail "Provisioning response was incomplete. Mint a fresh install link and re-run."
  fi

  ok "Node registered: $NODE_ID"
  ok "Installed for ${OWNER_NAME:-your account} · payouts to ${UPI_ID:-UPI on file}"
elif [ -n "$GRIDSHARE_KEY" ]; then
  echo ""
  echo "  ── Fleet Mode ─────────────────────────────────────────────────────────"
  echo "      GRIDSHARE_KEY found — skipping registration."
  API_KEY="$GRIDSHARE_KEY"
  ok "Fleet mode: API key loaded"
else
  echo ""
  echo "      GridShare will pay your earnings to your UPI ID."
  echo ""
  OWNER_NAME=$(ask "Your name" "")
  UPI_ID=$(ask "UPI ID for payouts (GPay/PhonePe/Paytm)" "")
  CITY=$(ask "City" "")
  EMAIL_OPT=$(ask "Email (optional, for payout receipts)" "")

  [ -z "$OWNER_NAME" ] && fail "Name is required"
  [ -z "$UPI_ID" ]     && fail "UPI ID is required"

  echo ""
  echo "  Registering with GridShare..."
  REG_RESPONSE=$(curl -s -X POST "$CENTRAL_URL/nodes/issue-key" \
    -H "Content-Type: application/json" \
    -d "{\"owner_name\":\"$OWNER_NAME\",\"upi_id\":\"$UPI_ID\",\"email\":\"$EMAIL_OPT\",\"city\":\"$CITY\",\"gpu_backend\":\"rocm\"}" \
    2>/dev/null || echo '{"error":"connection failed"}')

  API_KEY=$(echo "$REG_RESPONSE" | python3 -c "import sys,json; d=json.load(sys.stdin); print(d.get('api_key') or '')" 2>/dev/null || true)

  if [ -z "$API_KEY" ]; then
    warn "Could not auto-register."
    API_KEY=$(ask "Paste your API key from gridshare.in/portal" "")
    [ -z "$API_KEY" ] && fail "API key required."
  else
    ok "API key issued"
    echo ""
    echo "  ┌──────────────────────────────────────────────────────────────┐"
    echo "  │  YOUR API KEY — SAVE THIS, IT WON'T BE SHOWN AGAIN:         │"
    echo "  │  $API_KEY"
    echo "  └──────────────────────────────────────────────────────────────┘"
    echo ""
  fi
fi

# ── Install gridshare-node ────────────────────────────────────────────────────
step "Installing GridShare Node Agent"
mkdir -p "$INSTALL_DIR" "$CONFIG_DIR" "$LOG_DIR"
python3 -m venv "$VENV_DIR"
"$VENV_DIR/bin/pip" install -q --upgrade pip

WHEEL_URL="$CENTRAL_URL/dl/gridshare_node-1.3.93-py3-none-any.whl"
if curl -sfI "$WHEEL_URL" &>/dev/null; then
  "$VENV_DIR/bin/pip" install -q "$WHEEL_URL"
else
  "$VENV_DIR/bin/pip" install -q gridshare-node 2>/dev/null || \
    fail "Could not install gridshare-node. Contact support@gridshare.in"
fi
ok "Node agent installed"

# Install PyTorch with ROCm support — version-matched to installed ROCm
if [[ "$ROCM_VERSION" == "5.7.1" ]]; then
  TORCH_ROCM_TAG="rocm5.7"
else
  TORCH_ROCM_TAG="rocm6.0"
fi
info "Installing PyTorch ($TORCH_ROCM_TAG)..."
"$VENV_DIR/bin/pip" install -q \
  torch torchvision torchaudio \
  --index-url "https://download.pytorch.org/whl/${TORCH_ROCM_TAG}" \
  2>/dev/null && ok "PyTorch ${TORCH_ROCM_TAG} installed" || warn "PyTorch install failed — will retry on first run"

# ── Write config ──────────────────────────────────────────────────────────────
step "Writing configuration"

# Token path already minted its local_id (it went into /nodes/provision).
[ -z "$LOCAL_ID" ] && LOCAL_ID="amd-$(hostname | tr '[:upper:]' '[:lower:]')-$(cat /proc/sys/kernel/random/uuid 2>/dev/null | head -c 8 || date +%s | tail -c 8)"

python3 - <<PYEOF
import json, os
cfg = {
    "central_url":              "$CENTRAL_URL",
    "node_id":                  "$NODE_ID" or None,
    "api_key":                  "$API_KEY",
    "owner_name":               "$OWNER_NAME",
    "upi_id":                   "$UPI_ID",
    "city":                     "$CITY",
    "local_id":                 "$LOCAL_ID",
    "machine_label":            "$MACHINE_LABEL",
    "gpu_name":                 "$AMD_GPU_NAME",
    "gpu_backend":              "rocm",
    "gpu_vram_gb":              $AMD_VRAM_GB,
    "price_per_hour_inr":       float($PRICE_PER_HOUR),
    "dashboard_port":           7842,
    "dashboard_open_on_start":  False,
    "accept_job_types":         ["training", "inference", "batch"],
    "rocm_version":             "$ROCM_VERSION",
    "docker_devices":           ["/dev/kfd", "/dev/dri"],
    "max_gpu_temp_c":           90,
    "max_cpu_temp_c":           90,
    "safety_auto_cutoff":       True,
    "schedule_enabled":         False,
}
os.makedirs("$CONFIG_DIR", exist_ok=True)
with open("$CONFIG_DIR/config.json", "w") as f:
    json.dump(cfg, f, indent=2)
print("Config written to $CONFIG_DIR/config.json")
PYEOF
ok "Config written (gpu_backend: rocm)"

# ── systemd service ───────────────────────────────────────────────────────────
# ── HSA_OVERRIDE_GFX_VERSION — ARCH-CONDITIONAL, never blanket ───────────────
# This was hardcoded to 11.0.0 (gfx1100 / RDNA 3) for EVERY AMD card. On an
# MI300X (gfx942 / CDNA 3) that makes HIP load RDNA3 kernels onto CDNA silicon
# and every GPU op dies with "shared object initialization failed" — the agent
# then reports the GPU UNUSABLE and the node earns nothing, while the exact same
# op works fine in a shell without the variable. Proven on real hardware
# 2026-07-28: with the override -> HIP error; without it -> OK.
# The override is a COMPATIBILITY HACK for consumer RDNA cards that ROCm has no
# native kernels for. Datacenter CDNA (Instinct) is natively supported and must
# never be overridden. RDNA3 is already gfx11xx, so it needs nothing either.
HSA_ENV_LINE=""
case "$GPU_ARCH" in
  rdna2) HSA_ENV_LINE="Environment=HSA_OVERRIDE_GFX_VERSION=10.3.0"$'\n' ;;
  rdna4) HSA_ENV_LINE="Environment=HSA_OVERRIDE_GFX_VERSION=11.0.0"$'\n' ;;
  *)     HSA_ENV_LINE="" ;;   # cdna / rdna3 / gcn / rdna1 -> native, no override
esac
[ -n "$HSA_ENV_LINE" ] && info "HSA override for $GPU_ARCH: ${HSA_ENV_LINE%$'\n'}" \
  || info "No HSA override needed for $GPU_ARCH (natively supported)"

step "Installing systemd service"

cat > /etc/systemd/system/${SERVICE_NAME}.service <<EOF
[Unit]
Description=GridShare Node Agent (AMD ROCm)
After=network-online.target docker.service
Wants=network-online.target

[Service]
Type=simple
User=root
WorkingDirectory=$INSTALL_DIR
ExecStart=$VENV_DIR/bin/python -m gridshare_node.main --config $CONFIG_DIR/config.json
# 🔴 MUST be 'always', never 'on-failure' — see the long note in install-linux.sh.
# The OTA updater SIGTERMs itself to hand over to the new code; that is a CLEAN
# exit, so 'on-failure' leaves the agent dead after a successful update.
# Diagnosed 2026-07-29 on a live node: update installed, service inactive.
Restart=always
RestartSec=10
StandardOutput=append:$LOG_DIR/node.log
StandardError=append:$LOG_DIR/node.log
Environment=PYTHONUNBUFFERED=1
Environment=GRIDSHARE_CENTRAL_URL=$CENTRAL_URL
# Point the agent at the dir where this installer writes the API key; without it
# the agent loads an empty ~/.gridshare config and never registers (fleet key
# silently ignored). The --config flag is not honoured by Config(); this is.
Environment=GRIDSHARE_CONFIG_DIR=$CONFIG_DIR
${HSA_ENV_LINE}Environment=ROCR_VISIBLE_DEVICES=0

[Install]
WantedBy=multi-user.target
EOF

systemctl daemon-reload
systemctl enable "$SERVICE_NAME"
systemctl start  "$SERVICE_NAME"
sleep 3

if systemctl is-active --quiet "$SERVICE_NAME"; then
  ok "Service is running"
else
  warn "Service failed to start — check logs: journalctl -u gridshare-node -n 30"
fi

# ── Done ──────────────────────────────────────────────────────────────────────
echo ""
echo "  ╔══════════════════════════════════════════════════════════════╗"
echo "  ║  ✅  GridShare AMD ROCm Node Agent installed!               ║"
echo "  ║                                                              ║"
echo "  ║  GPU:       $AMD_GPU_NAME"
echo "  ║  Backend:   AMD ROCm $ROCM_VERSION"
# Every value below MUST use a :- default. On a token / bootstrap-key install the
# price + tier + payout id are assigned by CENTRAL at registration, so these vars
# are never set locally — and with `set -u` that made the FINAL SUMMARY abort with
# "NET_EARNINGS: unbound variable" AFTER a completely successful install (found
# live 2026-07-28 on the MI300X). The agent was running fine, but the provider
# saw an error as the last thing on screen and would reasonably think it failed.
echo "  ║  Tier:      ${TIER_NAME:-set by GridShare on registration}"
if [ -n "${PRICE_PER_HOUR:-}" ] && [ -n "${NET_EARNINGS:-}" ]; then
  echo "  ║  Rate:      ₹${PRICE_PER_HOUR}/hr (you earn ₹${NET_EARNINGS}/hr)"
else
  echo "  ║  Rate:      set by GridShare on registration — you keep 70%"
fi
echo "  ║  Payouts:   Free monthly bank payout (weekly/daily optional)"
echo "  ║                                                              ║"
[[ -n "${API_KEY:-}" ]] && echo "  ║  API Key:  $API_KEY"
echo "  ║  Cloud portal: https://gridshare.in/provider (log in with the API Key)"
echo "  ║  Local dashboard: http://localhost:7842"
echo "  ║                                                              ║"
echo "  ║  Check status:  systemctl status gridshare-node             ║"
echo "  ║  View logs:     journalctl -u gridshare-node -f             ║"
echo "  ║                                                              ║"
echo "  ║  Note: ROCm nodes are priced at ~72% of NVIDIA equivalent.  ║"
echo "  ║  Rates increase as AMD buyer pool grows.                     ║"
echo "  ╚══════════════════════════════════════════════════════════════╝"
echo ""
echo "  💡 IMPORTANT: A system reboot is recommended after ROCm install"
echo "     to ensure GPU kernel modules load correctly."
echo "     Run: sudo reboot"
echo ""
