#!/bin/bash
# ==============================================================================================
# ================================= Unraid API Key Renewal ====================================
# ==============================================================================================
#
# PURPOSE
# ─────────────────────────────────────────────────────────────────────────────
# Creates/overwrites the Varaverk API key in the unraid-api service registry at
# array start. The registry is ephemeral — OS updates and service restarts clear
# it. This script re-registers the key every boot so Varaverk's enhanced
# monitoring self-heals without manual intervention.
#
# Also updates HOST*_UNRAID_API_KEY in the local host conf so the partnership
# page always reflects the live key value.
#
# ==============================================================================================
# DESIGN PRINCIPLES
# ==============================================================================================
#
# Self-Healing at Boot
#   The unraid-api registry is ephemeral — OS updates and service restarts clear
#   it without warning. Running at every array start means the key is always
#   present after boot without any manual intervention.
#
# Conf Stays Current
#   HOST*_UNRAID_API_KEY in the local host conf is updated after every renewal.
#   The partnership page reads the conf — it always reflects the live key value
#   without a separate sync step.
#
# ==============================================================================================
# OPERATIONAL MODEL
# ==============================================================================================
#
#   1. Check whether a Varaverk key already exists in the unraid-api registry
#   2. Create or overwrite it — the registry is ephemeral, so re-registering is the norm
#   3. Write the resulting key into this host's conf, replacing any previous value
#   4. Report whether the key was created, refreshed, or unchanged
#
# Runs at array start. The registry does not survive OS updates or an unraid-api restart,
# which is why this re-registers unconditionally rather than only when the key is missing —
# a key present in the conf but absent from the registry is the exact failure it repairs.
#
# ==============================================================================================
# OPERATIONAL SAFEGUARDS
# ==============================================================================================
#
#   acquire_lock    — prevents concurrent renewal attempts at boot
#   detect_hosts()  — sets MY_ID to derive the correct conf var name
#   Conf file check — aborts before any writes if the host conf is missing
#   dry-run mode    — shows what would happen without touching anything
#
# ==============================================================================================
# CONFIGURATION
# ==============================================================================================
#
# host*.conf
#
#   HOST*_UNRAID_API_KEY
#       Written by this script every array start. Read by the plugin's PHP for enhanced
#       monitoring. Treated as output, not input — an existing value is always replaced,
#       because a conf value that no longer matches the registry is precisely the broken
#       state this exists to fix.
#
# Platform-owned:
#
#   The unraid-api service registry — ephemeral, cleared by OS updates and service restarts.
#
# ==============================================================================================
# RUNTIME MODES
# ==============================================================================================
#
# unraid_api_key_renew.sh
#     Renew the key. Silent on success.
#
# unraid_api_key_renew.sh --dry-run
#     Show what would happen — no changes made.
#
# unraid_api_key_renew.sh --log
#     Verbose output.
#
# ==============================================================================================

SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"

source "$SCRIPT_DIR/../../../load_config.sh"

parse_args "$@"

# Rewrites the API key into host*.conf and registers it with the unraid-api service.
[[ "$EUID" -ne 0 ]] && { error "Must be run as root"; exit 1; }

acquire_lock
detect_hosts

# ──────────────────────────────────────────────────────────────────────────────
CONF_FILE="$SCRIPT_DIR/../../../Configurations/${MY_ID,,}.conf"
VAR_NAME="${MY_ID}_UNRAID_API_KEY"

# Key name: "Varaverk <hostname>" stripping any unraid- prefix
# Space separator — unRAID API only allows letters, numbers, and spaces
HOSTNAME_SUFFIX=$(hostname -s 2>/dev/null | sed 's/^[Uu][Nn][Rr][Aa][Ii][Dd]-//' || hostname -s)
KEY_NAME="Varaverk ${HOSTNAME_SUFFIX}"

log "$ICON_GEAR Conf file: $CONF_FILE"
log "$ICON_GEAR Key var:   $VAR_NAME"
log "$ICON_GEAR Key name:  $KEY_NAME"

if [[ ! -f "$CONF_FILE" ]]; then
    error "Conf file not found: $CONF_FILE"
    exit 1
fi

if [[ "$DRY_RUN" == true ]]; then
    warn "DRY RUN — would check registry for $KEY_NAME, renew only if missing"
    exit 0
fi

# ──────────────────────────────────────────────────────────────────────────────
# Check if key already exists in the unraid-api registry before creating.
# --overwrite generates a new key value every time, invalidating the old one.
# Only renew if the registry has lost it.
#
# Timeout was 5s — measured live 2026-07-19 at 2.2-2.7s baseline latency for this
# exact command with the system idle, leaving almost no margin. Any load spike
# (confirmed correlated with resource_watchdog "pressure escalating" events in the
# same log) pushed it past 5s, killing the lookup — the script then couldn't tell
# "timed out" from "genuinely not in the registry" and fell through to the create
# path, logging a false "API key renewed (registry had lost it)" even though the
# on-disk key file's timestamp never actually changed. 15s gives real headroom.
log "Checking unraid-api registry for $KEY_NAME..."
EXISTING=$(timeout 15 /usr/local/sbin/unraid-api apikey --name "$KEY_NAME" --json </dev/null 2>/dev/null)
KEY=$(echo "$EXISTING" | jq -r '.key // empty' 2>/dev/null)

if [[ -n "$KEY" ]]; then
    PREVIEW="${KEY:0:8}...${KEY: -4}"
    # Always sync registry key → conf, even if the key was already there.
    # Conf gets wiped on git pull / conf regeneration without touching the registry.
    CONF_HAS_KEY=$(grep -oP "(?<=^\s*${VAR_NAME}=\")[^\"]*" "$CONF_FILE" 2>/dev/null || true)
    if [[ "$CONF_HAS_KEY" == "$KEY" ]]; then
        echo "API key valid ✅ — $VAR_NAME = $PREVIEW"
        log "Key in registry and conf — no action needed"
        exit 0
    fi
    log "Key in registry but conf is stale — syncing..."
    if grep -q "^\s*${VAR_NAME}\s*=" "$CONF_FILE"; then
        sed -i "s|^\(\s*${VAR_NAME}\s*=\s*\)\"[^\"]*\"|\1\"${KEY}\"|" "$CONF_FILE"
    else
        echo "    ${VAR_NAME}=\"${KEY}\"" >> "$CONF_FILE"
    fi
    echo "API key synced to conf ✅ — $VAR_NAME = $PREVIEW"
    exit 0
fi

log "Key not found in registry — creating new key..."

RAW=$(timeout 10 /usr/local/sbin/unraid-api apikey \
    --name "$KEY_NAME" --create --overwrite \
    --description "Varaverk plugin" --roles ADMIN --json </dev/null 2>&1)

if [[ -z "$RAW" ]]; then
    error "unraid-api returned no output"
    exit 1
fi

KEY=$(echo "$RAW" | jq -r '.key // empty' 2>/dev/null)
if [[ -z "$KEY" ]]; then
    error "No key in unraid-api response: ${RAW:0:200}"
    exit 1
fi

# ──────────────────────────────────────────────────────────────────────────────
if grep -q "^\s*${VAR_NAME}\s*=" "$CONF_FILE"; then
    sed -i "s|^\(\s*${VAR_NAME}\s*=\s*\)\"[^\"]*\"|\1\"${KEY}\"|" "$CONF_FILE"
else
    echo "    ${VAR_NAME}=\"${KEY}\"" >> "$CONF_FILE"
fi

PREVIEW="${KEY:0:8}...${KEY: -4}"
log "Writing new key to: $CONF_FILE"
warn "API key renewed ✅ — $VAR_NAME = $PREVIEW  (registry had lost it)"

# ── Push renewed key into each partner's OWN conf ─────────────────────────────
# Each host's conf is its complete keychest — no cross-host conf files needed.
# SSH_KEY is set by detect_hosts() — this server's outbound private key.
if [[ -z "$SSH_KEY" ]]; then
    log "No SSH key configured — skipping partner push"
    exit 0
fi

for host_var in $(compgen -v | grep -E '^HOST[0-9]+$'); do
    partner_host="${!host_var}"
    [[ -z "$partner_host" ]] && continue
    [[ "${host_var,,}" == "${MY_ID,,}" ]] && continue

    partner_slot="${host_var,,}"   # e.g. host2
    partner_ip=$(resolve_tailscale_ip "$partner_host" 2>/dev/null || true)
    [[ -z "$partner_ip" ]] && { log "Cannot resolve IP for $partner_host — skipping"; continue; }

    # Target is the partner's OWN conf on their machine
    partner_conf="/boot/config/plugins/varaverk/Configurations/${partner_slot}.conf"
    tmp=$(mktemp /tmp/vv_kp_XXXXXX.sh)
    remote="/tmp/vv_kp_${RANDOM}.sh"
    chmod 700 "$tmp"

    # Key stays in the temp file — never appears in SSH command args
    cat > "$tmp" <<PUSHSCRIPT
#!/bin/sh
target='${partner_conf}'
if grep -q "\b${VAR_NAME}\b" "\$target" 2>/dev/null; then
  sed -i 's|^\(\\s*${VAR_NAME}\\s*=\\s*\)"[^"]*"|\1"${KEY}"|' "\$target"
else
  printf '    ${VAR_NAME}="%s"\n' '${KEY}' >> "\$target"
fi
echo ok
PUSHSCRIPT

    if timeout 10 scp -i "$SSH_KEY" -o ConnectTimeout=10 -o BatchMode=yes \
        -o StrictHostKeyChecking=no "$tmp" "root@${partner_ip}:${remote}" 2>/dev/null; then
        if timeout 10 ssh -i "$SSH_KEY" -o ConnectTimeout=10 -o BatchMode=yes \
            -o StrictHostKeyChecking=no "root@${partner_ip}" \
            "bash '${remote}'; rc=\$?; rm -f '${remote}'; exit \$rc" 2>/dev/null | grep -q ok; then
            echo "Key pushed to $partner_host ✅"
        else
            warn "Key push to $partner_host failed — they can create their own copy"
        fi
    else
        warn "SCP to $partner_host failed — skipping"
    fi
    rm -f "$tmp"
done
