#!/usr/bin/env bash
set -euo pipefail

# Reoclo Runner Installer
# Usage:     curl -sSL https://get.reoclo.dev | bash -s -- --token <TOKEN>
# Custom GW: curl -sSL https://get.reoclo.dev | bash -s -- --token <TOKEN> --gateway <URL>
# Uninstall: curl -sSL https://get.reoclo.dev | bash -s -- --uninstall
# Diagnose:  curl -sSL https://get.reoclo.dev | bash -s -- --check
# Supports: Linux (systemd) and macOS (launchd)

VERSION=""
INSTALL_DIR="/usr/local/bin"
# On Linux, the runner binary lives in a directory owned by RUNNER_USER so the
# runner can rename it during self-update. INSTALL_DIR holds a symlink that
# keeps the binary discoverable on the default PATH. On macOS the runner runs
# as root, so the binary stays directly in INSTALL_DIR.
RUNNER_HOME_DIR="/opt/reoclo"
RUNNER_BIN_DIR="${RUNNER_HOME_DIR}/bin"
CONFIG_DIR="/etc/reoclo"
BINARY_NAME="reoclo-runner"
SERVICE_NAME="reoclo-runner"
CDN_URL="${CDN_URL:-https://cdn.reoclo.dev}"

# macOS launchd
PLIST_LABEL="com.reoclo.runner"
PLIST_PATH="/Library/LaunchDaemons/${PLIST_LABEL}.plist"
LOG_DIR="/var/log"

# Colors
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[0;33m'
BLUE='\033[0;34m'
NC='\033[0m'

info()  { echo -e "${BLUE}[INFO]${NC}  $*"; }
ok()    { echo -e "${GREEN}[OK]${NC}    $*"; }
warn()  { echo -e "${YELLOW}[WARN]${NC}  $*"; }
error() { echo -e "${RED}[ERROR]${NC} $*" >&2; }
die()   { error "$*"; exit 1; }

# --- Parse arguments ---
TOKEN=""
GATEWAY_URL="wss://direct.reoclo.dev/ws/runner"
UNINSTALL=false
CHECK_ONLY=false
# Slice 1 (2026-05-23) — lite runner & pod-mode flags.
# PROFILE / MODE are filled by auto-detection below if left blank.
PROFILE=""
MODE=""
HOSTNAME_OVERRIDE=""
ASSUME_YES=false
ALLOW_NO_DOCKER=false

while [[ $# -gt 0 ]]; do
  case "$1" in
    --token)     TOKEN="$2"; shift 2 ;;
    --gateway)   GATEWAY_URL="$2"; shift 2 ;;
    --version)   VERSION="$2"; shift 2 ;;
    --uninstall) UNINSTALL=true; shift ;;
    --check)     CHECK_ONLY=true; shift ;;
    --profile)   PROFILE="$2"; shift 2 ;;
    --mode)      MODE="$2"; shift 2 ;;
    --hostname)  HOSTNAME_OVERRIDE="$2"; shift 2 ;;
    --yes|-y)    ASSUME_YES=true; shift ;;
    --allow-no-docker) ALLOW_NO_DOCKER=true; PROFILE="${PROFILE:-lite}"; shift ;;
    --help|-h)   cat <<HELP
Usage: $0 [--token <TOKEN>] [--gateway <URL>] [options]

Standard options:
  --token <T>          Registration token (required unless --check / --uninstall)
  --gateway <URL>      WebSocket gateway URL (default: wss://direct.reoclo.dev/ws/runner)
  --version <V>        Pin runner binary version (default: latest)
  --uninstall          Remove the runner and exit
  --check              Print detected settings without installing

Lite-runner / pod options (Slice 1):
  --profile {full,lite}  Runner profile (auto-detected: lite when no Docker, else full)
  --mode {system,user,foreground}  Service mode (auto-detected from environment)
  --hostname <NAME>    Override registered hostname (default: \$RUNPOD_POD_ID or system hostname)
  --yes, -y            Skip the confirmation prompt (non-interactive install)
  --allow-no-docker    Shortcut for --profile=lite; permits install without Docker
HELP
                 exit 0 ;;
    *) die "Unknown argument: $1" ;;
  esac
done

# Validate enum-like flags up front so a typo doesn't surface 200 lines later.
if [[ -n "$PROFILE" && "$PROFILE" != "full" && "$PROFILE" != "lite" ]]; then
  die "Invalid --profile value: $PROFILE (expected 'full' or 'lite')"
fi
if [[ -n "$MODE" && "$MODE" != "system" && "$MODE" != "user" && "$MODE" != "foreground" ]]; then
  die "Invalid --mode value: $MODE (expected 'system', 'user', or 'foreground')"
fi

# --- Uninstall mode ---
if [[ "$UNINSTALL" == true ]]; then
  info "Uninstalling Reoclo Runner..."

  # Detect OS for uninstall (before main OS detection)
  _UNINSTALL_OS="$(uname -s)"
  _SUDO=""
  if [[ "$(id -u)" -ne 0 ]]; then
    if command -v sudo >/dev/null 2>&1; then
      _SUDO="sudo"
    else
      die "Root privileges required and sudo is not installed. Re-run as root."
    fi
  fi

  if [[ "$_UNINSTALL_OS" == "Linux" ]]; then
    if $_SUDO systemctl is-active --quiet "$SERVICE_NAME" 2>/dev/null; then
      info "Stopping service..."
      $_SUDO systemctl stop "$SERVICE_NAME"
    fi
    if $_SUDO systemctl is-enabled --quiet "$SERVICE_NAME" 2>/dev/null; then
      $_SUDO systemctl disable "$SERVICE_NAME" 2>/dev/null || true
    fi
    if [[ -f "/etc/systemd/system/${SERVICE_NAME}.service" ]]; then
      $_SUDO rm -f "/etc/systemd/system/${SERVICE_NAME}.service"
      $_SUDO systemctl daemon-reload
    fi
  elif [[ "$_UNINSTALL_OS" == "Darwin" ]]; then
    if $_SUDO launchctl list "$PLIST_LABEL" >/dev/null 2>&1; then
      info "Stopping service..."
      $_SUDO launchctl bootout "system/${PLIST_LABEL}" 2>/dev/null || \
        $_SUDO launchctl unload "$PLIST_PATH" 2>/dev/null || true
    fi
    $_SUDO rm -f "$PLIST_PATH"
    $_SUDO rm -f "${LOG_DIR}/reoclo-runner.log"
  else
    warn "Unknown OS: $_UNINSTALL_OS. Removing binary and config only."
  fi

  $_SUDO rm -f "${INSTALL_DIR}/${BINARY_NAME}"
  $_SUDO rm -f "${INSTALL_DIR}/${BINARY_NAME}.old"
  $_SUDO rm -rf "$RUNNER_HOME_DIR"
  $_SUDO rm -rf "$CONFIG_DIR"

  ok "Reoclo Runner uninstalled successfully"
  exit 0
fi

if [[ "$CHECK_ONLY" != true ]]; then
  [[ -z "$TOKEN" ]] && die "Missing required --token argument"
fi

# --- Select a downloader (curl preferred) --------------------------------
# Minimal images — notably bare NVIDIA CUDA bases used on GPU hosts — can ship
# NONE of curl/wget/python3. We try each in turn, and as a last resort install
# curl with the OS package manager. http_get_* wrap the chosen tool for the
# two access patterns we need: stream-to-stdout and download-to-file.

# Install curl via whatever package manager is present. Last-resort only, when
# no downloader (curl/wget/python3) exists — e.g. a bare CUDA image. Uses sudo
# when not already root. Returns 0 if curl is available afterwards.
_install_curl() {
  local pm_sudo=""
  if [[ "$(id -u)" != "0" ]] && command -v sudo >/dev/null 2>&1; then
    pm_sudo="sudo"
  fi
  info "No downloader (curl/wget/python3) found — installing curl via the system package manager..."
  if command -v apt-get >/dev/null 2>&1; then
    $pm_sudo apt-get update -qq && $pm_sudo apt-get install -y -qq curl
  elif command -v apk >/dev/null 2>&1; then
    $pm_sudo apk add --no-cache curl
  elif command -v dnf >/dev/null 2>&1; then
    $pm_sudo dnf install -y -q curl
  elif command -v yum >/dev/null 2>&1; then
    $pm_sudo yum install -y -q curl
  else
    return 1
  fi
  command -v curl >/dev/null 2>&1
}

# Detect an available downloader (curl > wget > python3). "none" if absent —
# resolved later (real installs only) by _install_curl.
if command -v curl >/dev/null 2>&1; then
  DOWNLOADER=curl
elif command -v wget >/dev/null 2>&1; then
  DOWNLOADER=wget
elif command -v python3 >/dev/null 2>&1; then
  DOWNLOADER=python3
else
  DOWNLOADER=none
fi

http_get_stdout() {
  case "$DOWNLOADER" in
    curl)    curl -fsSL "$1" ;;
    wget)    wget -qO- "$1" ;;
    python3) python3 -c 'import sys,urllib.request as u; r=u.Request(sys.argv[1], headers={"User-Agent": "curl/8.7.1"}); sys.stdout.buffer.write(u.urlopen(r).read())' "$1" ;;
    *)       return 1 ;;
  esac
}

http_get_file() {
  case "$DOWNLOADER" in
    curl)    curl -fsSL -o "$2" "$1" ;;
    wget)    wget -qO "$2" "$1" ;;
    python3) python3 -c 'import sys,urllib.request as u; r=u.Request(sys.argv[1], headers={"User-Agent": "curl/8.7.1"}); open(sys.argv[2],"wb").write(u.urlopen(r).read())' "$1" "$2" ;;
    *)       return 1 ;;
  esac
}

# On a real install, ensure we actually have a downloader — install curl if the
# image shipped none. Skipped in --check so the diagnostic never mutates the
# system; --check just reports DOWNLOADER=none.
if [[ "$CHECK_ONLY" != true ]] && [[ "$DOWNLOADER" == none ]]; then
  if _install_curl; then
    DOWNLOADER=curl
  else
    die "need curl, wget, python3, or a supported package manager (apt-get/apk/dnf/yum) to download the runner"
  fi
fi

# --- Resolve version (fetch latest from CDN if not pinned) ---
if [[ "$CHECK_ONLY" != true ]] && [[ -z "$VERSION" ]]; then
  VERSION=$(http_get_stdout "${CDN_URL}/releases/runner/latest-version.txt" 2>/dev/null | tr -d '[:space:]')
  if [[ -z "$VERSION" ]]; then
    VERSION="latest"
  fi
fi

# --- Detect platform (early, needed for prerequisite checks) ---
OS="$(uname -s | tr '[:upper:]' '[:lower:]')"
ARCH="$(uname -m)"

case "$OS" in
  linux)  ;;
  darwin) ;;
  *) die "Unsupported OS: $OS (only Linux and macOS are supported)" ;;
esac

case "$ARCH" in
  x86_64|amd64) ARCH="x64" ;;
  aarch64|arm64) ARCH="arm64" ;;
  *) die "Unsupported architecture: $ARCH (only x64 and arm64 are supported)" ;;
esac

# --- Sudo check ---
# Probe non-interactive sudo first so passwordless setups (EC2 cloud-init,
# CI runners, anything with NOPASSWD: ALL) never see a password prompt.
# `sudo -v` is intentionally avoided — it forces credential re-validation
# even when individual commands are NOPASSWD, which surfaces a prompt on
# Ubuntu/Debian even though every $SUDO call would otherwise succeed.
SUDO=""
if [[ "$(id -u)" -ne 0 ]]; then
  if ! command -v sudo >/dev/null 2>&1; then
    if [[ "$CHECK_ONLY" == true ]]; then
      warn "sudo not installed — check will run with limited privileges"
    else
      die "Root privileges required and sudo is not installed. Re-run as root."
    fi
  elif sudo -n true 2>/dev/null; then
    SUDO="sudo -n"
  elif [[ "$CHECK_ONLY" == true ]]; then
    # No passwordless sudo; check mode runs with limited privileges rather than prompt.
    :
  elif [[ -r /dev/tty ]]; then
    warn "Root privileges required. Requesting sudo (you'll be prompted for a password)..."
    if ! sudo -v </dev/tty; then
      die "Failed to obtain sudo privileges"
    fi
    SUDO="sudo"
    # Keep sudo credentials warm for the rest of the install. The loop exits
    # the moment credentials lapse or the parent dies — no silent spinning.
    ( while sudo -n true 2>/dev/null; do sleep 50; kill -0 "$$" 2>/dev/null || exit; done ) &
  else
    die "Root privileges required but sudo would need a password and no TTY is available. Re-run as root, configure NOPASSWD for this user, or run from an interactive shell."
  fi
fi

# --- Detect environment (Slice 1 — for lite/pod auto-selection) ---
# These signals drive auto-selection of PROFILE and MODE when the user
# didn't pass explicit flags. The detection is non-destructive — no files
# are touched until the user (or --yes) confirms the resulting plan.
DOCKER_PRESENT=false
if command -v docker >/dev/null 2>&1; then
  DOCKER_PRESENT=true
fi

PODMAN_PRESENT=false
PODMAN_ROOTLESS=false
if command -v podman >/dev/null 2>&1; then
  PODMAN_PRESENT=true
  _podman_rootless=$(podman info --format '{{.Host.Security.Rootless}}' 2>/dev/null || true)
  [[ "$_podman_rootless" == "true" ]] && PODMAN_ROOTLESS=true
fi

IS_POD=false
POD_PROVIDER="none"
POD_ID=""
if [[ -n "${RUNPOD_POD_ID:-}" ]]; then
  IS_POD=true; POD_PROVIDER="runpod"; POD_ID="$RUNPOD_POD_ID"
elif [[ -n "${KUBERNETES_SERVICE_HOST:-}" ]]; then
  IS_POD=true; POD_PROVIDER="kubernetes"; POD_ID="${HOSTNAME:-}"
elif [[ -f /.dockerenv ]] || [[ -n "${container:-}" ]]; then
  IS_POD=true; POD_PROVIDER="docker"; POD_ID="${HOSTNAME:-}"
fi

INIT_KIND="none"
if [[ "$OS" == "darwin" ]]; then
  INIT_KIND="launchd"
elif [[ -d /run/systemd/system ]] && command -v systemctl >/dev/null 2>&1; then
  INIT_KIND="systemd"
fi

GPU_PRESENT=false
GPU_INFO=""
if command -v nvidia-smi >/dev/null 2>&1; then
  GPU_PRESENT=true
  GPU_INFO=$(nvidia-smi --query-gpu=name --format=csv,noheader 2>/dev/null | head -n 1 || true)
fi

IS_ROOT=false
[[ "$(id -u)" -eq 0 ]] && IS_ROOT=true

# --- Auto-select PROFILE and MODE when not explicitly set ---
if [[ -z "$PROFILE" ]]; then
  if [[ "$DOCKER_PRESENT" == true ]]; then
    PROFILE="full"
  else
    PROFILE="lite"
  fi
fi

if [[ -z "$MODE" ]]; then
  if [[ "$IS_POD" == true ]]; then
    MODE="foreground"
  elif [[ "$INIT_KIND" == "systemd" && "$IS_ROOT" == true ]]; then
    MODE="system"
  elif [[ "$OS" == "darwin" && "$IS_ROOT" == true ]]; then
    MODE="system"
  elif systemctl --user --version >/dev/null 2>&1; then
    MODE="user"
  else
    MODE="foreground"
  fi
fi

# --- Detection summary + confirmation ---
if [[ "$CHECK_ONLY" != true ]]; then
  cat << SUMMARY
Reoclo Runner Installer
─────────────────────────────────────
Detecting environment...
  OS:           $OS ($ARCH)
  Init:         $INIT_KIND
  Docker:       $([[ "$DOCKER_PRESENT" == true ]] && echo "present" || echo "not found")
  Podman:       $([[ "$PODMAN_PRESENT" == true ]] && { [[ "$PODMAN_ROOTLESS" == true ]] && echo "present (rootless)" || echo "present (rootful)"; } || echo "not found")
  Container:    $([[ "$IS_POD" == true ]] && echo "yes ($POD_PROVIDER, id=$POD_ID)" || echo "no")
  GPU:          $([[ "$GPU_PRESENT" == true ]] && echo "$GPU_INFO" || echo "none")
  Root:         $IS_ROOT

Selected profile: $PROFILE  ($([[ "$PROFILE" == "lite" ]] && echo "stats + shell, no deploys" || echo "deploys, stats, shell"))
Selected mode:    $MODE
SUMMARY

  if [[ "$ASSUME_YES" != true ]]; then
    if [[ -r /dev/tty ]]; then
      read -r -p "Continue? [Y/n] " _resp </dev/tty || _resp="y"
      case "${_resp:-y}" in
        n|N|no|NO) die "Aborted by user. Re-run with --yes or override with --profile/--mode flags." ;;
      esac
    else
      info "No TTY — proceeding (use --yes to suppress this notice)."
    fi
  fi
fi

# --- Check prerequisites ---
info "Checking prerequisites..."

# Downloader availability (curl or wget) was validated earlier.
# Slice 1: Docker is only required for full-profile runners. Lite runners
# do stats + shell only, which works without Docker.
if [[ "$DOCKER_PRESENT" != true ]]; then
  if [[ "$PROFILE" == "full" ]] && [[ "$ALLOW_NO_DOCKER" != true ]]; then
    die "Docker is required for the 'full' profile but not installed. Re-run with --profile=lite (or --allow-no-docker) to install a monitoring-only runner."
  fi
  warn "Docker not detected — installing lite-profile runner (stats + shell only, no deploys)."
fi

if [[ "$OS" == "linux" && "$MODE" == "system" ]]; then
  command -v systemctl >/dev/null 2>&1 || die "systemd is required for --mode=system (systemctl not found). Re-run with --mode=foreground."
elif [[ "$OS" == "darwin" && "$MODE" == "system" ]]; then
  command -v launchctl >/dev/null 2>&1 || die "launchctl is required for --mode=system but not found."
fi

# --- Detect Docker mode (rootless vs rootful) ---
# Rootless Docker runs as a specific non-root user, so `sudo docker info`
# fails (sudo strips DOCKER_HOST from the env and root has no socket). We
# test docker access as the invoking user first; if it works and reports
# the rootless security option, the runner must be installed as THAT user
# instead of the dedicated `reoclo` system user.
ROOTLESS_DOCKER=false
ROOTLESS_USER=""
ROOTLESS_UID=""
ROOTLESS_DOCKER_HOST=""

if [[ "$OS" == "linux" ]]; then
  if [[ "$(id -u)" -ne 0 ]]; then
    # Running as non-root — probe docker in the current shell context
    if docker info >/dev/null 2>&1 && \
       docker info --format '{{.SecurityOptions}}' 2>/dev/null | grep -qi rootless; then
      ROOTLESS_DOCKER=true
      ROOTLESS_USER="$(id -un)"
      ROOTLESS_UID="$(id -u)"
      ROOTLESS_DOCKER_HOST="${DOCKER_HOST:-unix:///run/user/${ROOTLESS_UID}/docker.sock}"
    fi
  elif [[ -n "${SUDO_USER:-}" ]] && [[ "$SUDO_USER" != "root" ]]; then
    # Running as root via sudo — probe rootless for the original user
    if sudo -u "$SUDO_USER" -H bash -lc 'docker info' >/dev/null 2>&1 && \
       sudo -u "$SUDO_USER" -H bash -lc 'docker info --format "{{.SecurityOptions}}"' 2>/dev/null | grep -qi rootless; then
      ROOTLESS_DOCKER=true
      ROOTLESS_USER="$SUDO_USER"
      ROOTLESS_UID="$(id -u "$SUDO_USER")"
      ROOTLESS_DOCKER_HOST="$(sudo -u "$SUDO_USER" -H bash -lc 'echo "${DOCKER_HOST:-}"' 2>/dev/null || true)"
      [[ -z "$ROOTLESS_DOCKER_HOST" ]] && \
        ROOTLESS_DOCKER_HOST="unix:///run/user/${ROOTLESS_UID}/docker.sock"
    fi
  fi
fi

# Verify Docker is running
DOCKER_RUNNING=false
if [[ "$ROOTLESS_DOCKER" == true ]]; then
  info "Detected rootless Docker (user: ${ROOTLESS_USER}, socket: ${ROOTLESS_DOCKER_HOST})"
  DOCKER_RUNNING=true
elif [[ -n "$SUDO" ]] && $SUDO docker info >/dev/null 2>&1; then
  DOCKER_RUNNING=true
elif docker info >/dev/null 2>&1; then
  # User has direct access (rootful docker + user in docker group)
  DOCKER_RUNNING=true
fi

if [[ "$DOCKER_RUNNING" != true ]] && [[ "$CHECK_ONLY" != true ]] && [[ "$PROFILE" == "full" ]]; then
  die "Docker is installed but not running"
fi

ok "Prerequisites satisfied"

# --- Determine runner user ---
# Rootful mode: create a dedicated `reoclo` system user and add it to the
# docker group so it can talk to the rootful Docker daemon.
# Rootless mode: reuse the rootless-docker user (they already own the socket).
# Foreground mode (pods): use the current user — pods are usually single-user
# and creating a system user fights the container orchestrator.
RUNNER_USER="reoclo"
RUNNER_GROUP="reoclo"
if [[ "$MODE" == "foreground" ]]; then
  RUNNER_USER="$(id -un)"
  RUNNER_GROUP="$(id -gn 2>/dev/null || echo "$RUNNER_USER")"
elif [[ "$OS" == "linux" ]] && [[ "$ROOTLESS_DOCKER" == true ]]; then
  RUNNER_USER="$ROOTLESS_USER"
  RUNNER_GROUP="$(id -gn "$ROOTLESS_USER" 2>/dev/null || echo "$ROOTLESS_USER")"
fi

# --- Check mode: print detection results and exit ---
# This path is used by the e2e tests (and by end users diagnosing install
# issues) to inspect what mode the installer would use without touching the
# system. It exits before any files are written or services installed.
if [[ "$CHECK_ONLY" == true ]]; then
  cat << CHECK
OS=$OS
ARCH=$ARCH
DOCKER_RUNNING=$DOCKER_RUNNING
DOCKER_PRESENT=$DOCKER_PRESENT
ROOTLESS_DOCKER=$ROOTLESS_DOCKER
ROOTLESS_USER=$ROOTLESS_USER
ROOTLESS_UID=$ROOTLESS_UID
ROOTLESS_DOCKER_HOST=$ROOTLESS_DOCKER_HOST
RUNNER_USER=$RUNNER_USER
RUNNER_GROUP=$RUNNER_GROUP
GATEWAY_URL=$GATEWAY_URL
PROFILE=$PROFILE
MODE=$MODE
INIT_KIND=$INIT_KIND
IS_POD=$IS_POD
POD_PROVIDER=$POD_PROVIDER
POD_ID=$POD_ID
GPU_PRESENT=$GPU_PRESENT
GPU_INFO=$GPU_INFO
HOSTNAME_OVERRIDE=$HOSTNAME_OVERRIDE
DOWNLOADER=$DOWNLOADER
CHECK
  ok "Check complete"
  exit 0
fi

# --- Create reoclo system user and install sudoers rules (Linux only) ---
# Skipped in foreground mode (pods are single-user; creating a system user
# fights the container orchestrator and there's nothing to chown to anyway).
if [[ "$OS" == "linux" && "$MODE" != "foreground" ]]; then
  if [[ "$ROOTLESS_DOCKER" == true ]]; then
    info "Runner will run as user: ${RUNNER_USER} (rootless Docker)"
    # Enable lingering so the user session (and rootless Docker) stays alive
    # across reboots and logouts — required for the runner to start on boot.
    if command -v loginctl >/dev/null 2>&1; then
      $SUDO loginctl enable-linger "$RUNNER_USER" 2>/dev/null || \
        warn "Failed to enable linger for ${RUNNER_USER} — runner may not start on boot"
    fi
  else
    if ! id -u reoclo >/dev/null 2>&1; then
      info "Creating reoclo system user..."
      $SUDO useradd --system --shell /usr/sbin/nologin --home-dir /etc/reoclo --create-home reoclo
      ok "Created reoclo user"
    else
      info "reoclo user already exists"
    fi

    # Add to docker group if it exists
    if getent group docker >/dev/null 2>&1; then
      $SUDO usermod -aG docker reoclo
      ok "Added reoclo to docker group"
    else
      warn "Docker group not found — runner won't have Docker access"
    fi
  fi

  # Install sudoers whitelist for the runner user
  info "Installing sudoers rules for ${RUNNER_USER}..."
  $SUDO tee /etc/sudoers.d/reoclo-runner > /dev/null << SUDOERS
# Reoclo Runner — limited privilege escalation
${RUNNER_USER} ALL=(ALL) NOPASSWD: /usr/bin/systemctl restart *, /usr/bin/systemctl stop *, /usr/bin/systemctl start *
${RUNNER_USER} ALL=(ALL) NOPASSWD: /usr/sbin/reboot, /usr/sbin/shutdown
${RUNNER_USER} ALL=(ALL) NOPASSWD: /usr/bin/ufw *
${RUNNER_USER} ALL=(ALL) NOPASSWD: /usr/sbin/nft *
${RUNNER_USER} ALL=(ALL) NOPASSWD: /usr/sbin/iptables *
SUDOERS
  $SUDO chmod 0440 /etc/sudoers.d/reoclo-runner
  ok "Sudoers rules installed"

  # --- Provision shared deploy folder ---
  # External-deploy workflows (docker compose up via reoclo/run) need a folder
  # that both the runner and human admins can write to without re-chowning
  # after every edit. /opt is root-owned on a stock Ubuntu, so the natural
  # `sudo mkdir /opt/<app>` creates root-owned folders that the rootless or
  # dedicated runner user can't write to. We provision /srv/reoclo up-front
  # with the right ownership so per-app folders need no sudo at all.
  #
  # Two layouts, depending on Docker mode:
  #
  # - Rootful: runner is a dedicated `reoclo` user, operator is a human admin.
  #   They are different users, so we use a shared `reoclo-deploy` group +
  #   setgid (2775) — any subdir/file created under /srv/reoclo inherits the
  #   group automatically, eliminating chown-after-edit friction.
  #
  # - Rootless: runner IS the operator's user (e.g. `ubuntu`). No shared
  #   group needed. /srv/reoclo is just chowned to that user with mode 0755,
  #   so `mkdir /srv/reoclo/<app>` works without sudo and the runner inherits
  #   write access by virtue of being the same Unix user.
  if [[ "$ROOTLESS_DOCKER" == true ]]; then
    info "Provisioning deploy folder /srv/reoclo (rootless — owner ${RUNNER_USER})..."
    $SUDO mkdir -p /srv/reoclo
    $SUDO chown "${RUNNER_USER}:${RUNNER_GROUP}" /srv/reoclo
    $SUDO chmod 0755 /srv/reoclo
    ok "Deploy folder ready at /srv/reoclo (owner ${RUNNER_USER})"
  else
    info "Provisioning shared deploy folder (/srv/reoclo, group reoclo-deploy)..."
    $SUDO groupadd -f reoclo-deploy
    $SUDO usermod -aG reoclo-deploy "$RUNNER_USER"
    $SUDO mkdir -p /srv/reoclo
    $SUDO chown root:reoclo-deploy /srv/reoclo
    # 2 = setgid (new files inherit the group); 775 = rwx for owner+group, rx for others.
    $SUDO chmod 2775 /srv/reoclo
    ok "Deploy folder ready at /srv/reoclo"
  fi
fi

info "Platform: ${OS}-${ARCH}"

# --- Download binary ---
if [ "$VERSION" = "latest" ]; then
  DOWNLOAD_URL="${CDN_URL}/releases/runner/latest/${BINARY_NAME}-${OS}-${ARCH}"
else
  DOWNLOAD_URL="${CDN_URL}/releases/runner/${VERSION}/${BINARY_NAME}-${OS}-${ARCH}"
fi
info "Downloading ${BINARY_NAME} v${VERSION}..."

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

TEMP_BIN="${TEMP_DIR}/${BINARY_NAME}"
if ! http_get_file "$DOWNLOAD_URL" "$TEMP_BIN"; then
  die "Failed to download from ${DOWNLOAD_URL}"
fi

chmod +x "$TEMP_BIN"
ok "Downloaded successfully"

# --- Uninstall existing runner if present ---
if [[ "$OS" == "linux" ]]; then
  if $SUDO systemctl is-active --quiet "$SERVICE_NAME" 2>/dev/null; then
    warn "Existing runner detected. Stopping service..."
    $SUDO systemctl stop "$SERVICE_NAME"
  fi

  if $SUDO systemctl is-enabled --quiet "$SERVICE_NAME" 2>/dev/null; then
    $SUDO systemctl disable "$SERVICE_NAME" 2>/dev/null || true
  fi

  if [[ -f "/etc/systemd/system/${SERVICE_NAME}.service" ]]; then
    warn "Removing old systemd unit..."
    $SUDO rm -f "/etc/systemd/system/${SERVICE_NAME}.service"
    $SUDO systemctl daemon-reload
  fi
elif [[ "$OS" == "darwin" ]]; then
  if $SUDO launchctl list "$PLIST_LABEL" >/dev/null 2>&1; then
    warn "Existing runner detected. Stopping service..."
    $SUDO launchctl bootout "system/${PLIST_LABEL}" 2>/dev/null || \
      $SUDO launchctl unload "$PLIST_PATH" 2>/dev/null || true
  fi

  if [[ -f "$PLIST_PATH" ]]; then
    warn "Removing old launchd plist..."
    $SUDO rm -f "$PLIST_PATH"
  fi
fi

if [[ -e "${INSTALL_DIR}/${BINARY_NAME}" ]] || [[ -L "${INSTALL_DIR}/${BINARY_NAME}" ]]; then
  warn "Removing old binary..."
  $SUDO rm -f "${INSTALL_DIR}/${BINARY_NAME}"
  $SUDO rm -f "${INSTALL_DIR}/${BINARY_NAME}.old"
fi
if [[ -e "${RUNNER_BIN_DIR}/${BINARY_NAME}" ]]; then
  $SUDO rm -f "${RUNNER_BIN_DIR}/${BINARY_NAME}"
  $SUDO rm -f "${RUNNER_BIN_DIR}/${BINARY_NAME}.old"
fi

if [[ -d "$CONFIG_DIR" ]]; then
  warn "Removing old config..."
  $SUDO rm -rf "$CONFIG_DIR"
fi

ok "Clean slate ready"

# --- Install binary ---
# Linux installs run as a non-root user (User=$RUNNER_USER in the systemd
# unit), so the runner needs write access to the directory containing its
# own binary to perform self-updates (which rename(2) the binary). We
# install the real binary into a runner-owned directory and symlink it
# from INSTALL_DIR for PATH discoverability.
if [[ "$OS" == "linux" ]]; then
  REAL_BIN_PATH="${RUNNER_BIN_DIR}/${BINARY_NAME}"
  info "Installing to ${REAL_BIN_PATH} (symlinked from ${INSTALL_DIR}/${BINARY_NAME})..."

  $SUDO mkdir -p "$RUNNER_BIN_DIR"
  $SUDO cp "$TEMP_BIN" "$REAL_BIN_PATH"
  $SUDO chmod 0755 "$REAL_BIN_PATH"
  # Whole tree must be writable by the runner user so rename(2) succeeds
  # during self-update — that requires write+execute on the directory,
  # not just the file.
  $SUDO chown -R "${RUNNER_USER}:${RUNNER_GROUP}" "$RUNNER_HOME_DIR"
  $SUDO ln -sf "$REAL_BIN_PATH" "${INSTALL_DIR}/${BINARY_NAME}"

  ok "Binary installed to ${REAL_BIN_PATH}"
else
  info "Installing to ${INSTALL_DIR}/${BINARY_NAME}..."

  $SUDO cp "$TEMP_BIN" "${INSTALL_DIR}/${BINARY_NAME}"
  $SUDO chmod +x "${INSTALL_DIR}/${BINARY_NAME}"

  ok "Binary installed to ${INSTALL_DIR}/${BINARY_NAME}"
fi

# --- Create config directory ---
$SUDO mkdir -p "$CONFIG_DIR"
if [[ "$OS" == "linux" ]]; then
  $SUDO chown "${RUNNER_USER}:${RUNNER_GROUP}" "$CONFIG_DIR"
fi
$SUDO chmod 0700 "$CONFIG_DIR"
$SUDO mkdir -p /tmp/reoclo-

# --- Initial registration ---
#
# In `system` / `user` modes the runner needs to write its session JWT to
# ${CONFIG_DIR}/runner.json BEFORE we install the systemd / launchd unit,
# otherwise the service starts up with no credentials and crashloops. So we
# do a one-shot foreground run, wait for the config file, then kill it and
# hand control to the init system.
#
# In `foreground` mode (RunPod / Vast.ai / Kubernetes pods) the orchestrator
# owns the process — the runner is launched fresh from run.sh on every pod
# boot and reads its config from env vars (envMode=true), never persisting
# to disk. A pre-install registration here is pointless and actively harmful:
# in a typical pod we're running as root with no `sudo` binary, so
# `$SUDO -u "$RUNNER_USER" ...` expands to `-u "root" ...` and bash
# interprets the leading `-u` as a command (`bash: line N: -u: command not
# found`). Skip this entire block for foreground mode.
if [[ "$MODE" != "foreground" ]]; then
  info "Registering runner with platform..."

  if [[ "$OS" == "linux" ]]; then
    if [[ "$ROOTLESS_DOCKER" == true ]]; then
      # In rootless mode we must inject DOCKER_HOST so the runner talks to the
      # user's rootless socket (sudo -u doesn't carry DOCKER_HOST through).
      $SUDO -u "$RUNNER_USER" -H env \
        "DOCKER_HOST=$ROOTLESS_DOCKER_HOST" \
        "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin" \
        "${INSTALL_DIR}/${BINARY_NAME}" \
        --token "$TOKEN" \
        --gateway "$GATEWAY_URL" &
    else
      $SUDO -u "$RUNNER_USER" "${INSTALL_DIR}/${BINARY_NAME}" \
        --token "$TOKEN" \
        --gateway "$GATEWAY_URL" &
    fi
  else
    $SUDO "${INSTALL_DIR}/${BINARY_NAME}" \
      --token "$TOKEN" \
      --gateway "$GATEWAY_URL" &
  fi

  RUNNER_PID=$!

  # Wait up to 30 seconds for registration
  for i in $(seq 1 30); do
    if [[ -f "${CONFIG_DIR}/runner.json" ]] || $SUDO test -f "${CONFIG_DIR}/runner.json"; then
      ok "Runner registered successfully"
      kill "$RUNNER_PID" 2>/dev/null || true
      wait "$RUNNER_PID" 2>/dev/null || true
      break
    fi
    sleep 1
  done

  if ! { [[ -f "${CONFIG_DIR}/runner.json" ]] || $SUDO test -f "${CONFIG_DIR}/runner.json"; }; then
    kill "$RUNNER_PID" 2>/dev/null || true
    wait "$RUNNER_PID" 2>/dev/null || true
    die "Registration timed out. Check your token and gateway URL."
  fi
fi

# --- Install service ---
# Slice 1 — foreground mode: skip the system-init service files entirely.
# Pods get a run.sh wrapper that the orchestrator (RunPod, etc.) execs.
if [[ "$MODE" == "foreground" ]]; then
  RUN_SH="${RUNNER_HOME_DIR}/run.sh"
  $SUDO mkdir -p "$RUNNER_HOME_DIR"
  # Precompute the optional REOCLO_HOSTNAME export. Inlining
  # `${HOSTNAME_OVERRIDE:+export REOCLO_HOSTNAME="${REOCLO_HOSTNAME:-$HOSTNAME_OVERRIDE}"}`
  # inside the heredoc looks tempting but bash's nested-${...} parser inside
  # ${VAR:+...} misreads the closing brace of the inner `${REOCLO_HOSTNAME:-...}`
  # as closing the OUTER ${HOSTNAME_OVERRIDE:+...}, leaking the trailing `\"`
  # out as literal text and producing an unterminated quote — the resulting
  # run.sh fails at runtime with `bash: line N: ": command not found`. Build
  # the line up-front instead so the heredoc just inlines a plain string.
  if [[ -n "$HOSTNAME_OVERRIDE" ]]; then
    HOSTNAME_EXPORT_LINE="export REOCLO_HOSTNAME=\"\${REOCLO_HOSTNAME:-${HOSTNAME_OVERRIDE}}\""
  else
    HOSTNAME_EXPORT_LINE="# (no REOCLO_HOSTNAME override — runner derives from system hostname)"
  fi
  $SUDO tee "$RUN_SH" > /dev/null <<RUNSH
#!/usr/bin/env bash
# Reoclo Runner — foreground entrypoint (Slice 1).
# The runner reads its config from env vars (REOCLO_TOKEN, REOCLO_GATEWAY_URL,
# optional REOCLO_HOSTNAME, REOCLO_PROFILE) and never persists to disk —
# suitable for ephemeral pod filesystems. Re-registers on every boot.
set -e
export REOCLO_TOKEN="\${REOCLO_TOKEN:-${TOKEN}}"
export REOCLO_GATEWAY_URL="\${REOCLO_GATEWAY_URL:-${GATEWAY_URL}}"
export REOCLO_PROFILE="\${REOCLO_PROFILE:-${PROFILE}}"
${HOSTNAME_EXPORT_LINE}
exec "${RUNNER_BIN_DIR}/${BINARY_NAME}"
RUNSH
  $SUDO chmod +x "$RUN_SH"
  ok "Wrote foreground entrypoint: ${RUN_SH}"
  cat <<HINT

  Foreground mode — no service was installed. To run the runner, exec:
    ${RUN_SH}

  For pod images (RunPod / Kubernetes), use ${RUN_SH} as your container
  ENTRYPOINT. The runner reads REOCLO_TOKEN / REOCLO_GATEWAY_URL /
  REOCLO_PROFILE / REOCLO_HOSTNAME from env so the same image can register
  many pods just by varying env-vars.
HINT
elif [[ "$OS" == "linux" ]]; then
  info "Installing systemd service..."

  if [[ "$ROOTLESS_DOCKER" == true ]]; then
    # Rootless mode: run as the rootless-docker user, inject DOCKER_HOST, and
    # drop Requires=docker.service (rootless Docker is a user-level unit, not
    # the system docker.service). ProtectHome is omitted so the runner can
    # reach the rootless Docker state under ~/.local/share/docker.
    $SUDO tee "/etc/systemd/system/${SERVICE_NAME}.service" > /dev/null << UNIT
[Unit]
Description=Reoclo Runner Agent
Documentation=https://docs.reoclo.dev/runner
After=network-online.target
Wants=network-online.target

[Service]
Type=simple
ExecStart=${RUNNER_BIN_DIR}/${BINARY_NAME} --config /etc/reoclo/runner.json
Restart=always
RestartSec=5
User=${RUNNER_USER}
Group=${RUNNER_GROUP}
# Files created under /srv/reoclo/<app>/ inherit the reoclo-deploy group via
# setgid; umask 0002 ensures they also inherit group-write so human admins can
# edit them without re-chmoding after every runner-side write.
UMask=0002
Environment="DOCKER_HOST=${ROOTLESS_DOCKER_HOST}"
Environment="PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"

# Security hardening (ProtectHome omitted — rootless Docker state lives under ~)
ProtectKernelTunables=yes
ProtectKernelModules=yes
ProtectControlGroups=yes
RestrictRealtime=yes
SystemCallArchitectures=native

# Logging
StandardOutput=journal
StandardError=journal
SyslogIdentifier=reoclo-runner

[Install]
WantedBy=multi-user.target
UNIT
  else
    $SUDO tee "/etc/systemd/system/${SERVICE_NAME}.service" > /dev/null << 'UNIT'
[Unit]
Description=Reoclo Runner Agent
Documentation=https://docs.reoclo.dev/runner
After=network-online.target docker.service
Wants=network-online.target
Requires=docker.service

[Service]
Type=simple
ExecStart=/opt/reoclo/bin/reoclo-runner --config /etc/reoclo/runner.json
Restart=always
RestartSec=5
User=reoclo
Group=reoclo
# Files created under /srv/reoclo/<app>/ inherit the reoclo-deploy group via
# setgid; umask 0002 ensures they also inherit group-write so human admins can
# edit them without re-chmoding after every runner-side write.
UMask=0002

# Security hardening (NoNewPrivileges omitted to allow sudoers whitelist)
ProtectHome=yes
ProtectKernelTunables=yes
ProtectKernelModules=yes
ProtectControlGroups=yes
RestrictRealtime=yes
SystemCallArchitectures=native

# Logging
StandardOutput=journal
StandardError=journal
SyslogIdentifier=reoclo-runner

[Install]
WantedBy=multi-user.target
UNIT
  fi

  $SUDO systemctl daemon-reload
  $SUDO systemctl enable "$SERVICE_NAME"
  $SUDO systemctl start "$SERVICE_NAME"

  # Wait for initial connection, then restart to ensure fresh capabilities
  # are reported (the temporary registration process may have sent stale data)
  sleep 8
  $SUDO systemctl restart "$SERVICE_NAME"

  ok "Service installed and started"

elif [[ "$OS" == "darwin" ]]; then
  info "Installing launchd service..."

  $SUDO tee "$PLIST_PATH" > /dev/null << PLIST
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
    <key>Label</key>
    <string>${PLIST_LABEL}</string>
    <key>ProgramArguments</key>
    <array>
        <string>${INSTALL_DIR}/${BINARY_NAME}</string>
        <string>--config</string>
        <string>${CONFIG_DIR}/runner.json</string>
    </array>
    <key>RunAtLoad</key>
    <true/>
    <key>KeepAlive</key>
    <true/>
    <key>ThrottleInterval</key>
    <integer>5</integer>
    <key>StandardOutPath</key>
    <string>${LOG_DIR}/reoclo-runner.log</string>
    <key>StandardErrorPath</key>
    <string>${LOG_DIR}/reoclo-runner.log</string>
    <key>EnvironmentVariables</key>
    <dict>
        <key>PATH</key>
        <string>/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin</string>
    </dict>
</dict>
</plist>
PLIST

  $SUDO launchctl bootstrap system "$PLIST_PATH" 2>/dev/null || \
    $SUDO launchctl load -w "$PLIST_PATH"

  ok "Service installed and started"
fi

# --- Verify ---
# Foreground mode skips systemd/launchd entirely (pod orchestrator owns the
# process lifecycle), so there's no service to probe. The pod-image
# ENTRYPOINT re-execs run.sh on every boot; verification happens at the
# orchestrator level, not here.
if [[ "$MODE" != "foreground" ]]; then
  sleep 2
  if [[ "$OS" == "linux" ]]; then
    if $SUDO systemctl is-active --quiet "$SERVICE_NAME"; then
      ok "Runner is active and running"
    else
      warn "Service started but may not be fully ready yet"
      warn "Check status: systemctl status ${SERVICE_NAME}"
      warn "Check logs:   journalctl -u ${SERVICE_NAME} -f"
    fi
  elif [[ "$OS" == "darwin" ]]; then
    if $SUDO launchctl list "$PLIST_LABEL" >/dev/null 2>&1; then
      ok "Runner is active and running"
    else
      warn "Service started but may not be fully ready yet"
      warn "Check status: sudo launchctl list ${PLIST_LABEL}"
      warn "Check logs:   tail -f ${LOG_DIR}/reoclo-runner.log"
    fi
  fi
fi

echo ""
if [[ "$MODE" == "foreground" ]]; then
  echo -e "${GREEN}╔═══════════════════════════════════════════════════╗${NC}"
  echo -e "${GREEN}║  Reoclo Runner installed (foreground mode)        ║${NC}"
  echo -e "${GREEN}╠═══════════════════════════════════════════════════╣${NC}"
  echo -e "${GREEN}║                                                   ║${NC}"
  echo -e "${GREEN}║  Binary:     ${INSTALL_DIR}/${BINARY_NAME}${NC}        ${GREEN}║${NC}"
  echo -e "${GREEN}║  Entrypoint: ${RUNNER_HOME_DIR}/run.sh${NC}                ${GREEN}║${NC}"
  echo -e "${GREEN}║  Profile:    ${PROFILE}${NC}                                ${GREEN}║${NC}"
  echo -e "${GREEN}║                                                   ║${NC}"
  echo -e "${GREEN}║  Run:   ${RUNNER_HOME_DIR}/run.sh${NC}                     ${GREEN}║${NC}"
  echo -e "${GREEN}║                                                   ║${NC}"
  echo -e "${GREEN}║  For pod images, set ${RUNNER_HOME_DIR}/run.sh as${NC}     ${GREEN}║${NC}"
  echo -e "${GREEN}║  the container ENTRYPOINT. Env: REOCLO_TOKEN,     ║${NC}"
  echo -e "${GREEN}║  REOCLO_GATEWAY_URL, REOCLO_PROFILE,              ║${NC}"
  echo -e "${GREEN}║  REOCLO_HOSTNAME.                                 ║${NC}"
  echo -e "${GREEN}║                                                   ║${NC}"
  echo -e "${GREEN}╚═══════════════════════════════════════════════════╝${NC}"
elif [[ "$OS" == "linux" ]]; then
  echo -e "${GREEN}╔═══════════════════════════════════════════════════╗${NC}"
  echo -e "${GREEN}║  Reoclo Runner installed successfully!            ║${NC}"
  echo -e "${GREEN}╠═══════════════════════════════════════════════════╣${NC}"
  echo -e "${GREEN}║                                                   ║${NC}"
  echo -e "${GREEN}║  Binary:  ${INSTALL_DIR}/${BINARY_NAME}${NC}           ${GREEN}║${NC}"
  echo -e "${GREEN}║  Config:  ${CONFIG_DIR}/runner.json${NC}                ${GREEN}║${NC}"
  echo -e "${GREEN}║  Service: ${SERVICE_NAME}${NC}                          ${GREEN}║${NC}"
  echo -e "${GREEN}║                                                   ║${NC}"
  echo -e "${GREEN}║  Commands:                                        ║${NC}"
  echo -e "${GREEN}║    Status: systemctl status ${SERVICE_NAME}${NC}        ${GREEN}║${NC}"
  echo -e "${GREEN}║    Logs:   journalctl -u ${SERVICE_NAME} -f${NC}       ${GREEN}║${NC}"
  echo -e "${GREEN}║    Stop:   systemctl stop ${SERVICE_NAME}${NC}          ${GREEN}║${NC}"
  echo -e "${GREEN}║    Start:  systemctl start ${SERVICE_NAME}${NC}         ${GREEN}║${NC}"
  echo -e "${GREEN}║                                                   ║${NC}"
  echo -e "${GREEN}╚═══════════════════════════════════════════════════╝${NC}"
elif [[ "$OS" == "darwin" ]]; then
  echo -e "${GREEN}╔═══════════════════════════════════════════════════════════╗${NC}"
  echo -e "${GREEN}║  Reoclo Runner installed successfully!                    ║${NC}"
  echo -e "${GREEN}╠═══════════════════════════════════════════════════════════╣${NC}"
  echo -e "${GREEN}║                                                           ║${NC}"
  echo -e "${GREEN}║  Binary:  ${INSTALL_DIR}/${BINARY_NAME}${NC}                   ${GREEN}║${NC}"
  echo -e "${GREEN}║  Config:  ${CONFIG_DIR}/runner.json${NC}                        ${GREEN}║${NC}"
  echo -e "${GREEN}║  Service: ${PLIST_LABEL}${NC}                              ${GREEN}║${NC}"
  echo -e "${GREEN}║  Logs:    ${LOG_DIR}/reoclo-runner.log${NC}                     ${GREEN}║${NC}"
  echo -e "${GREEN}║                                                           ║${NC}"
  echo -e "${GREEN}║  Commands:                                                ║${NC}"
  echo -e "${GREEN}║    Status: sudo launchctl list ${PLIST_LABEL}${NC}         ${GREEN}║${NC}"
  echo -e "${GREEN}║    Logs:   tail -f ${LOG_DIR}/reoclo-runner.log${NC}            ${GREEN}║${NC}"
  echo -e "${GREEN}║    Stop:   sudo launchctl bootout system/${PLIST_LABEL}${NC} ${GREEN}║${NC}"
  echo -e "${GREEN}║    Start:  sudo launchctl bootstrap system ${PLIST_PATH}${NC} ${GREEN}║${NC}"
  echo -e "${GREEN}║                                                           ║${NC}"
  echo -e "${GREEN}╚═══════════════════════════════════════════════════════════╝${NC}"
fi

# --- Deploy folder next-step hint (Linux full-profile only) ---
# Lite-profile / foreground runners don't deploy Docker apps (the platform
# refuses dispatches), so the /srv/reoclo deploy-folder workflow doesn't
# apply. Skip the hint to avoid confusing pod operators with irrelevant
# rootless-docker / reoclo-deploy-group instructions.
if [[ "$OS" == "linux" && "$MODE" != "foreground" && "$PROFILE" != "lite" ]]; then
  echo ""
  echo -e "${BLUE}── Next step: deploy folder ───────────────────────────────${NC}"
  if [[ "$ROOTLESS_DOCKER" == true ]]; then
    echo "Reoclo provisioned /srv/reoclo (owner: ${RUNNER_USER}) as the recommended"
    echo "home for your deploy folders. The runner already runs as this user, so"
    echo "no chown is needed per app:"
    echo ""
    echo "  mkdir /srv/reoclo/<app-name>     # no sudo, no chown"
    echo "  cd /srv/reoclo/<app-name>"
    echo "  # drop in docker-compose.yml, .env, etc — the runner can read/write."
  else
    # SUDO_USER is set when the operator ran the installer via sudo. If they ran
    # as root directly there's no "intended user" to name, so we fall back to a
    # generic placeholder.
    DEPLOY_HINT_USER="${SUDO_USER:-<your-user>}"
    echo "Reoclo provisioned /srv/reoclo (group: reoclo-deploy, setgid) so the"
    echo "runner and your admins share write access without re-chowning files."
    echo ""
    echo "To put yourself in the group (one-time, per human admin):"
    echo "  sudo usermod -aG reoclo-deploy ${DEPLOY_HINT_USER}"
    echo "  newgrp reoclo-deploy        # or log out / back in"
    echo ""
    echo "Then create a folder per app — no sudo, no chown:"
    echo "  mkdir /srv/reoclo/<app-name>"
  fi
  echo ""
  echo "Docs: https://docs.reoclo.dev/guides/external-deployments/server-setup/"
fi
