#!/bin/bash
# =============================================================================
#  Proxmox VE Kernel Manager
#  Purpose : List all installed PVE kernels and selectively remove old ones.
#            Always protects: (1) running kernel  (2) previous kernel (backup)
# =============================================================================
clear
main() {

RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
CYAN='\033[0;36m'
BOLD='\033[1m'
DIM='\033[2m'
NC='\033[0m'

print_banner() {
    echo ""
    echo -e "${CYAN}${BOLD}╔══════════════════════════════════════════════════════╗${NC}"
    echo -e "${CYAN}${BOLD}║         Proxmox VE  ·  Kernel Manager                ║${NC}"
    echo -e "${CYAN}${BOLD}╚══════════════════════════════════════════════════════╝${NC}"
    echo ""
}

print_step() { echo -e "\n${CYAN}▶  ${BOLD}$1${NC}"; }
ok()         { echo -e "  ${GREEN}✔  $1${NC}"; }
warn()       { echo -e "  ${YELLOW}⚠  $1${NC}"; }
err()        { echo -e "  ${RED}✘  $1${NC}"; }
info()       { echo -e "  ${DIM}ℹ  $1${NC}"; }

# ── Root check ────────────────────────────────────────────────────────────────
if [[ "$EUID" -ne 0 ]]; then
    err "This script must be run as root."
    exit 1
fi

print_banner

# ── Step 1: Detect the running kernel ─────────────────────────────────────────
print_step "Detecting currently running kernel ..."
RUNNING_FULL=$(uname -r)
ok "Running kernel : ${BOLD}${RUNNING_FULL}${NC}"

# ── Step 2: Scan installed PVE kernel packages ────────────────────────────────
print_step "Scanning installed Proxmox kernel packages ..."

mapfile -t PKG_LIST < <(
    dpkg --list \
    | awk '/^ii/ && /proxmox-kernel-[0-9]/ && /-pve-signed/ {print $2}' \
    | sort --version-sort
)

# Fallback: unsigned packages (older PVE installs)
if [[ ${#PKG_LIST[@]} -eq 0 ]]; then
    mapfile -t PKG_LIST < <(
        dpkg --list \
        | awk '/^ii/ && /proxmox-kernel-[0-9]/ && /-pve/ && !/-signed/ {print $2}' \
        | sort --version-sort
    )
fi

if [[ ${#PKG_LIST[@]} -eq 0 ]]; then
    err "No PVE kernels found. Exiting."
    exit 1
fi

ok "Found ${#PKG_LIST[@]} installed kernel package(s)."

# ── Step 3: Sort newest-first; define the two protected slots ─────────────────
mapfile -t PKG_SORTED < <(printf '%s\n' "${PKG_LIST[@]}" | tac)

CURRENT_PKG="${PKG_SORTED[0]}"    # slot 1 - running kernel  -> ALWAYS PROTECTED
PREVIOUS_PKG="${PKG_SORTED[1]}"   # slot 2 - backup kernel   -> ALWAYS PROTECTED

# ── Step 4: Build menu ────────────────────────────────────────────────────────
print_step "Installed Kernels (newest first)"
echo ""
printf "  ${BOLD}%-6s  %-52s  %s${NC}\n" "Opt." "Package Name" "Status"
echo -e "  ${DIM}------  ----------------------------------------------------  ----------------------${NC}"

declare -A MENU_MAP
VALID_OPTS=()
OPT=1

for pkg in "${PKG_SORTED[@]}"; do

    # PROTECTED: running kernel (slot 1)
    if [[ "$pkg" == "$CURRENT_PKG" ]] || [[ "$pkg" == *"$RUNNING_FULL"* ]]; then
        STATUS="${GREEN}${BOLD}● In Use  (protected)${NC}"
        OPT_LABEL=" -- "
        printf "  ${BOLD}%-6s${NC}  %-52s  " "$OPT_LABEL" "$pkg"
        echo -e "$STATUS"
        OPT=$((OPT + 1))
        continue
    fi

    # PROTECTED: previous/backup kernel (slot 2) - always protected
    if [[ "$pkg" == "$PREVIOUS_PKG" ]] && [[ -n "$PREVIOUS_PKG" ]]; then
        STATUS="${YELLOW}◑ Previous (backup, protected)${NC}"
        OPT_LABEL=" -- "
        printf "  ${BOLD}%-6s${NC}  %-52s  " "$OPT_LABEL" "$pkg"
        echo -e "$STATUS"
        OPT=$((OPT + 1))
        continue
    fi

    # REMOVABLE: anything older than the top two
    STATUS="${RED}✖ Removable${NC}"
    OPT_LABEL="  ${OPT} "
    MENU_MAP[$OPT]="$pkg"
    VALID_OPTS+=("$OPT")
    printf "  ${BOLD}%-6s${NC}  %-52s  " "$OPT_LABEL" "$pkg"
    echo -e "$STATUS"
    OPT=$((OPT + 1))

done

echo ""

# ── Step 5: Nothing removable? ────────────────────────────────────────────────
if [[ ${#VALID_OPTS[@]} -eq 0 ]]; then
    ok "Nothing to remove - only the running kernel and its backup are installed."
    echo ""
    exit 0
fi

OPTS_STR=$(IFS=" or "; echo "${VALID_OPTS[*]}")

echo -e "  ${BOLD}Options:${NC}"
for n in "${VALID_OPTS[@]}"; do
    echo -e "    ${CYAN}${n}${NC}  ->  Remove: ${BOLD}${MENU_MAP[$n]}${NC}"
done
echo -e "    ${CYAN}A${NC}  ->  Remove ALL old kernels (keeps running + previous backup only)"
echo -e "    ${CYAN}N${NC}  ->  Exit without changes"
echo ""

# ── Step 6: Read user choice ──────────────────────────────────────────────────
# NOTE: echo -ne + read -r used intentionally (no nested quotes = no stream parse errors)
while true; do
    echo -ne "  ${BOLD}Choose option [${OPTS_STR} or A or N]: ${NC}"
    read -r CHOICE
    CHOICE="${CHOICE^^}"

    if [[ "$CHOICE" == "N" ]]; then
        info "No changes made. Exiting."
        echo ""
        exit 0
    fi

    if [[ "$CHOICE" == "A" ]]; then
        TO_REMOVE=()
        for n in "${VALID_OPTS[@]}"; do
            TO_REMOVE+=("${MENU_MAP[$n]}")
        done
        break
    fi

    if [[ "$CHOICE" =~ ^[0-9]+$ ]] && [[ -n "${MENU_MAP[$CHOICE]+_}" ]]; then
        TO_REMOVE=("${MENU_MAP[$CHOICE]}")
        break
    fi

    warn "Invalid option '${CHOICE}'. Please enter one of: ${OPTS_STR} or A or N"
done

# ── Step 7: Confirm ───────────────────────────────────────────────────────────
echo ""
echo -e "  ${YELLOW}${BOLD}The following kernel(s) will be permanently removed:${NC}"
for pkg in "${TO_REMOVE[@]}"; do
    echo -e "    ${RED}✖  ${pkg}${NC}"
done
echo ""
echo -ne "  ${BOLD}Are you sure? [y/N]: ${NC}"
read -r CONFIRM
CONFIRM="${CONFIRM,,}"

if [[ "$CONFIRM" != "y" && "$CONFIRM" != "yes" ]]; then
    info "Removal cancelled. No changes made."
    echo ""
    exit 0
fi

# ── Step 8: Remove ────────────────────────────────────────────────────────────
echo ""
print_step "Removing selected kernel(s) ..."

REMOVED=0
FAILED=0

for pkg in "${TO_REMOVE[@]}"; do

    # Hard safety net - never touch the two protected kernels under any circumstance
    if [[ "$pkg" == "$CURRENT_PKG" ]] || [[ "$pkg" == "$PREVIOUS_PKG" ]] || [[ "$pkg" == *"$RUNNING_FULL"* ]]; then
        warn "Skipping protected kernel: ${pkg}"
        continue
    fi

    BASE_PKG="${pkg%-signed}"
    PKGS_TO_PURGE=("$pkg")
    if dpkg -l "$BASE_PKG" 2>/dev/null | grep -q "^ii"; then
        PKGS_TO_PURGE+=("$BASE_PKG")
        info "Also removing base package: ${BASE_PKG}"
    fi

    echo -ne "  ${CYAN}Removing:${NC} ${BOLD}${pkg}${NC} ... "

    if apt-get purge -y "${PKGS_TO_PURGE[@]}" >> /tmp/pve-kernel-rm.log 2>&1; then
        echo -e "${GREEN}${BOLD}Done${NC}"
        ok "Successfully removed: ${pkg}"
        REMOVED=$((REMOVED + 1))
    else
        echo -e "${RED}${BOLD}Failed${NC}"
        err "Failed via apt. Trying dpkg fallback ..."
        if dpkg --purge "${PKGS_TO_PURGE[@]}" >> /tmp/pve-kernel-rm.log 2>&1; then
            ok "Successfully removed via dpkg: ${pkg}"
            REMOVED=$((REMOVED + 1))
        else
            err "dpkg fallback also failed: ${pkg}"
            info "See /tmp/pve-kernel-rm.log"
            FAILED=$((FAILED + 1))
        fi
    fi

done

# ── Step 9: Autoremove orphans ────────────────────────────────────────────────
if [[ $REMOVED -gt 0 ]]; then
    print_step "Cleaning up orphaned kernel dependencies ..."
    apt-get autoremove --purge -y >> /tmp/pve-kernel-rm.log 2>&1 \
        && ok "Orphaned packages cleaned up." \
        || warn "autoremove had warnings (see /tmp/pve-kernel-rm.log)."
fi

# ── Step 10: Refresh bootloader ───────────────────────────────────────────────
if [[ $REMOVED -gt 0 ]]; then
    print_step "Updating bootloader ..."
    if command -v proxmox-boot-tool &>/dev/null; then
        proxmox-boot-tool refresh >> /tmp/pve-kernel-rm.log 2>&1 \
            && ok "proxmox-boot-tool refresh completed." \
            || warn "proxmox-boot-tool refresh had warnings."
    fi
    if command -v update-grub &>/dev/null; then
        update-grub >> /tmp/pve-kernel-rm.log 2>&1 \
            && ok "GRUB updated successfully." \
            || warn "update-grub had warnings."
    fi
fi

# ── Step 11: Verify removal ───────────────────────────────────────────────────
if [[ $REMOVED -gt 0 ]]; then
    print_step "Verifying removal ..."
    for pkg in "${TO_REMOVE[@]}"; do
        if dpkg -l "$pkg" 2>/dev/null | grep -q "^ii"; then
            warn "Package still present: ${pkg} - manual intervention may be needed."
        else
            ok "Confirmed removed: ${pkg}"
        fi
    done
fi

# ── Step 12: Summary ──────────────────────────────────────────────────────────
echo ""
echo -e "${CYAN}${BOLD}╔══════════════════════════════════════════╗${NC}"
echo -e "${CYAN}${BOLD}║              Summary                     ║${NC}"
echo -e "${CYAN}${BOLD}╚══════════════════════════════════════════╝${NC}"
[[ $REMOVED -gt 0 ]] && ok "Kernels removed  : ${REMOVED}"
[[ $FAILED  -gt 0 ]] && err "Kernels failed   : ${FAILED}"
ok "Running kernel   : ${RUNNING_FULL}  (untouched)"
[[ -n "$PREVIOUS_PKG" ]] && ok "Backup kernel    : ${PREVIOUS_PKG}  (untouched)"
echo ""
info "Full log saved to /tmp/pve-kernel-rm.log"
echo ""

} # end of main()

main "$@"
