#!/usr/bin/bash
set -Eeuo pipefail
PATH=/usr/sbin:/usr/bin
export PATH

readonly state_dir=/var/lib/nabu-kernel-maintenance
readonly pending_dir=$state_dir/pending.d
readonly prepared_file=$state_dir/prepared-kernel

exec 9>/run/nabu-kernel-maintenance.lock
flock -n 9 || exit 0

if [[ -r /sys/firmware/devicetree/base/compatible ]] &&
   ! grep -aq 'xiaomi,nabu' /sys/firmware/devicetree/base/compatible; then
    exit 0
fi

declare -Ar selector=(
    [alpha]=senemos-nabu-kernel-alpha
    [stable]=senemos-nabu-kernel
    [mainline]=senemos-nabu-kernel-mainline-alpha
    [lts]=senemos-nabu-kernel-lts
)
declare -Ar core=(
    [alpha]=senemos-nabu-kernel-alpha
    [stable]=senemos-nabu-kernel
    [mainline]=senemos-nabu-kernel-mainline-alpha
    [lts]=senemos-nabu-kernel-lts
)

preferred=alpha
if [[ -r /etc/nabu/kernel.conf ]]; then
    configured=$(sed -nE 's/^preferred=(alpha|stable|mainline|lts)$/\1/p' /etc/nabu/kernel.conf | tail -n1)
    [[ -z $configured ]] || preferred=$configured
fi
if ! rpm -q "${selector[$preferred]}" >/dev/null 2>&1; then
    for candidate in alpha stable mainline lts; do
        if rpm -q "${selector[$candidate]}" >/dev/null 2>&1; then
            preferred=$candidate
            break
        fi
    done
fi
rpm -q "${selector[$preferred]}" >/dev/null 2>&1 || {
    printf 'No Nabu kernel selector is installed.\n' >&2
    exit 2
}

install -d -m0755 "$state_dir" "$pending_dir"
# Non-preferred families may remain installed for development, but they never
# own a second boot entry. Drain their transaction markers so the path unit
# cannot continuously retrigger while the selected family remains unchanged.
for candidate in alpha stable mainline lts; do
    [[ $candidate == "$preferred" ]] || rm -f -- "$pending_dir/$candidate"
done
pending_file=$pending_dir/$preferred
kernel=
if [[ -r $pending_file ]]; then
    read -r kernel <"$pending_file"
fi

# Compatibility with the former single-value marker. Accept it only when the
# exact vmlinuz is owned by the selected family, then retire the legacy marker.
legacy_pending=$state_dir/pending-kernel
if [[ -z $kernel && -r $legacy_pending ]]; then
    read -r candidate <"$legacy_pending"
    owner=$(rpm -qf --qf '%{NAME}\n' "/boot/vmlinuz-$candidate" 2>/dev/null | head -n1 || :)
    [[ $owner == "${core[$preferred]}" ]] && kernel=$candidate
fi
rm -f -- "$legacy_pending" "$state_dir/pending-fallback-kernel"

# A manual family switch may have no pending marker. In that case use the one
# kernel image owned by the currently installed selector's exact EVR.
if [[ -z $kernel ]]; then
    core_nevra=$(rpm -q --qf '%{NEVRA}\n' "${core[$preferred]}" | tail -n1)
    mapfile -t kernels < <(rpm -ql "$core_nevra" | sed -n 's@^/boot/vmlinuz-@@p')
    [[ ${#kernels[@]} -eq 1 ]] || {
        printf 'Expected one kernel image in %s, found %s.\n' "$core_nevra" "${#kernels[@]}" >&2
        exit 3
    }
    kernel=${kernels[0]}
fi

[[ $kernel != */ && $kernel != *$'\n'* ]] || {
    printf 'Invalid pending kernel identity.\n' >&2
    exit 4
}
owner=$(rpm -qf --qf '%{NAME}\n' "/boot/vmlinuz-$kernel" 2>/dev/null | head -n1 || :)
[[ $owner == "${core[$preferred]}" ]] || {
    printf 'Pending kernel %s is not owned by selected family %s.\n' "$kernel" "$preferred" >&2
    exit 5
}
[[ -s /boot/vmlinuz-$kernel && -d /usr/lib/modules/$kernel ]] || {
    printf 'Incomplete kernel payload: %s\n' "$kernel" >&2
    exit 6
}
if [[ -r $prepared_file ]] && read -r prepared <"$prepared_file" &&
   [[ $prepared == "$preferred $kernel" && ! -e $pending_file ]]; then
    exit 0
fi

# Android return is the only protected secondary entry. A Linux fallback is
# neither required nor inspected; senemos.conf is the sole Linux target.
android_entry=/boot/efi/loader/entries/android.conf
android_efi=
android_entry_digest=
android_efi_digest=
if [[ -r $android_entry ]]; then
    android_efi=$(sed -nE 's@^efi[[:space:]]+(/.*)@\1@p' "$android_entry" | head -n1)
    [[ -n $android_efi && -s /boot/efi$android_efi ]] || {
        printf 'Android return entry is incomplete.\n' >&2
        exit 7
    }
    android_entry_digest=$(sha256sum "$android_entry" | cut -d' ' -f1)
    android_efi_digest=$(sha256sum "/boot/efi$android_efi" | cut -d' ' -f1)
fi

/usr/bin/nabu-regenerate-uki "$kernel"

entry=/boot/efi/loader/entries/senemos.conf
efi_path=$(sed -nE 's@^efi[[:space:]]+(/.*)@\1@p' "$entry" | head -n1)
[[ -n $efi_path && -s /boot/efi$efi_path ]] || {
    printf 'Canonical Nabu entry does not reference a complete UKI.\n' >&2
    exit 8
}
if [[ -n $android_efi ]]; then
    [[ $(sha256sum "$android_entry" | cut -d' ' -f1) == "$android_entry_digest" &&
       $(sha256sum "/boot/efi$android_efi" | cut -d' ' -f1) == "$android_efi_digest" ]] || {
        printf 'Android return artifact changed unexpectedly.\n' >&2
        exit 9
    }
fi

temporary=$(mktemp "$state_dir/.prepared-kernel.XXXXXX")
trap 'rm -f -- "${temporary:-}"' EXIT
printf '%s %s\n' "$preferred" "$kernel" >"$temporary"
chmod 0644 "$temporary"
mv -f "$temporary" "$prepared_file"
temporary=
rm -f -- "$pending_file"
printf 'Prepared %s kernel: %s (%s)\n' "$preferred" "$kernel" "$efi_path"
