#!/usr/bin/env bash
# mugnsoft-install.sh - download, verify, configure, install and start one Mugnsoft
# component as a Linux service (systemd, Upstart or SysV), in one command.
#
# Usage (as root):
#   bash mugnsoft-install.sh <component> [options]
#
# Components:
#   webserver        the Webserver (needs --license)
#   monitor          a monitor probe
#   integrator       an Integrator (needs --license)
#   sentinel         a Sentinel Agent (discovery_agent)
#   webserver-front  a Webserver-Front (needs --front-pki)
#
# Options:
#   --version <x.y.z>          release to install (default 4.2.0)
#   --dir <path>               parent folder (default /opt/mugnsoft); the component
#                              is installed in <path>/<package folder>
#   --package <file>           install this package (.tar.gz or .zip) instead of downloading it
#   --no-verify                skip the GPG signature check (not recommended)
#   webserver, integrator:
#   --license <file>           the license_MNS.dat received from Mugnsoft
#   webserver:
#   --api-port <port>          API port (default 8050)
#   --web-port <port>          web UI port (default 9090)
#   monitor, integrator, sentinel:
#   --webserver <host:port>    the Webserver API address (host:API port, e.g. 10.0.0.5:8050)
#   --shared-internal <value>  the "_sharedInternal" value of the Webserver's webserver.json
#   --name <name>              component name (default <component>-<short hostname>)
#   --port <port>              component API port (default: the package's)
#   --location <text>          location shown on the Webserver (monitor, sentinel)
#   --advertise <ip>           monitor only: IP address the Webserver must use to
#                              reach this probe (default: auto-detected)
#   --webserver-url <url>      integrator only: Webserver web UI URL used in
#                              notifications (default https://<webserver host>:9090)
#   webserver-front:
#   --front-pki <dir>          the front/ folder produced by mugnsoft-front-pki.sh
#
# Examples (replace the values with yours):
#   bash mugnsoft-install.sh webserver --license ./license_MNS.dat
#   bash mugnsoft-install.sh monitor --webserver 10.0.0.5:8050 --shared-internal 3f9c...e1 --location Paris

set -euo pipefail

VERSION=4.2.0 DIR=/opt/mugnsoft PACKAGE="" VERIFY=1
LICENSE="" API_PORT="" WEB_PORT="" WEBSERVER="" SHARED="" NAME="" PORT="" LOCATION=""
ADVERTISE="" WEBSERVER_URL="" FRONT_PKI=""
BASE_URL="https://mugnsoft.com/bin"

die()  { echo "ERROR: $*" >&2; exit 1; }
info() { echo "==> $*"; }
usage() { sed -n '2,/^$/p' "$0" | sed 's/^# \{0,1\}//'; exit "${1:-0}"; }
fetch() { if command -v curl >/dev/null; then curl -fsSL -o "$2" "$1"; else wget -q -O "$2" "$1"; fi; }

[ $# -ge 1 ] || usage 1
COMPONENT="$1"; shift
case "$COMPONENT" in -h|--help) usage 0 ;; esac
while [ $# -gt 0 ]; do
  [ $# -ge 2 ] || [ "$1" = "--no-verify" ] || die "option $1 needs a value"
  case "$1" in
    --version) VERSION="$2" ;;          --dir) DIR="$2" ;;
    --package) PACKAGE="$2" ;;          --no-verify) VERIFY=0; shift; continue ;;
    --license) LICENSE="$2" ;;          --api-port) API_PORT="$2" ;;
    --web-port) WEB_PORT="$2" ;;        --webserver) WEBSERVER="$2" ;;
    --shared-internal) SHARED="$2" ;;   --name) NAME="$2" ;;
    --port) PORT="$2" ;;                --location) LOCATION="$2" ;;
    --advertise) ADVERTISE="$2" ;;      --webserver-url) WEBSERVER_URL="$2" ;;
    --front-pki) FRONT_PKI="$2" ;;
    *) die "unknown option: $1 (see --help)" ;;
  esac
  shift 2
done

# component -> package name, folder, binary, config file
case "$COMPONENT" in
  webserver)       PKG=webserver;        BIN=webserver ;;
  webserver-front) PKG=webserver-front;  BIN=webserver-front ;;
  monitor)         PKG=monitor;          BIN=monitor ;;
  integrator)      PKG=integrator;       BIN=integrator ;;
  sentinel|discovery_agent) COMPONENT=sentinel; PKG=discovery_agent; BIN=discovery_agent ;;
  *) die "unknown component '$COMPONENT' (webserver, monitor, integrator, sentinel, webserver-front)" ;;
esac
CONF="$BIN.json"
TARGET="$DIR/$PKG"

# ---- checks, before anything is written -------------------------------------
[ "$(id -u)" -eq 0 ] || die "run as root (sudo bash $0 ...)"
for c in tar gzip sed grep; do command -v "$c" >/dev/null || die "'$c' is required: install it first"; done
num() { [[ "$2" =~ ^[0-9]+$ ]] && [ "$2" -ge 1 ] && [ "$2" -le 65535 ] || die "$1 must be a port number, got '$2'"; }
safe() { [[ "$2" =~ ^[A-Za-z0-9._:/@+=,\ -]*$ ]] || die "$1 contains characters that are not allowed: '$2'"; }
[ -n "$API_PORT" ] && num --api-port "$API_PORT"
[ -n "$WEB_PORT" ] && num --web-port "$WEB_PORT"
[ -n "$PORT" ] && num --port "$PORT"
case "$COMPONENT" in
  webserver|integrator)
    [ -n "$LICENSE" ] || die "--license <license_MNS.dat> is required for the $COMPONENT"
    [ -f "$LICENSE" ] || die "license file not found: $LICENSE" ;;
esac
case "$COMPONENT" in
  monitor|integrator|sentinel)
    [ -n "$WEBSERVER" ] || die "--webserver <host:port> is required (the Webserver API address)"
    [[ "$WEBSERVER" =~ ^[A-Za-z0-9.-]+:[0-9]+$ ]] || die "--webserver must look like host:port, e.g. 10.0.0.5:8050"
    [ -n "$SHARED" ] || die "--shared-internal is required: copy \"_sharedInternal\" from the Webserver's webserver.json"
    [[ "$SHARED" =~ ^[A-Za-z0-9._~!#%^*+=-]+$ ]] || die "--shared-internal holds unexpected characters"
    NAME="${NAME:-$COMPONENT-$(hostname -s)}"
    [[ "$NAME" =~ ^[A-Za-z0-9-]+$ ]] || die "--name may hold letters, digits and hyphens only: '$NAME'"
    safe --location "$LOCATION"; safe --advertise "$ADVERTISE"; safe --webserver-url "$WEBSERVER_URL" ;;
  webserver-front)
    [ -n "$FRONT_PKI" ] || die "--front-pki <dir> is required: the front/ folder made by mugnsoft-front-pki.sh"
    [ -f "$FRONT_PKI/config/ssl/certificates/webserver.pem" ] && [ -f "$FRONT_PKI/webserver-front.json" ] \
      || die "$FRONT_PKI does not look like the front/ folder of mugnsoft-front-pki.sh" ;;
esac
[ -e "$TARGET" ] && die "$TARGET already exists: this script installs, it does not upgrade (see the Upgrade section of the docs)"

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

# ---- 1. get the package -----------------------------------------------------
FILE="mugnsoft-$PKG-$VERSION.linux-amd64.tar.gz"
if [ -z "$PACKAGE" ]; then
  info "Downloading $FILE"
  fetch "$BASE_URL/$VERSION/$FILE" "$WORK/$FILE" || die "download failed: $BASE_URL/$VERSION/$FILE"
  [ "$VERIFY" -eq 1 ] && { fetch "$BASE_URL/$VERSION/$FILE.sig" "$WORK/$FILE.sig" || die "signature download failed"; }
  PACKAGE="$WORK/$FILE"
else
  [ -f "$PACKAGE" ] || die "package not found: $PACKAGE"
  [ "$VERIFY" -eq 1 ] && { [ -f "$PACKAGE.sig" ] || die "no $PACKAGE.sig next to the package: download it too, or pass --no-verify"; cp "$PACKAGE.sig" "$WORK/pkg.sig"; }
fi

# ---- 2. verify its signature -------------------------------------------------
if [ "$VERIFY" -eq 1 ]; then
  command -v gpg >/dev/null || die "'gpg' is required to verify the package (or pass --no-verify)"
  info "Verifying the GPG signature"
  SIG="$PACKAGE.sig"; [ -f "$WORK/pkg.sig" ] && SIG="$WORK/pkg.sig"
  export GNUPGHOME="$WORK/gnupg"; mkdir -m 700 "$GNUPGHOME"
  fetch "$BASE_URL/Mugnsoft_public.key" "$WORK/key" || die "could not download Mugnsoft_public.key"
  gpg --batch --quiet --import "$WORK/key" 2>/dev/null
  gpg --batch --verify "$SIG" "$PACKAGE" 2>/dev/null || die "SIGNATURE CHECK FAILED for $PACKAGE: do not install it"
  info "Signature OK"
else
  echo "WARNING: signature not verified (--no-verify)" >&2
fi

# ---- 3. extract ---------------------------------------------------------------
info "Extracting to $TARGET"
mkdir -p "$DIR"
mkdir "$WORK/x"
case "$PACKAGE" in
  *.zip) command -v unzip >/dev/null || die "'unzip' is required for a .zip package"; unzip -q "$PACKAGE" -d "$WORK/x" ;;
  *)     tar -xzf "$PACKAGE" -C "$WORK/x" ;;
esac
[ -d "$WORK/x/$PKG" ] || die "unexpected package layout: no $PKG/ folder inside"
mv "$WORK/x/$PKG" "$TARGET"
cd "$TARGET"
chmod +x "./$BIN"
[ -d exec ] && find exec -maxdepth 1 -type f \( -name ffmpeg -o -name harx \) -exec chmod +x {} +
[ -d exec/jdk/bin ] && chmod +x exec/jdk/bin/*
[ -f "$CONF" ] || die "$CONF missing from the package"

# set "key": "value" in a flat JSON file, adding the key when it is absent
setkey() {
  local key="$1" val="$2"
  if grep -q "\"$key\"[[:space:]]*:" "$CONF"; then
    sed -i -E "s|(\"$key\"[[:space:]]*:[[:space:]]*)\"[^\"]*\"|\1\"$val\"|" "$CONF"
  else
    sed -i -E "0,/\{/s|\{|{\n    \"$key\": \"$val\",|" "$CONF"
  fi
}

# ---- 4. configure ---------------------------------------------------------------
info "Configuring $CONF"
case "$COMPONENT" in
  webserver)
    cp "$LICENSE" ./license_MNS.dat
    [ -n "$API_PORT" ] && setkey PortAPI "$API_PORT"
    [ -n "$WEB_PORT" ] && setkey PortWEB "$WEB_PORT" ;;
  webserver-front)
    cp -r "$FRONT_PKI"/. . ;;
  *)
    setkey _sharedInternal "$SHARED"
    setkey webserver "$WEBSERVER"
    setkey name "$NAME"
    setkey description "Mugnsoft $COMPONENT on $(hostname -s)"
    [ -n "$PORT" ] && setkey port "$PORT"
    [ "$COMPONENT" != integrator ] && setkey location "$LOCATION"
    [ "$COMPONENT" = monitor ] && [ -n "$ADVERTISE" ] && setkey ip "$ADVERTISE"
    [ "$COMPONENT" = sentinel ] && setkey discoFSGMonitored "/"
    if [ "$COMPONENT" = integrator ]; then
      cp "$LICENSE" ./license_MNS.dat
      setkey os linux
      setkey webserverUrl "${WEBSERVER_URL:-https://${WEBSERVER%%:*}:9090}"
    fi ;;
esac

# ---- 5. install the service (components register with the Webserver here) ------
info "Installing the service"
OUT=$("./$BIN" install 2>&1) || true
echo "$OUT" | grep -v '^time=' || true
# "Service '<name>' installed." - not "Service '<name>' not installed."
echo "$OUT" | grep -q "Service '[^']*' installed\." || die "the service was not installed - read the messages above, fix, then run: cd $TARGET && ./$BIN install"

# ---- 6. start it ----------------------------------------------------------------
info "Starting the service"
"./$BIN" start 2>&1 | grep -v '^time=' || true

echo
case "$COMPONENT" in
  webserver)
    SI=$(sed -n -E 's/.*"_sharedInternal"[[:space:]]*:[[:space:]]*"([^"]*)".*/\1/p' "$CONF")
    echo "Webserver installed in $TARGET"
    echo "  Web UI:   https://<this host>:$(sed -n -E 's/.*"PortWEB"[[:space:]]*:[[:space:]]*"([^"]*)".*/\1/p' "$CONF")/   (admin / admin, change it at first login)"
    echo "  API:      <this host>:$(sed -n -E 's/.*"PortAPI"[[:space:]]*:[[:space:]]*"([^"]*)".*/\1/p' "$CONF")   -> the --webserver value for the components"
    echo "  _sharedInternal (pass it to every component as --shared-internal):"
    echo "    $SI" ;;
  webserver-front)
    echo "Webserver-Front installed in $TARGET - now configure ADMIN (see the Webserver-Front install page)." ;;
  *)
    echo "$COMPONENT '$NAME' installed in $TARGET and registered with $WEBSERVER."
    echo "Last step: on the Webserver, open Settings (gear) > Components, right-click '$NAME' and choose enable server." ;;
esac
