#!/usr/bin/env bash
# LucidStack agent installer, served at https://get.lucidstack.dev
#
#   curl -sSL https://get.lucidstack.dev | sudo bash -s -- --token=lsa_xxx [--endpoint=https://...]
#
# Idempotent: re-running it updates the binary and the systemd unit, and keeps
# the existing token (and other settings) unless new values are given.
set -euo pipefail

DOWNLOAD_URL="${LUCIDSTACK_DOWNLOAD_URL:-https://get.lucidstack.dev/releases/latest}"
SVC_USER=lucidstack-agent
BIN_PATH=/usr/local/bin/lucidstack-agent
CONF_DIR=/etc/lucidstack
CONF_FILE="$CONF_DIR/agent.conf"
UNIT_FILE=/etc/systemd/system/lucidstack-agent.service
DROPIN_FILE=/etc/systemd/system/lucidstack-agent.service.d/crontab.conf

say() { printf '==> %s\n' "$*"; }
die() { printf 'Erreur : %s\n' "$*" >&2; exit 1; }

usage() {
	cat <<'EOF'
Installe l'agent LucidStack (service systemd, utilisateur dédié non root).

  --token=lsa_...     jeton du serveur (obligatoire à la première installation)
  --endpoint=URL      URL d'ingestion (défaut : https://app.lucidstack.dev/api/agent/ingest)
EOF
}

TOKEN=""
ENDPOINT=""
while [ $# -gt 0 ]; do
	case "$1" in
	--token=*) TOKEN="${1#*=}" ;;
	--token) shift; TOKEN="${1:-}" ;;
	--endpoint=*) ENDPOINT="${1#*=}" ;;
	--endpoint) shift; ENDPOINT="${1:-}" ;;
	-h | --help) usage; exit 0 ;;
	*) usage >&2; die "option inconnue : $1" ;;
	esac
	shift
done

[ "$(id -u)" -eq 0 ] || die "ce script doit être lancé en root (sudo)."
[ "$(uname -s)" = "Linux" ] || die "seul Linux est pris en charge."
command -v systemctl >/dev/null 2>&1 && [ -d /run/systemd/system ] ||
	die "systemd est requis ; sans systemd, utilisez l'image Docker."
case "$DOWNLOAD_URL" in https://*) ;; *) die "LUCIDSTACK_DOWNLOAD_URL doit être en https." ;; esac

case "$(uname -m)" in
x86_64 | amd64) ARCH=amd64 ;;
aarch64 | arm64) ARCH=arm64 ;;
*) die "architecture non prise en charge : $(uname -m) (amd64 et arm64 uniquement)." ;;
esac

# Value of KEY in the existing configuration, if any.
conf_get() {
	[ -f "$CONF_FILE" ] || return 0
	sed -n "s/^$1=//p" "$CONF_FILE" | tail -n 1 | sed -e 's/^["'\'']//' -e 's/["'\'']$//'
}

[ -n "$TOKEN" ] || TOKEN="$(conf_get LUCIDSTACK_TOKEN)"
[ -n "$TOKEN" ] || die "jeton manquant : relancez avec --token=lsa_..."
[[ "$TOKEN" =~ ^lsa_[A-Za-z0-9_-]+$ ]] || die "jeton invalide (format attendu : lsa_...)."
if [ -n "$ENDPOINT" ]; then
	re_url='^https://[A-Za-z0-9._~:/?#@!$&()*+,;=%-]+$'
	[[ "$ENDPOINT" =~ $re_url ]] || die "--endpoint doit être une URL https."
fi

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

fetch() {
	if command -v curl >/dev/null 2>&1; then
		curl -fsSL --proto '=https' --tlsv1.2 --retry 3 -o "$2" "$1"
	elif command -v wget >/dev/null 2>&1; then
		wget -q --https-only -O "$2" "$1"
	else
		die "curl ou wget est requis."
	fi
}

say "Téléchargement de l'agent (linux-$ARCH)"
fetch "$DOWNLOAD_URL/lucidstack-agent-linux-$ARCH" "$TMP/lucidstack-agent" || die "téléchargement impossible."
fetch "$DOWNLOAD_URL/lucidstack-agent-linux-$ARCH.sha256" "$TMP/sha256" || die "téléchargement de la somme de contrôle impossible."
expected="$(awk '{print $1; exit}' "$TMP/sha256")"
actual="$(sha256sum "$TMP/lucidstack-agent" | awk '{print $1}')"
[ "${#expected}" -eq 64 ] && [ "$expected" = "$actual" ] || die "somme de contrôle SHA-256 invalide, installation annulée."
chmod 0755 "$TMP/lucidstack-agent"
VERSION="$("$TMP/lucidstack-agent" --version)" || die "le binaire téléchargé ne s'exécute pas."
say "Somme de contrôle vérifiée : $VERSION"

if ! getent passwd "$SVC_USER" >/dev/null; then
	say "Création de l'utilisateur système $SVC_USER"
	NOLOGIN="$(command -v nologin || echo /bin/false)"
	useradd --system --user-group --no-create-home --home-dir /var/lib/lucidstack-agent \
		--shell "$NOLOGIN" --comment "LucidStack agent" "$SVC_USER"
fi

say "Écriture de $CONF_FILE"
install -d -m 0755 -o root -g root "$CONF_DIR"
{
	echo "# LucidStack agent, géré par install.sh (voir https://lucidstack.dev/docs/agent)"
	echo "LUCIDSTACK_TOKEN=$TOKEN"
	if [ -n "$ENDPOINT" ]; then
		echo "LUCIDSTACK_ENDPOINT=$ENDPOINT"
	fi
	# Keep the other settings of an existing configuration.
	if [ -f "$CONF_FILE" ]; then
		drop='^(# LucidStack agent, géré|LUCIDSTACK_TOKEN=)'
		if [ -n "$ENDPOINT" ]; then
			drop='^(# LucidStack agent, géré|LUCIDSTACK_TOKEN=|LUCIDSTACK_ENDPOINT=)'
		fi
		grep -Ev "$drop" "$CONF_FILE" || true
	fi
} >"$TMP/agent.conf"
install -m 0640 -o root -g "$SVC_USER" "$TMP/agent.conf" "$CONF_FILE"

say "Installation de $BIN_PATH"
install -m 0755 -o root -g root "$TMP/lucidstack-agent" "$BIN_PATH.new"
mv -f "$BIN_PATH.new" "$BIN_PATH"

say "Installation du service systemd"
cat >"$UNIT_FILE" <<'UNIT'
# LucidStack monitoring agent. Keep in sync with the copy embedded in install.sh.
[Unit]
Description=LucidStack monitoring agent
Documentation=https://lucidstack.dev/docs/agent
After=network-online.target
Wants=network-online.target

[Service]
Type=simple
User=lucidstack-agent
Group=lucidstack-agent
ExecStart=/usr/local/bin/lucidstack-agent --config /etc/lucidstack/agent.conf
Restart=always
RestartSec=10
TimeoutStopSec=20

# /var/lib/lucidstack-agent (local buffer), owned by the service user.
StateDirectory=lucidstack-agent
StateDirectoryMode=0700
UMask=0077

# Hardening: no privileges, read-only system, outgoing network only.
NoNewPrivileges=yes
CapabilityBoundingSet=
AmbientCapabilities=
ProtectSystem=strict
ProtectHome=read-only
ReadWritePaths=/var/lib/lucidstack-agent
PrivateTmp=yes
PrivateDevices=yes
ProtectKernelTunables=yes
ProtectKernelModules=yes
ProtectKernelLogs=yes
ProtectControlGroups=yes
ProtectClock=yes
ProtectHostname=yes
RestrictAddressFamilies=AF_INET AF_INET6 AF_UNIX AF_NETLINK
RestrictNamespaces=yes
RestrictRealtime=yes
RestrictSUIDSGID=yes
LockPersonality=yes
MemoryDenyWriteExecute=yes
SystemCallArchitectures=native

# Resource budget (target: < 20 MB RSS).
MemoryMax=64M
TasksMax=32

[Install]
WantedBy=multi-user.target
UNIT
chmod 0644 "$UNIT_FILE"

# Debian/Ubuntu: the "crontab" group owns the user crontabs. It does not grant
# read access with the default permissions (see README), but costs nothing.
if getent group crontab >/dev/null; then
	mkdir -p "$(dirname "$DROPIN_FILE")"
	printf '[Service]\nSupplementaryGroups=crontab\n' >"$DROPIN_FILE"
else
	rm -f "$DROPIN_FILE"
fi

systemctl daemon-reload
systemctl enable --quiet lucidstack-agent
systemctl restart lucidstack-agent

sleep 2
if systemctl is-active --quiet lucidstack-agent; then
	say "Agent LucidStack démarré. Le serveur apparaîtra dans l'application d'ici une minute."
	say "Journaux : journalctl -u lucidstack-agent -f"
else
	journalctl -u lucidstack-agent -n 20 --no-pager >&2 || true
	die "le service n'a pas démarré (voir les journaux ci-dessus)."
fi
