#!/bin/bash
#
# air-generate-jwt-secret
#
# First-boot oneshot: generates a per-device JWT signing secret for cast-server.
# Runs before cast.service via air-jwt-secret.service.
#
# - Secret lives at /etc/air/jwt_secret (root:cast 0640 when the cast
#   account exists; root:root fallback only for broken/manual installs).
# - EnvironmentFile for cast.service is written to /etc/air/environment.
# - Both files are created atomically and only if missing (idempotent across reboots).
#
set -euo pipefail

SECRET_DIR="/etc/air"
SECRET_FILE="${SECRET_DIR}/jwt_secret"
ENV_FILE="${SECRET_DIR}/environment"

device_arch() {
    if [ -s /etc/air/arch ]; then
        tr -d '[:space:]' </etc/air/arch
    else
        uname -m
    fi
}

ensure_groups() {
    local group
    for group in "$@"; do
        if ! getent group "$group" >/dev/null 2>&1; then
            groupadd --system "$group" 2>/dev/null || true
        fi
    done
}

ensure_cast_account() {
    if ! getent group cast >/dev/null 2>&1; then
        groupadd --system cast 2>/dev/null || true
    fi
    if getent group cast >/dev/null 2>&1; then
        if ! id -u cast >/dev/null 2>&1; then
            useradd --system --gid cast --home-dir /var/lib/cast --shell /usr/sbin/nologin cast 2>/dev/null || true
        else
            usermod --home /var/lib/cast --shell /usr/sbin/nologin cast 2>/dev/null || true
        fi
        case "$(device_arch)" in
            x86_64|amd64)
                ensure_groups dialout video render
                usermod -aG dialout,video,render cast 2>/dev/null || true
                ;;
            *)
                ensure_groups dialout video
                usermod -aG dialout,video cast 2>/dev/null || true
                ;;
        esac
    fi
}

secret_group() {
    if getent group cast >/dev/null 2>&1; then
        printf '%s\n' cast
    else
        printf '%s\n' root
    fi
}

set_secret_perms() {
    local path="$1"
    chown "root:$(secret_group)" "$path"
    chmod 0640 "$path"
}

ensure_cast_account
install -d -m 0755 -o root -g root "${SECRET_DIR}"

if [ ! -s "${SECRET_FILE}" ]; then
    # 64 random bytes → base64 (88 chars, ~512 bits of entropy).
    umask 027
    TMP="$(mktemp "${SECRET_FILE}.XXXXXX")"
    trap 'rm -f "${TMP}"' EXIT
    head -c 64 /dev/urandom | base64 -w 0 > "${TMP}"
    # Ensure newline terminator for safety.
    printf '\n' >> "${TMP}"
    set_secret_perms "${TMP}"
    mv -f "${TMP}" "${SECRET_FILE}"
    trap - EXIT
fi

# Make sure perms/ownership are correct even if the file pre-existed.
set_secret_perms "${SECRET_FILE}"

# (Re)write the systemd EnvironmentFile only when needed. Two reasons NOT
# to clobber unconditionally:
#   1. Operators may have appended extra `AIR_*` vars (e.g. AIR_LOG_LEVEL,
#      AIR_LICENSE_API_URL override). Rewriting wipes them on every boot.
#   2. If the SECRET_FILE read transiently fails or returns empty, an
#      unconditional rewrite truncates ENV_FILE to `AIR_JWT_SECRET=\n` and
#      cast.service starts with no secret — silent fleet-wide outage.
SECRET_VALUE="$(tr -d '\n' < "${SECRET_FILE}")"
if [ -z "${SECRET_VALUE}" ]; then
    echo "air-generate-jwt-secret: FATAL — ${SECRET_FILE} read returned empty" >&2
    exit 1
fi

# Skip the rewrite if ENV_FILE already carries a non-trivially-short
# AIR_JWT_SECRET value (≥40 base64 chars ≈ 240 bits, well under our 88-char
# default but enough that we're confident it was produced by this script
# and not a placeholder). Operator edits to other lines are preserved.
NEEDS_WRITE=1
if [ -s "${ENV_FILE}" ] && grep -Eq '^AIR_JWT_SECRET=.{40,}' "${ENV_FILE}"; then
    NEEDS_WRITE=0
fi

if [ "${NEEDS_WRITE}" -eq 1 ]; then
    umask 027
    TMP_ENV="$(mktemp "${ENV_FILE}.XXXXXX")"
    trap 'rm -f "${TMP_ENV}"' EXIT
    if [ -s "${ENV_FILE}" ]; then
        # Preserve every line that is not an AIR_JWT_SECRET assignment, then
        # append the freshly-generated one. Idempotent across upgrades.
        grep -v '^AIR_JWT_SECRET=' "${ENV_FILE}" > "${TMP_ENV}" || true
    fi
    printf 'AIR_JWT_SECRET=%s\n' "${SECRET_VALUE}" >> "${TMP_ENV}"
    set_secret_perms "${TMP_ENV}"
    mv -f "${TMP_ENV}" "${ENV_FILE}"
    trap - EXIT
fi

# Always reassert ownership/mode in case a manual edit dropped them.
set_secret_perms "${ENV_FILE}"

# Verify the environment file was written successfully
if [ ! -s "${ENV_FILE}" ] || ! grep -Eq '^AIR_JWT_SECRET=.{40,}' "${ENV_FILE}"; then
    echo "air-generate-jwt-secret: FATAL — ${ENV_FILE} missing AIR_JWT_SECRET after write" >&2
    exit 1
fi

exit 0
