#!/bin/sh
# anyagent bootstrap — the `curl https://get.anyagent.eu | sh` entry point.
#
# Minimal + POSIX sh. Gets a bare VPS to the point where the operator can run
# `anyagent login`:
#   1. ensure Docker Engine + compose plugin (install via get.docker.com if absent)
#   2. ensure the Docker daemon has IPv6 + ip6tables (the stack's compose network
#      sets enable_ipv6; without daemon support `up` fails creating the network)
#   3. install the `anyagent` CLI + login.sh/update.sh (from a checkout if present,
#      else downloaded from $ANYAGENT_GET_URL) onto the box
#   4. persist ANYAGENT_HOME
# `anyagent login` then activates the box at central (device-flow URL+code),
# writes registry creds, and downloads the rest of the deploy-kit (compose,
# nginx, config, runner CLIs, installer).
#
# Verifying this script before running it (AA-21):
#   The `curl … | sh` one-liner cannot verify itself — inherent to the pattern.
#   To verify out-of-band, download instead of piping, compare its SHA-256
#   against the digest published in the release notes, then run it:
#     curl -fsSLO https://get.anyagent.eu/bootstrap.sh
#     sha256sum bootstrap.sh          # compare to the published digest
#     sh bootstrap.sh
#   Every client script this bootstrap then downloads (the anyagent CLI,
#   login.sh, update.sh) is checksum-verified automatically against the
#   published SHA256SUMS manifest below — a tampered or partial object aborts
#   the install.

set -eu

ANYAGENT_HOME="${ANYAGENT_HOME:-/opt/anyagent}"
GET_URL="${ANYAGENT_GET_URL:-https://get.anyagent.eu}"

green()  { printf '\033[32m%s\033[0m\n' "$*"; }
yellow() { printf '\033[33m%s\033[0m\n' "$*"; }
red()    { printf '\033[31m%s\033[0m\n' "$*" >&2; }

SUDO=""
if [ "$(id -u)" -ne 0 ]; then
  if command -v sudo >/dev/null 2>&1; then SUDO="sudo"; else
    red "error: needs root to install Docker, write $ANYAGENT_HOME and /usr/local/bin."
    red "  re-run as root, or install sudo."
    exit 1
  fi
fi

# ---------- Docker ----------
green "── Docker"
if command -v docker >/dev/null 2>&1 && docker compose version >/dev/null 2>&1; then
  echo "  present: $(docker --version 2>/dev/null)"
else
  yellow "  Docker (or the compose plugin) not found — installing via get.docker.com"
  curl -fsSL https://get.docker.com | $SUDO sh
  $SUDO systemctl enable --now docker 2>/dev/null || true
  green "  Docker installed"
fi

# GPU is optional (the default OpenRouter backend needs none).
if command -v nvidia-smi >/dev/null 2>&1 && \
   ! (command -v nvidia-ctk >/dev/null 2>&1 || command -v nvidia-container-toolkit >/dev/null 2>&1); then
  yellow "  note: NVIDIA GPU detected but nvidia-container-toolkit is missing —"
  yellow "        install it for local vLLM inference, or use the OpenRouter backend."
fi

# ---------- Docker IPv6 ----------
# The stack's compose network declares enable_ipv6, so the daemon needs ipv6 +
# ip6tables or `docker compose up` fails to create the network. Seed daemon.json
# only when absent (never clobber an operator's existing config).
green "── Docker IPv6"
if [ ! -f /etc/docker/daemon.json ]; then
  $SUDO mkdir -p /etc/docker
  printf '{\n  "ipv6": true,\n  "ip6tables": true,\n  "fixed-cidr-v6": "fd00:d0c5:e6::/64"\n}\n' \
    | $SUDO tee /etc/docker/daemon.json >/dev/null
  $SUDO systemctl restart docker 2>/dev/null || $SUDO service docker restart 2>/dev/null || true
  echo "  enabled ipv6 + ip6tables in /etc/docker/daemon.json"
else
  if grep -q '"ipv6"' /etc/docker/daemon.json 2>/dev/null; then
    echo "  daemon.json already configures ipv6 — leaving as-is"
  else
    yellow "  /etc/docker/daemon.json exists without ipv6 — add '\"ipv6\": true, \"ip6tables\": true,"
    yellow "  \"fixed-cidr-v6\": \"fd00:d0c5:e6::/64\"' and restart docker, or the stack network won't create."
  fi
fi

# ---------- install location ----------
green "── install location"
$SUDO mkdir -p "$ANYAGENT_HOME/tools/installer" "$ANYAGENT_HOME/tools/cli"
echo "  ANYAGENT_HOME: $ANYAGENT_HOME"

# ---------- CLI + bootstrap installer scripts ----------
# From a checkout/deploy-kit, copy the bundled files. For a remote `curl | sh`
# run, download the thin client (CLI + login.sh + update.sh) from $GET_URL — just
# enough for `anyagent login` to fetch the full deploy-kit.
green "── anyagent CLI"
SCRIPT_DIR="$(cd "$(dirname "$0")" 2>/dev/null && pwd || echo /nonexistent)"

# sha256 helper — coreutils `sha256sum` or BSD/mac `shasum -a 256`.
sha256_of() { # sha256_of <file> → prints the hex digest (empty if no tool)
  if command -v sha256sum >/dev/null 2>&1; then
    sha256sum "$1" | awk '{print $1}'
  elif command -v shasum >/dev/null 2>&1; then
    shasum -a 256 "$1" | awk '{print $1}'
  fi
}

# For remote installs, fetch the checksum manifest once so each downloaded
# client script can be verified against it (AA-21). Absent tooling/manifest we
# warn loudly and continue (the download is still TLS-protected); a present
# manifest is enforced fail-closed on mismatch.
SUMS_FILE=""
if [ ! -f "$SCRIPT_DIR/../installer/bootstrap.sh" ] && [ ! -f "$SCRIPT_DIR/../cli/anyagent" ]; then
  if [ -z "$(sha256_of /dev/null)" ]; then
    yellow "  warning: no sha256sum/shasum on this box — skipping download verification"
  elif SUMS_TMP="$(mktemp 2>/dev/null)" && curl -fsSL "$GET_URL/SHA256SUMS" -o "$SUMS_TMP" 2>/dev/null; then
    SUMS_FILE="$SUMS_TMP"
  else
    yellow "  warning: could not fetch $GET_URL/SHA256SUMS — skipping download verification"
  fi
fi

verify_sum() { # verify_sum <relpath> <file> — aborts on mismatch; no-op if unverifiable
  [ -n "$SUMS_FILE" ] || return 0
  expected="$(awk -v p="$1" '$2==p {print $1}' "$SUMS_FILE")"
  if [ -z "$expected" ]; then
    red "error: $1 not listed in SHA256SUMS — refusing to install an unverified file."
    exit 1
  fi
  actual="$(sha256_of "$2")"
  if [ "$expected" != "$actual" ]; then
    red "error: checksum mismatch for $1"
    red "  expected $expected"
    red "  actual   $actual"
    exit 1
  fi
}

fetch() { # fetch <relpath> <dest> <mode>
  if [ -f "$SCRIPT_DIR/../${1#tools/}" ]; then
    $SUDO install -m "$3" "$SCRIPT_DIR/../${1#tools/}" "$2"
  else
    $SUDO sh -c "curl -fsSL '$GET_URL/$1' -o '$2'" && $SUDO chmod "$3" "$2"
    verify_sum "$1" "$2"
  fi
}
fetch tools/cli/anyagent            /usr/local/bin/anyagent          0755
fetch tools/installer/login.sh      "$ANYAGENT_HOME/tools/installer/login.sh"  0755
fetch tools/installer/update.sh     "$ANYAGENT_HOME/tools/installer/update.sh" 0755
echo "  installed /usr/local/bin/anyagent + login.sh/update.sh"

if [ -d /etc/profile.d ]; then
  printf 'export ANYAGENT_HOME="%s"\n' "$ANYAGENT_HOME" | $SUDO tee /etc/profile.d/anyagent.sh >/dev/null
  $SUDO chmod 0644 /etc/profile.d/anyagent.sh
fi

green "── done"
# The CLI defaults ANYAGENT_HOME to /opt/anyagent, so the default install needs
# no prefix. Only show it for a non-default home (this shell hasn't sourced
# /etc/profile.d yet, and a curl|sh subshell can't export into the parent shell).
if [ "$ANYAGENT_HOME" = "/opt/anyagent" ]; then
  LOGIN_CMD="anyagent login"; UP_CMD="anyagent up"
else
  LOGIN_CMD="ANYAGENT_HOME=$ANYAGENT_HOME anyagent login"
  UP_CMD="ANYAGENT_HOME=$ANYAGENT_HOME anyagent up"
fi
cat <<EOF

Next — activate this box at central (fetches the stack config + registry creds):

  $LOGIN_CMD

This prints a URL + short code; approve it on the central console and login
finishes automatically. Then bring everything up:

  $UP_CMD
EOF
