#!/usr/bin/env bash
#
# MZPanel agent installer.
# Generated by https://api.mzpanel.com/v1/install.sh — do not edit by hand.
#
set -euo pipefail

API_URL="https://api.mzpanel.com"
BIN_DIR="/usr/local/bin"
CONFIG_DIR="/etc/mzagent"
LOG_DIR="/var/log/mzagent"
INSTALL_TOKEN="${INSTALL_TOKEN:-}"
SERVER_NAME="${SERVER_NAME:-}"

c_red()   { printf '\033[31m%s\033[0m\n' "$*"; }
c_green() { printf '\033[32m%s\033[0m\n' "$*"; }
c_blue()  { printf '\033[34m%s\033[0m\n' "$*"; }
fail()    { c_red "[mzagent] $*"; exit 1; }
# Best-effort progress relay so the web can follow a command-path install
# (POST a milestone line). Backgrounded + 3s timeout — NEVER blocks/breaks.
_progress()      { [ -n "$INSTALL_TOKEN" ] && curl -fsS -m 3 -X POST --data "$1" "$API_URL/v1/install-progress/$INSTALL_TOKEN" >/dev/null 2>&1 & }
_progress_done() { [ -n "$INSTALL_TOKEN" ] && curl -fsS -m 5 -X POST "$API_URL/v1/install-progress/$INSTALL_TOKEN?done=1" >/dev/null 2>&1 || true; }

[ "$(id -u)" -eq 0 ] || fail "Run as root (systemd unit install needs it)."
[ -n "$INSTALL_TOKEN" ] || fail "Missing INSTALL_TOKEN env. Usage:
  curl -fsSL $API_URL/v1/install.sh | INSTALL_TOKEN=<token> bash"

# ── 0. Already enrolled? ───────────────────────────────────────────────
# If the agent is already installed AND running, don't tear down a working
# enrolment with a (usually already-consumed) token — that just fails at the
# register step and leaves the box looking broken. Upgrades go through
# `mzagent self-update` / the panel's Update button. FORCE=1 re-enrols anyway.
if [ -z "${FORCE:-}" ] && [ -f "$CONFIG_DIR/config.json" ] && systemctl is-active --quiet mzagent.service 2>/dev/null; then
  c_green "[mzagent] already installed and running on this host — nothing to do."
  c_blue  "         upgrade:   mzagent self-update   (or the panel's Update button)"
  c_blue  "         re-enrol:  re-run with FORCE=1 and a fresh install token"
  exit 0
fi

# ── 1. Detect platform ─────────────────────────────────────────────────
case "$(uname -s)" in
  Linux) ;;
  *) fail "Unsupported OS: $(uname -s). MZPanel agent currently supports Linux only." ;;
esac

case "$(uname -m)" in
  x86_64|amd64) ARCH=amd64 ;;
  aarch64|arm64) ARCH=arm64 ;;
  *) fail "Unsupported arch: $(uname -m). Expected x86_64 or aarch64." ;;
esac
c_blue "[mzagent] platform: linux-$ARCH"

# ── 2. Required tools ──────────────────────────────────────────────────
for cmd in curl systemctl; do
  command -v "$cmd" >/dev/null || fail "Missing required tool: $cmd"
done

# ── 3. Download binary ─────────────────────────────────────────────────
BIN_URL="$API_URL/v1/agent/binary/mzagent-linux-$ARCH"
TMP_BIN="$(mktemp -t mzagent.XXXXXX)"
trap 'rm -f "$TMP_BIN"' EXIT

c_blue "[mzagent] downloading $BIN_URL ..."
HTTP_STATUS=$(curl -fsSL --retry 3 --retry-delay 2 --retry-connrefused -w '%{http_code}' -o "$TMP_BIN" "$BIN_URL" || true)
if [ ! -s "$TMP_BIN" ] || [ "$HTTP_STATUS" != "200" ]; then
  fail "Download failed (status $HTTP_STATUS). Check outbound HTTPS to $API_URL."
fi

# Stage into $BIN_DIR (an exec filesystem — $TMP_BIN may sit on a noexec /tmp)
# and sanity-run it BEFORE swapping it into place: a truncated download or a
# wrong-arch binary must not become /usr/local/bin/mzagent, and a bad download
# must not clobber an existing good binary. (TLS is the integrity boundary for
# the initial install; post-install upgrades are sha256- and signature-verified
# by `mzagent self-update`.)
mkdir -p "$BIN_DIR"
install -m 0755 "$TMP_BIN" "$BIN_DIR/mzagent.new"
if ! "$BIN_DIR/mzagent.new" version >/dev/null 2>&1; then
  rm -f "$BIN_DIR/mzagent.new"
  fail "Downloaded binary will not run here (corrupt download or wrong architecture for $ARCH)."
fi
mv -f "$BIN_DIR/mzagent.new" "$BIN_DIR/mzagent"
c_green "[mzagent] binary installed at $BIN_DIR/mzagent ($("$BIN_DIR/mzagent" version 2>/dev/null | head -1))"

# ── 3b. (No Bash mz CLI) ───────────────────────────────────────────────
# The agent is native-only (docs/72): the on-box engine is compiled into the
# mzagent binary, so there is NO Bash `mz` CLI to download or symlink. New
# boxes are zero-Bash from the start.

# ── 4. Prep state dirs ─────────────────────────────────────────────────
mkdir -p "$CONFIG_DIR" "$LOG_DIR"
chmod 0700 "$CONFIG_DIR"

# ── 5. Register with API ───────────────────────────────────────────────
REG_ARGS=(register --api "$API_URL" --token "$INSTALL_TOKEN" --config "$CONFIG_DIR/config.json")
[ -n "$SERVER_NAME" ] && REG_ARGS+=(--name "$SERVER_NAME")

c_blue "[mzagent] registering with $API_URL ..."
if ! "$BIN_DIR/mzagent" "${REG_ARGS[@]}"; then
  fail "Registration failed. The install token may be invalid, expired, or already used."
fi

# ── 6. systemd unit ────────────────────────────────────────────────────
cat > /etc/systemd/system/mzagent.service <<UNIT
[Unit]
Description=MZPanel customer-VPS agent
Documentation=https://mzpanel.com
After=network-online.target
Wants=network-online.target

[Service]
Type=simple
User=root
ExecStart=$BIN_DIR/mzagent run --config $CONFIG_DIR/config.json
Restart=always
RestartSec=5
LimitNOFILE=65536
# The agent's native engine creates system users, writes /etc/nginx,
# /home/<site>, /etc/letsencrypt, /var/log, etc. as root. A read-only system
# sandbox (ProtectSystem=strict) makes every mutating command (site
# create/delete, SSL, PHP switch) fail with "cannot lock /etc/passwd" /
# "Read-only file system", so it is NOT used here.
PrivateTmp=true
StandardOutput=journal
StandardError=journal
SyslogIdentifier=mzagent

[Install]
WantedBy=multi-user.target
UNIT

systemctl daemon-reload
systemctl enable mzagent.service >/dev/null
systemctl restart mzagent.service

# Wait for the service to come up AND, ideally, dial the control plane. The
# agent logs "[ws] welcomed" once the WSS handshake + hello round-trips, so a
# short poll on the journal is a much better "it actually worked" signal than a
# bare is-active (which only means "not crashed"). Not connecting in time is a
# warning, not a failure — it may still be dialing behind a slow link.
CONNECTED=""
for _ in 1 2 3 4 5 6 7 8 9 10 11 12; do
  systemctl is-active --quiet mzagent.service || break
  if journalctl -u mzagent --since "-60s" 2>/dev/null | grep -qiE "welcomed|ws.*connected"; then
    CONNECTED=1; break
  fi
  sleep 1
done

if ! systemctl is-active --quiet mzagent.service; then
  c_red    "[mzagent] ✗ service failed to start"
  c_red    "         journalctl -u mzagent -n 50  — for details"
  exit 1
fi
if [ -n "$CONNECTED" ]; then
  c_green "[mzagent] ✓ installed, running, and connected to the control plane"
else
  c_green "[mzagent] ✓ installed and running"
  c_blue  "         still dialing the control plane — it should appear online in the panel shortly"
fi
c_blue   "         logs:   journalctl -u mzagent -f"
c_blue   "         config: $CONFIG_DIR/config.json"

# ── 7. Web-stack provisioning (handled from the panel, NOT here) ───────
# Native-only (docs/72): the legacy `mz` CLI that ran the LEMP provisioner
# (multi/install.sh) + composer extras (docker / phpMyAdmin) has been removed,
# so the installer's ONLY job is to get the agent online (download → register →
# run). Day-0 provisioning (nginx · php · mariadb · redis) and add-ons
# (postgres / docker / phpMyAdmin) are driven from the panel once the agent is
# connected, over the agent's native engine — see the installer-native-only
# follow-up. Keeping install minimal also means a re-run is cheap + safe.
c_blue "[mzagent] agent is online. Set up the web stack from the panel → Server → Extensions."

# ── 8. Signal completion → clears the fleet "Installing" badge. ────────
# Scope: the installer is done when the AGENT is online (its only job now), so
# the milestone reads "agent online" rather than over-claiming a ready stack.
_progress "✓ Agent online"
_progress_done
