#!/usr/bin/env bash
# PmaControl APT bootstrap. Run as root on a dedicated Debian/Ubuntu amd64 host.
# https://pmacontrol.com/fr/site/blog_post/installer-pmacontrol-debian-ubuntu-docker/
set -Eeuo pipefail

PMACTRL_KEY_FINGERPRINT='CAFDABF87717CC60291E68E4D56A0ADAAB5F97E2'
PMACTRL_REPOSITORY='https://repo.pmacontrol.com/debian'
bootstrap_tmp=''

fail() {
    printf 'PmaControl: %s\n' "$*" >&2
    exit 1
}

cleanup() {
    if [[ -n "$bootstrap_tmp" && -d "$bootstrap_tmp" ]]; then
        rm -rf -- "$bootstrap_tmp"
    fi
}

main() {
    [[ "$EUID" == 0 ]] || fail 'Run this installer as root (sudo bash).'
    [[ -r /etc/os-release ]] || fail 'Cannot identify the operating system.'
    # shellcheck source=/dev/null
    . /etc/os-release
    case "${ID:-}:${VERSION_CODENAME:-}" in
        debian:bookworm|debian:trixie|ubuntu:noble|ubuntu:resolute) ;;
        *) fail "Unsupported operating system: ${ID:-unknown}:${VERSION_CODENAME:-unknown}." ;;
    esac
    command -v dpkg >/dev/null || fail 'dpkg is required.'
    [[ "$(dpkg --print-architecture)" == amd64 ]] || fail 'Only amd64 packages are supported.'
    command -v apt-get >/dev/null || fail 'apt-get is required.'

    local installed_version
    if installed_version=$(dpkg-query -W -f='${db:Status-Status} ${Version}' pmacontrol 2>/dev/null); then
        if [[ "$installed_version" == installed\ * ]]; then
            printf 'PmaControl is already installed (%s). Use APT for upgrades.\n' "${installed_version#installed }"
            return
        fi
    fi

    local source_line source_file key_file key_info primary_count=0 awaiting_fingerprint=0 fingerprint=''
    local -a fields
    source_file='/etc/apt/sources.list.d/pmacontrol.list'
    key_file='/etc/apt/keyrings/pmacontrol-archive-keyring.gpg'
    source_line="deb [arch=amd64 signed-by=$key_file] $PMACTRL_REPOSITORY $VERSION_CODENAME main"
    if [[ -e "$source_file" || -L "$source_file" ]]; then
        [[ -f "$source_file" && ! -L "$source_file" ]] || fail "Cannot replace $source_file."
        [[ "$(<"$source_file")" == "$source_line" ]] || fail "An existing custom repository file needs review: $source_file."
    fi
    [[ ! -L "$key_file" ]] || fail "Cannot replace symlink $key_file."

    export DEBIAN_FRONTEND=noninteractive
    apt-get update
    apt-get install -y ca-certificates curl gnupg

    bootstrap_tmp=$(mktemp -d /tmp/pmacontrol-bootstrap.XXXXXX)
    trap cleanup EXIT
    install -d -m 0700 "$bootstrap_tmp/gnupg"
    curl -q -fsSL --retry 3 --connect-timeout 15 --max-time 120 \
        "$PMACTRL_REPOSITORY/pmacontrol-archive-keyring.gpg" \
        -o "$bootstrap_tmp/keyring.gpg"
    key_info=$(gpg --batch --no-options --homedir "$bootstrap_tmp/gnupg" \
        --with-colons --show-keys --fingerprint "$bootstrap_tmp/keyring.gpg") \
        || fail 'Cannot inspect the repository signing key.'
    while IFS=: read -r -a fields; do
        case "${fields[0]:-}" in
            pub) primary_count=$((primary_count + 1)); awaiting_fingerprint=1 ;;
            fpr)
                if [[ "$awaiting_fingerprint" == 1 ]]; then
                    fingerprint="${fields[9]:-}"
                    awaiting_fingerprint=0
                fi
                ;;
        esac
    done <<< "$key_info"
    [[ "$primary_count" == 1 && "$fingerprint" == "$PMACTRL_KEY_FINGERPRINT" ]] \
        || fail 'Unexpected repository signing key. Confirm any key rotation with PmaControl.'

    install -d -m 0755 /etc/apt/keyrings /etc/apt/sources.list.d
    install -m 0644 "$bootstrap_tmp/keyring.gpg" "$key_file"
    printf '%s\n' "$source_line" > "$bootstrap_tmp/pmacontrol.list"
    install -m 0644 "$bootstrap_tmp/pmacontrol.list" "$source_file"
    apt-get update
    apt-cache policy pmacontrol
    apt-get -s install pmacontrol
    apt-get install -y pmacontrol
    printf '\nPmaControl package: '
    dpkg-query -W -f='${Version}\n' pmacontrol
    printf '%s\n' 'Administrator credentials: /root/pmacontrol-install.env (root only).' \
        'Follow the guide for HTTPS, first login and collection checks.'
}

# Keep execution last: a truncated curl response must not start installation
# while the function definitions are still incomplete.
main "$@"
