#!/usr/bin/env bash
#
# switch-firmware-train.sh
#
# Switches an OpenNMC board between the "production" and "development"
# firmware trains: installs the matching keyring, installs the latest
# .raucb bundle for that train, and reboots.
#
# Intended usage:
#   wget -qO- https://dl.netcubesystems.at/products/opennmc/software/switch-firmware-train.sh | bash
#
set -euo pipefail

# --------------------------------------------------------------------------
# Try to size the terminal before we print anything.
# (curl | bash usually still has a real tty on fd 1/2, just not on stdin)
# --------------------------------------------------------------------------
resize 2>/dev/null < /dev/tty || true

# --------------------------------------------------------------------------
# Config
# --------------------------------------------------------------------------
BASE_URL="https://dl.netcubesystems.at/products/opennmc/software"

KEYRING_URL_DEV="${BASE_URL}/keyrings/development.pem"
KEYRING_URL_PROD="${BASE_URL}/keyrings/production.pem"

FW_URL_DEV="${BASE_URL}/opennmc-development-latest.raucb"
FW_URL_PROD="${BASE_URL}/opennmc-production-latest.raucb"

KEYRING_PATH="/etc/rauc/keyring.pem"

# md5sum of /etc/rauc/keyring.pem for each train
MD5_DEV="d9db46cdb40413f4081650a72cda27fb"
MD5_PROD="b8fb3c2939ac058f4fc3d70f80e73dad"

# --------------------------------------------------------------------------
# Helpers
# --------------------------------------------------------------------------
BOX_WIDTH=70

# a BOX_WIDTH-long line of '=', built with builtins only
printf -v HR '%*s' "$BOX_WIDTH" ''
HR="${HR// /=}"

# a BOX_WIDTH-long line of '-', used as a lighter in-section separator
printf -v SEP '%*s' "$BOX_WIDTH" ''
SEP="${SEP// /-}"

center() {
    local text="$1"
    local len=${#text}
    local pad=$(( BOX_WIDTH - len ))
    (( pad < 0 )) && pad=0
    local left=$(( pad / 2 ))
    local right=$(( pad - left ))
    printf '%*s%s%*s\n' "$left" '' "$text" "$right" ''
}

header() {
    clear
    printf '%s\n' "$HR"
    center "$1"
    printf '%s\n' "$HR"
    echo
}

sep() {
    printf '%s\n' "$SEP"
}

info() {
    printf '  %s\n' "$1"
}

die() {
    printf '\n!!! %s\n' "$1" >&2
    exit 1
}

# Since stdin is consumed by "wget -qO- ... | bash" itself, all interactive
# prompts must read from the controlling terminal directly.
ask() {
    # ask "prompt text" varname
    local __prompt="$1"
    local __var="$2"
    local __reply

    if [ ! -e /dev/tty ]; then
        die "No controlling terminal available (/dev/tty) - cannot prompt interactively."
    fi

    read -r -p "$__prompt" __reply < /dev/tty
    printf -v "$__var" '%s' "$__reply"
}

current_train() {
    if [ ! -f "$KEYRING_PATH" ]; then
        echo "none"
        return
    fi

    local sum
    sum="$(md5sum "$KEYRING_PATH" 2>/dev/null | awk '{print $1}')"

    case "$sum" in
        "$MD5_DEV")  echo "development" ;;
        "$MD5_PROD") echo "production"  ;;
        *)           echo "unknown"     ;;
    esac
}

header "OpenNMC Firmware Train Switcher"
echo "This script needs root privileges to install the keyring/firmware and reboot."
echo "You may be prompted for your password below."
echo

if ! sudo -v < /dev/tty; then
    die "Could not obtain sudo privileges. Aborting."
fi

# keep the sudo timestamp alive for the duration of the script
(
    while true; do
        sudo -n true
        sleep 60
        kill -0 "$$" 2>/dev/null || exit
    done
) &
SUDO_KEEPALIVE_PID=$!
trap 'kill "$SUDO_KEEPALIVE_PID" >/dev/null 2>&1 || true' EXIT

header "OpenNMC Firmware Train Switcher"
info "Currently installed keyring corresponds to: $(current_train)"
echo

TRAIN=""
while true; do
    ask "Which firmware train do you want to switch to? [production/development]: " TRAIN
    case "${TRAIN,,}" in
        production|prod|p)
            TRAIN="production"
            break
            ;;
        development|dev|d)
            TRAIN="development"
            break
            ;;
        *)
            echo "Please answer 'production' or 'development'."
            ;;
    esac
done

if [ "$TRAIN" = "production" ]; then
    KEYRING_URL="$KEYRING_URL_PROD"
    FW_URL="$FW_URL_PROD"
else
    KEYRING_URL="$KEYRING_URL_DEV"
    FW_URL="$FW_URL_DEV"
fi

header "You selected: ${TRAIN}"
info "This will:"
info "  1. Install the ${TRAIN} keyring to ${KEYRING_PATH}"
info "  2. Install the latest ${TRAIN} firmware via rauc"
info "  3. Reboot the board immediately once installation succeeds"
sep
info "The board will be updated RIGHT NOW and WILL REBOOT when this finishes."
echo

CONFIRM=""
ask "Continue? [yes/NO]: " CONFIRM
case "$CONFIRM" in
    yes|Yes|YES|y|Y)
        ;;
    *)
        echo
        info "Aborted by user. No changes were made."
        exit 0
        ;;
esac

header "Installing ${TRAIN} keyring"
sudo wget "$KEYRING_URL" -O "$KEYRING_PATH"
echo
info "Keyring installed. md5sum: $(md5sum "$KEYRING_PATH" | awk '{print $1}')"

header "Installing ${TRAIN} firmware"
sudo rauc install "$FW_URL"

header "Rebooting"
info "Firmware installed successfully. Rebooting now..."
echo
sudo reboot
