#!/usr/bin/env bash
# Public bootstrap installer for mesh — fetch, verify, and install meshd +
# meshctl on a fresh machine with no prior mesh install and no Gitea access.
# Unlike scripts/update.sh (which updates an already-bootstrapped, already-
# mesh-connected node via Gitea directly), this script downloads from the
# public distribution mirror (see docs/DEPLOY-DIST-MIRROR.md) — no Gitea
# reachability or credential is ever needed here.
#
# Usage:
#   curl -fsSL https://dl.n3rd-lab.net/install.sh | bash            # latest
#   curl -fsSL https://dl.n3rd-lab.net/install.sh | bash -s v2.2.0  # pinned
#
# Config (env vars, with defaults):
#   MESH_DIST_BASE        Public mirror base URL      (default: https://dl.n3rd-lab.net)
#   MESH_INSTALL_DIR      where to install binaries   (default: /usr/local/bin)
#   MESH_PUBKEY           minisign public key string  (overrides the bundled key below)
#   MESH_ALLOW_UNSIGNED   set to 1 to install without a signature (INSECURE; only
#                         for first-time bootstrap before a release key exists)
#   MESH_ETC_DIR          config directory            (default: /etc/mesh)
#   MESH_SYSTEMD_UNIT_DIR systemd unit directory       (default: /etc/systemd/system)
#
# This script stops once binaries (and, on Linux with systemd, the mesh user +
# unit) are in place. It does NOT run `meshctl init`, edit mesh.toml, or start
# the service — see the "next steps" it prints, and INSTALL.md.
set -euo pipefail

# BUNDLED_PUBKEY mirrors scripts/update.sh's constant of the same name — the
# release minisign public key, pinned so a fresh curl|bash run verifies
# authenticity by default. Keep the two files in sync (see RELEASING.md).
BUNDLED_PUBKEY="RWQK3/8No2f+nc+APeCdeDqeDCi7uRDydDFlHYQJk7DAqxWlgj5cdMH+"

DIST_BASE="${MESH_DIST_BASE:-https://dl.n3rd-lab.net}"
INSTALL_DIR="${MESH_INSTALL_DIR:-/usr/local/bin}"
PUBKEY="${MESH_PUBKEY:-$BUNDLED_PUBKEY}"
ALLOW_UNSIGNED="${MESH_ALLOW_UNSIGNED:-0}"
ETC_DIR="${MESH_ETC_DIR:-/etc/mesh}"
UNIT_DIR="${MESH_SYSTEMD_UNIT_DIR:-/etc/systemd/system}"
VERSION="${1:-}"

# --- prerequisites ---------------------------------------------------------
have() { command -v "$1" >/dev/null 2>&1; }

if have curl; then
  DL() { curl -fsSL "$1" -o "$2"; }
elif have wget; then
  DL() { wget -q "$1" -O "$2"; }
else
  echo "install: need curl or wget on PATH" >&2; exit 1
fi

if have sha256sum; then
  SHACHECK() { sha256sum --ignore-missing -c "$1"; }
elif have shasum; then
  SHACHECK() { shasum -a 256 --ignore-missing -c "$1"; }
else
  echo "install: need sha256sum or shasum on PATH" >&2; exit 1
fi

# --- platform detection ----------------------------------------------------
case "$(uname -s)" in
  Linux)  OS=linux ;;
  Darwin) OS=darwin ;;
  *) echo "install: unsupported OS $(uname -s) (mesh supports linux and macOS)" >&2; exit 1 ;;
esac
case "$(uname -m)" in
  x86_64|amd64)  ARCH=amd64 ;;
  aarch64|arm64) ARCH=arm64 ;;
  *) echo "install: unsupported arch $(uname -m)" >&2; exit 1 ;;
esac

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

# --- resolve version -------------------------------------------------------
if [ -z "$VERSION" ]; then
  echo "install: resolving latest version from $DIST_BASE..."
  DL "$DIST_BASE/latest" "$TMP/latest" || { echo "install: could not fetch $DIST_BASE/latest" >&2; exit 1; }
  VERSION="$(tr -d '[:space:]' < "$TMP/latest")"
  [ -n "$VERSION" ] || { echo "install: $DIST_BASE/latest was empty" >&2; exit 1; }
fi
echo "install: installing mesh $VERSION ($OS/$ARCH)"

TARBALL="mesh-${VERSION}-${OS}-${ARCH}.tar.gz"
DLBASE="$DIST_BASE/$VERSION"

# --- download --------------------------------------------------------------
echo "install: downloading $TARBALL + SHA256SUMS"
DL "$DLBASE/$TARBALL"   "$TMP/$TARBALL"
DL "$DLBASE/SHA256SUMS" "$TMP/SHA256SUMS"
if DL "$DLBASE/SHA256SUMS.minisig" "$TMP/SHA256SUMS.minisig" 2>/dev/null; then
  HAVE_SIG=1
else
  HAVE_SIG=0
fi

# --- verify ----------------------------------------------------------------
if [ -n "$PUBKEY" ]; then
  have minisign || { echo "install: a public key is configured but minisign is not installed (brew/apt install minisign)" >&2; exit 1; }
  [ "$HAVE_SIG" = 1 ] || { echo "install: a public key is configured but the release has no SHA256SUMS.minisig" >&2; exit 1; }
  ( cd "$TMP" && minisign -Vm SHA256SUMS -P "$PUBKEY" ) || { echo "install: SIGNATURE VERIFICATION FAILED — aborting" >&2; exit 1; }
  echo "install: signature OK"
elif [ "$ALLOW_UNSIGNED" = 1 ]; then
  echo "install: WARNING — MESH_ALLOW_UNSIGNED=1: installing without signature verification (integrity only, NOT authenticity)." >&2
else
  echo "install: refusing to install without signature verification." >&2
  echo "         Set MESH_PUBKEY to the release minisign public key (recommended)," >&2
  echo "         or MESH_ALLOW_UNSIGNED=1 to bootstrap insecurely this once." >&2
  exit 1
fi

( cd "$TMP" && SHACHECK SHA256SUMS ) || { echo "install: CHECKSUM MISMATCH — aborting" >&2; exit 1; }
echo "install: checksum OK"

# --- install binaries -------------------------------------------------------
tar -xzf "$TMP/$TARBALL" -C "$TMP"
SRC="$TMP/mesh-${VERSION}-${OS}-${ARCH}"
[ -x "$SRC/meshd" ] && [ -x "$SRC/meshctl" ] || { echo "install: tarball missing meshd/meshctl" >&2; exit 1; }

if [ -w "$INSTALL_DIR" ]; then INSTALL="install -m 755"; else INSTALL="sudo install -m 755"; fi
$INSTALL "$SRC/meshd"   "$INSTALL_DIR/meshd"
$INSTALL "$SRC/meshctl" "$INSTALL_DIR/meshctl"
echo "install: installed meshd + meshctl to $INSTALL_DIR ($("$INSTALL_DIR/meshd" -version))"

# --- Linux + systemd: provision the mesh user + unit (idempotent) ----------
SERVICE_READY=0
if [ "$OS" = linux ] && have systemctl && have useradd; then
  if [ -f "$SRC/mesh.service" ]; then
    if ! id mesh >/dev/null 2>&1; then
      echo "install: creating system user 'mesh'"
      sudo useradd --system --no-create-home --shell /usr/sbin/nologin mesh
    else
      echo "install: system user 'mesh' already exists"
    fi
    sudo mkdir -p "$ETC_DIR"
    sudo install -m 644 "$SRC/mesh.service" "$UNIT_DIR/mesh.service"
    sudo systemctl daemon-reload
    echo "install: installed systemd unit $UNIT_DIR/mesh.service (not started — no config yet)"
    SERVICE_READY=1
  else
    echo "install: tarball has no mesh.service (unexpected for a linux release) — skipping service setup" >&2
  fi
else
  echo "install: skipping systemd service setup (not Linux, or systemctl/useradd not found)"
fi

# --- copy INSTALL.md locally for reference (best-effort) --------------------
cp "$SRC/INSTALL.md" ./INSTALL.md 2>/dev/null || true

# --- next steps --------------------------------------------------------------
echo
echo "install: done. Next steps:"
echo
echo "  1. Create your identity:"
if [ "$SERVICE_READY" = 1 ]; then
  echo "       sudo meshctl init -name <your-name> -psk \"<network PSK>\" -out $ETC_DIR/mesh.toml"
else
  echo "       meshctl init -name <your-name> -psk \"<network PSK>\""
fi
echo "  2. Edit mesh.toml: add peer stanzas and [[services]] you want reachable"
echo "     (see ./INSTALL.md, steps 5-6)."
if [ "$SERVICE_READY" = 1 ]; then
  echo "  3. Hand the config to the mesh user and start the service:"
  echo "       sudo chown mesh:mesh $ETC_DIR/mesh.toml"
  echo "       sudo chmod 600 $ETC_DIR/mesh.toml"
  echo "       sudo systemctl enable --now mesh"
  echo "       journalctl -u mesh -f"
else
  echo "  3. Run it:"
  echo "       sudo ./meshd -config mesh.toml"
fi
