#!/bin/bash
SOURCE="$0"
source /usr/lib/elive-tools/functions
if el_check_version_is_days_recent 120; then
    export FORCE_REPORTS="1"
    export NOREPORTS="0"
    export EL_REPORTS="1"
else
    export FORCE_REPORTS="0"
    export NOREPORTS="1"
    export EL_REPORTS="0"
fi
el_make_environment

if [[ -z "${yad:-}" ]]; then
    yad="/usr/bin/yad --borders=15 --center --image=logo-elive"
fi

# wrapper for fix strange languages
#===  FUNCTION  ================================================================
#          NAME:  locales_source
#   DESCRIPTION:  Sources default locales and normalizes non-Latin locale settings
#    PARAMETERS:  None
#       RETURNS:  0
#===============================================================================
locales_source(){
    if [[ -s "/etc/default/locale" ]] ; then
        source /etc/default/locale
    fi
    case "${LANG}" in
        am*|ar*|bg*|bn*|el*|fa*|gu*|he*|hy*|hi*|iw*|ja*|ka*|kn*|ko*|ml*|mr*|ne*|pa*|ru*|sr*|ta*|te*|th*|uk*|vi*|zh*)
            export LC_ALL=en_US.UTF-8
            export LANG=en_US.UTF-8
            ;;
    esac
    return 0
}
locales_source

. gettext.sh
TEXTDOMAIN="elive-upgrader"
export TEXTDOMAIN


# ==============================================================================
# Debian Upgrade Script
# Uses tmux for session management and runs non-interactively
#
# To manually simulate/run the upgrade in the same way:
#   export DEBIAN_FRONTEND=noninteractive
#   export DEBIAN_PRIORITY=critical
#   export DEBCONF_NONINTERACTIVE_SEEN=true
#   export DEBCONF_NOWARNINGS=true
#   export UCF_FORCE_CONFFOLD=1
#   export APT_LISTCHANGES_FRONTEND=none
#   export LC_ALL=$EL_LC_EN
#   export TERM=linux
#
#   # Replace codenames (e.g., bookworm to trixie) in sources, preferences, and configurations
#   sed -i -e 's|bookworm|trixie|g' /etc/apt/sources.list /etc/apt/sources.list.d/*
#   sed -i -e 's|bookworm|trixie|g' /etc/apt/preferences /etc/apt/preferences.d/* 2>/dev/null || true
#   sed -i -e 's|bookworm|trixie|g' /etc/apt/apt.conf /etc/apt/apt.conf.d/* 2>/dev/null || true
#   sed -i -e 's|bookworm|trixie|g' /etc/elive-version 2>/dev/null || true
#   sed -i -e 's|DISTRIB_RELEASE="12"|DISTRIB_RELEASE="13"|g' -e 's|DISTRIB_CODENAME="bookworm"|DISTRIB_CODENAME="trixie"|g' -e 's|bookworm|trixie|g' /etc/lsb-release 2>/dev/null || true
#
#   apt-get update
#   apt-get upgrade --without-new-pkgs -y -o Dpkg::Options::="--force-confdef" -o Dpkg::Options::="--force-confold" -o Dpkg::Options::="--force-overwrite" \
#       --allow-downgrades --allow-remove-essential --allow-change-held-packages
#   apt-get full-upgrade -y -o Dpkg::Options::="--force-confdef" -o Dpkg::Options::="--force-confold" -o Dpkg::Options::="--force-overwrite" \
#       --allow-downgrades --allow-remove-essential --allow-change-held-packages \
#       -o APT::Immediate-Configure=0 -o APT::Force-LoopBreak=1
#   apt-get autoremove --purge -y
#   apt-get clean
# ==============================================================================

. /usr/lib/elive-upgrader/functions.sh

# If debian-upgrade state is "alpha", force reports
if [[ -f "${hooks_d}/distro-upgrade/debian-upgrade" ]]; then
    _upgrade_type=$(tr -d '\r\n ' < "${hooks_d}/distro-upgrade/debian-upgrade")
    if [[ "$_upgrade_type" == "alpha" ]]; then
        export FORCE_REPORTS="1"
    fi
fi

# Whether to use plymouth for boot-time messages.
# Set to "true" to display messages in Plymouth.
# Set to "false" to display beautiful, friendly messages directly on the TTY/console.
if [[ "${is_bookworm:-0}" -eq 1 ]] || [[ "${is_trixie:-0}" -eq 1 ]] || [[ "${is_forky:-0}" -eq 1 ]] || [[ "$(cat /etc/debian_version 2>/dev/null)" =~ ^(1[2-9]|[2-9][0-9]) ]]; then
    USE_PLYMOUTH="false"
    if command -v plymouth &>/dev/null && plymouth --ping &>/dev/null; then
        plymouth quit 2>/dev/null || true
    fi
else
    if command -v plymouth &>/dev/null && plymouth --ping &>/dev/null; then
        USE_PLYMOUTH="true"
    else
        USE_PLYMOUTH="false"
    fi
fi

# Minimum free disk space required for upgrade (in GB and KB)
MIN_FREE_SPACE_GB=2
MIN_FREE_SPACE_KB=$(( MIN_FREE_SPACE_GB * 1024 * 1024 ))

# Full OS backup directory
FULL_OS_BACKUP_DIR="/var/cache/elive-upgrader/full-os-backup"
SKIP_FULL_OS_BACKUP_FLAG="/var/cache/elive-upgrader/skip-full-os-backup"

# Recovery notification file - placed where desktop session can find it
RECOVERY_NOTIFICATION_FLAG="/var/log/elive-upgrader/recovery-mode-pending"

# Configure frontend and priority for interactive dialog support on TTY1
export DEBIAN_FRONTEND=noninteractive
export DEBIAN_PRIORITY=critical
export DEBCONF_NOWARNINGS=true
export UCF_FORCE_CONFFOLD=1
export APT_LISTCHANGES_FRONTEND=none
export LC_ALL=$EL_LC_EN
export TERM=linux

# Pre-seed debconf to prevent Secure Boot / MOK enrollment prompts from hanging the boot upgrade
if command -v debconf-set-selections &>/dev/null; then
    printf "shim\tshim/enable_secureboot\tboolean\tfalse\n" | debconf-set-selections || true
    printf "update-secureboot-policy\tupdate-secureboot-policy/enable_secureboot\tboolean\tfalse\n" | debconf-set-selections || true
    printf "dkms\tdkms/sign_tool\tboolean\tfalse\n" | debconf-set-selections || true
fi

# set -e disabled to prevent premature exits on minor failures

BOOT_MODE="false"

# Source LSB functions for boot mode logging
if [[ -f /lib/lsb/init-functions ]]; then
    . /lib/lsb/init-functions
fi

# Provide dummy LSB functions if they don't exist/weren't loaded
if ! command -v log_action_msg &>/dev/null; then
    log_action_msg() { echo " * $*"; }
fi
if ! command -v log_warning_msg &>/dev/null; then
    log_warning_msg() { echo " ! WARNING: $*"; }
fi
if ! command -v log_failure_msg &>/dev/null; then
    log_failure_msg() { echo " ! FAILURE: $*"; }
fi

# Will be set from command-line argument
TARGET_CODENAME=""

# State file for tracking upgrade progress across reboots
UPGRADE_STATE_FILE="/etc/default/elive-distro-upgrade"

# Lock file to prevent concurrent execution
UPGRADE_LOCK_FILE="/tmp/.debian-upgrader.lock"

# Recovery flag file - if present, upgrade will not run (for manual recovery)
UPGRADE_RECOVERY_FLAG="/etc/default/elive-distro-upgrade-recovery"

# Colors for output
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
CYAN='\033[0;36m'
MAGENTA='\033[0;35m'
BOLD='\033[1m'
NC='\033[0m' # No Color

#===  FUNCTION  ================================================================
#          NAME:  draw_banner
#   DESCRIPTION:  Displays a formatted visual header banner in terminal/Plymouth
#    PARAMETERS:  $1 = message_v, $2 = color_v
#       RETURNS:  0
#===============================================================================
draw_banner(){
    local message_v="${1}"
    local color_v="${2:-${CYAN}}"
    local len_v=${#message_v}
    local border_v=""
    local i_v=0

    for ((i_v=0; i_v<len_v+4; i_v++)); do
        border_v="${border_v}="
    done

    stty sane 2>/dev/null || true
    echo -e "${color_v}${border_v}${NC}"
    echo -e "${color_v}| ${BOLD}${message_v}${NC}${color_v} |${NC}"
    echo -e "${color_v}${border_v}${NC}"

    if [[ "${BOOT_MODE}" == "true" ]]; then
        if [[ "${USE_PLYMOUTH}" == "true" ]] && command -v plymouth &>/dev/null && plymouth --ping &>/dev/null; then
            plymouth display-message --text="ELIVE SYSTEM UPGRADE IN PROGRESS - DO NOT REBOOT!" 2>/dev/null || true
            plymouth message --text="*** ${message_v} ***" 2>/dev/null || true
        fi
        log_action_msg "=== ${message_v} ==="
    fi
    return 0
}

#===  FUNCTION  ================================================================
#          NAME:  disable_automatic_clean
#   DESCRIPTION:  Disables APT automatic package cleaning in apt.conf.d files
#    PARAMETERS:  None
#       RETURNS:  0
#===============================================================================
disable_automatic_clean(){
    local conf_file_f=""
    local state_file_f="/var/cache/elive-upgrader/modified-apt-clean.txt"
    mkdir -p /var/cache/elive-upgrader 2>/dev/null || true

    log_info "Temporarily disabling automatic APT cleaning in configuration files..."
    for conf_file_f in /etc/apt/apt.conf.d/*; do
        if [[ -f "${conf_file_f}" ]]; then
            if grep -qE '^\s*(DSELECT::Clean|APT::Get::Clean)\s+("always"|always)' "${conf_file_f}" 2>/dev/null; then
                if ! grep -qxF "${conf_file_f}" "${state_file_f}" 2>/dev/null; then
                    echo "${conf_file_f}" >> "${state_file_f}"
                fi
                sed -i 's/^\s*DSELECT::Clean\s\+always/#DSELECT::Clean always/g' "${conf_file_f}" 2>/dev/null || true
                sed -i 's/^\s*APT::Get::Clean\s\+always/#APT::Get::Clean always/g' "${conf_file_f}" 2>/dev/null || true
                sed -i 's/^\s*DSELECT::Clean\s\+"always"/#DSELECT::Clean "always"/g' "${conf_file_f}" 2>/dev/null || true
                sed -i 's/^\s*APT::Get::Clean\s\+"always"/#APT::Get::Clean "always"/g' "${conf_file_f}" 2>/dev/null || true
            fi
        fi
    done
    return 0
}

#===  FUNCTION  ================================================================
#          NAME:  enable_automatic_clean
#   DESCRIPTION:  Re-enables APT automatic package cleaning in apt.conf.d files
#    PARAMETERS:  None
#       RETURNS:  0
#===============================================================================
enable_automatic_clean(){
    local conf_file_f=""
    local state_file_f="/var/cache/elive-upgrader/modified-apt-clean.txt"

    log_info "Re-enabling automatic APT cleaning in modified configuration files..."
    if [[ -f "${state_file_f}" ]]; then
        while read -r conf_file_f || [[ -n "${conf_file_f}" ]]; do
            [[ -z "${conf_file_f}" ]] && continue
            if [[ -f "${conf_file_f}" ]]; then
                sed -i 's/^#\s*DSELECT::Clean\s\+always/DSELECT::Clean always/g' "${conf_file_f}" 2>/dev/null || true
                sed -i 's/^#\s*APT::Get::Clean\s\+always/APT::Get::Clean always/g' "${conf_file_f}" 2>/dev/null || true
                sed -i 's/^#\s*DSELECT::Clean\s\+"always"/DSELECT::Clean "always"/g' "${conf_file_f}" 2>/dev/null || true
                sed -i 's/^#\s*APT::Get::Clean\s\+"always"/APT::Get::Clean "always"/g' "${conf_file_f}" 2>/dev/null || true
            fi
        done < <(sort -u "${state_file_f}" 2>/dev/null || cat "${state_file_f}")
        rm -f "${state_file_f}" 2>/dev/null || true
    else
        for conf_file_f in /etc/apt/apt.conf.d/*; do
            if [[ -f "${conf_file_f}" ]]; then
                sed -i 's/^#\s*DSELECT::Clean\s\+always/DSELECT::Clean always/g' "${conf_file_f}" 2>/dev/null || true
                sed -i 's/^#\s*APT::Get::Clean\s\+always/APT::Get::Clean always/g' "${conf_file_f}" 2>/dev/null || true
                sed -i 's/^#\s*DSELECT::Clean\s\+"always"/DSELECT::Clean "always"/g' "${conf_file_f}" 2>/dev/null || true
                sed -i 's/^#\s*APT::Get::Clean\s\+"always"/APT::Get::Clean "always"/g' "${conf_file_f}" 2>/dev/null || true
            fi
        done
    fi
    return 0
}

#===  FUNCTION  ================================================================
#          NAME:  get_nvidia_version
#   DESCRIPTION:  Retrieves installed NVIDIA driver version from system config or dpkg
#    PARAMETERS:  None
#       RETURNS:  Prints NVIDIA driver version string if found
#===============================================================================
get_nvidia_version(){
    local buffer_v=""

    if [[ -f /etc/elive-version ]]; then
        buffer_v="$(awk -F': ' '/^nvidia-privative: /{print $2}' /etc/elive-version 2>/dev/null || true)"
    fi
    if [[ -z "${buffer_v}" ]]; then
        if nvidia-privative-drivers-install -t &>/dev/null; then
            buffer_v="$(dpkg -l | awk '/^ii  nvidia-.*-driver/ {print $2}' | sed -E 's/^nvidia-(.*-)?driver$/\1/; s/-$//; s/^$/current/' | head -n 1)"
            [[ -z "${buffer_v}" ]] && buffer_v="current"
        fi
    fi

    [[ -n "${buffer_v}" ]] && echo "${buffer_v}"
    return 0
}


# Logging setup
LOG_DIR="/var/log/elive-upgrader"

#===  FUNCTION  ================================================================
#          NAME:  get_log_filename
#   DESCRIPTION:  Generates structured log file path based on step and codenames
#    PARAMETERS:  $1 = step_v
#       RETURNS:  Prints formatted log file path string
#===============================================================================
get_log_filename(){
    local step_v="${1:-prepare-system}"
    local timestamp_v=""
    local from_cn_v=""
    local target_cn_v=""

    timestamp_v="$(date +%Y%m%d-%H%M%S)"
    from_cn_v="${current_codename:-$(debian_version_to_codename "$(cat /etc/debian_version 2>/dev/null | cut -d'.' -f1)" 2>/dev/null)}"
    from_cn_v="${from_cn_v:-unknown}"
    target_cn_v="${TARGET_CODENAME:-unknown}"

    mkdir -p "${LOG_DIR}" 2>/dev/null || true
    chmod 755 "${LOG_DIR}" 2>/dev/null || true

    echo "${LOG_DIR}/debian-upgrade-from-${from_cn_v}-to-${target_cn_v}-${step_v}-${timestamp_v}.log"
}

# Determine current step for log naming
init_logging() {
    if [[ -z "${LOG_FILE_INITIALIZED:-}" ]]; then
        CURRENT_STEP_NAME="prepare-system"
        if [[ "$BOOT_MODE" == "true" ]]; then
            CURRENT_STEP_NAME="boot-console-upgrade"
        fi

        DEBIAN_UPGRADE_LOGFILE="${DEBIAN_UPGRADE_LOGFILE:-$(get_log_filename "$CURRENT_STEP_NAME")}"
        export DEBIAN_UPGRADE_LOGFILE
        LOG_FILE="$DEBIAN_UPGRADE_LOGFILE"
        exec > >(stdbuf -i0 -oL -eL tee -a "$LOG_FILE") 2>&1
        LOG_FILE_INITIALIZED="true"
    fi
}

ensure_log_permissions() {
    if [[ -f "$LOG_FILE" ]]; then
        chmod 644 "$LOG_FILE" 2>/dev/null || true
    fi
}

# Fix unsupported keyring filetypes (e.g., marillat key)
fix_unsupported_keyrings() {
    if [[ -f /etc/apt/trusted.gpg.d/marillat.key.binary.asc ]]; then
        log_info "Fixing marillat keyring filetype..."
        if command -v gpg &>/dev/null; then
            gpg --dearmor < /etc/apt/trusted.gpg.d/marillat.key.binary.asc > /etc/apt/trusted.gpg.d/marillat.key.binary.gpg 2>/dev/null || true
        fi
        rm -f /etc/apt/trusted.gpg.d/marillat.key.binary.asc
    fi
}

# Lock mechanism to prevent concurrent execution
acquire_lock() {
    if [[ -f "$UPGRADE_LOCK_FILE" ]]; then
        local lock_pid
        lock_pid=$(cat "$UPGRADE_LOCK_FILE" 2>/dev/null)
        if [[ -n "$lock_pid" ]] && kill -0 "$lock_pid" 2>/dev/null; then
            log_error "Another instance of debian-upgrader is already running (PID: $lock_pid)"
            exit 1
        else
            rm -f "$UPGRADE_LOCK_FILE"
        fi
    fi
    echo $$ > "$UPGRADE_LOCK_FILE"
    trap release_lock EXIT
}

release_lock() {
    rm -f "$UPGRADE_LOCK_FILE"
}

usage() {
    echo "Usage: $0 <target_codename>"
    echo "   or: $0 --enable-boot-upgrade <target_codename>"
    echo "   or: $0 --disable-boot-upgrade"
    echo "   or: $0 --boot-mode <target_codename>"
    echo "   or: $0 --status"
    echo "   or: $0 --cancel"
    echo
    echo "Example: $0 trixie"
    echo "Supported targets: ${!DEBIAN_CODENAMES[@]}"
    echo
    echo "Options:"
    echo "  --enable-boot-upgrade <codename>  Prepare and enable boot-time upgrade"
    echo "  --disable-boot-upgrade            Disable boot-time upgrade"
    echo "  --boot-mode <codename>            Run upgrade in boot mode (used by init system)"
    echo "  --status                          Show current upgrade state"
    echo "  --cancel                          Cancel an in-progress boot upgrade"
    exit 1
}

# ==============================================================================
# Full OS Backup and Recovery Functions
# ==============================================================================

#===  FUNCTION  ================================================================
#          NAME:  is_btrfs
#   DESCRIPTION:  Checks whether root filesystem type is BTRFS
#    PARAMETERS:  None
#       RETURNS:  0 if btrfs, 1 otherwise
#===============================================================================
is_btrfs(){
    local fs_type_v=""
    fs_type_v="$(stat -f -c %T / 2>/dev/null || echo "unknown")"
    if [[ "${fs_type_v}" == "btrfs" ]]; then
        return 0
    fi
    return 1
}

#===  FUNCTION  ================================================================
#          NAME:  is_non_rotational
#   DESCRIPTION:  Checks if the root device is non-rotational media (SSD / NVMe)
#    PARAMETERS:  None
#       RETURNS:  0 if SSD/NVMe, 1 if rotational/HDD
#===============================================================================
is_non_rotational(){
    local root_dev_v=""
    local slave_dir_d=""
    local slave_f=""
    local rotational_file_f=""
    local rotational_v=""

    root_dev_v="$(df -P / | awk 'NR==2 {print $1}' | sed 's|/dev/||;s|/.*||;s/[0-9]*$//')"

    if [[ "${root_dev_v}" == dm-* ]] || [[ "${root_dev_v}" == mapper* ]]; then
        slave_dir_d="/sys/class/block/${root_dev_v}/slaves"
        if [[ -d "${slave_dir_d}" ]]; then
            for slave_f in "${slave_dir_d}"/*; do
                if [[ -L "${slave_f}" ]]; then
                    root_dev_v="$(basename "$(readlink -f "${slave_f}")")"
                    root_dev_v="${root_dev_v%%[0-9]*}"
                    break
                fi
            done
        fi
        if [[ "${root_dev_v}" == dm-* ]]; then
            if [[ -f /sys/block/nvme0n1/queue/rotational ]]; then
                root_dev_v="nvme0n1"
            elif [[ -f /sys/block/sda/queue/rotational ]]; then
                root_dev_v="sda"
            fi
        fi
    fi

    root_dev_v="${root_dev_v%%p*}"
    rotational_file_f="/sys/block/${root_dev_v}/queue/rotational"

    if [[ -f "${rotational_file_f}" ]]; then
        rotational_v="$(cat "${rotational_file_f}" 2>/dev/null || echo "1")"
        if [[ "${rotational_v}" == "0" ]]; then
            return 0
        fi
    fi

    if ls /sys/block/nvme* &>/dev/null; then
        return 0
    fi

    return 1
}

# Helper function to convert size unit to KB
to_kb() {
    local val="$1"
    local unit
    unit=$(echo "$2" | tr '[:upper:]' '[:lower:]')
    val=${val//,/.}

    if [[ -z "$val" || -z "$unit" ]]; then
        echo "0"
        return
    fi

    local int_val
    if [[ "$val" == *.* ]]; then
        local int_part="${val%%.*}"
        local dec_part="${val#*.}"
        dec_part="${dec_part}000"
        dec_part="${dec_part:0:3}"
        int_val=$(( 10#${int_part:-0} * 1000 + 10#${dec_part:-0} ))
    else
        int_val=$(( 10#${val:-0} * 1000 ))
    fi

    local factor=1
    case "$unit" in
        k*|kb) factor=1 ;;
        m*|mb) factor=1024 ;;
        g*|gb) factor=$((1024 * 1024)) ;;
        t*|tb) factor=$((1024 * 1024 * 1024)) ;;
        b*|byte*) factor=0 ;;
        *) factor=1 ;;
    esac

    if [[ "$unit" == b* ]]; then
        echo $(( int_val / 1000000 ))
    else
        echo $(( int_val * factor / 1000 ))
    fi
}

#===  FUNCTION  ================================================================
#          NAME:  get_os_dirs_size_kb
#   DESCRIPTION:  Calculates total size of OS directories in KB for backup estimation
#    PARAMETERS:  None
#       RETURNS:  Prints total size in KB
#===============================================================================
get_os_dirs_size_kb(){
    local backup_dirs=(
        "/usr"
        "/boot"
        "/etc"
        "/var"
        "/opt"
        "/srv"
        "/root"
        "/lib"
        "/lib32"
        "/lib64"
        "/libx32"
        "/bin"
        "/sbin"
    )

    local du_excludes=(
        --exclude='/boot/efi'
        --exclude='/boot/efi/*'
        --exclude='/var/cache/elive-upgrader'
        --exclude='/var/cache/apt/archives/*'
        --exclude='/var/tmp/*'
        --exclude='/var/log/journal/*'
        --exclude='/var/cache/thumbnails/*'
    )

    local os_dirs_size_kb=0
    local existing_dirs=()
    local d=""

    for d in "${backup_dirs[@]}"; do
        if [[ -d "$d" ]]; then
            existing_dirs+=("$d")
        fi
    done

    if [[ ${#existing_dirs[@]} -gt 0 ]]; then
        os_dirs_size_kb=$(du -s -c -k "${du_excludes[@]}" "${existing_dirs[@]}" 2>/dev/null | awk '/total$/ {print $1}')
    fi

    if ! [[ "$os_dirs_size_kb" =~ ^[0-9]+$ ]]; then
        os_dirs_size_kb=0
        for d in "${existing_dirs[@]}"; do
            local size_kb
            size_kb=$(du -s -k "${du_excludes[@]}" "$d" 2>/dev/null | awk '{print $1}')
            if [[ "$size_kb" =~ ^[0-9]+$ ]]; then
                os_dirs_size_kb=$((os_dirs_size_kb + size_kb))
            fi
        done
    fi

    echo "${os_dirs_size_kb}"
}

#===  FUNCTION  ================================================================
#          NAME:  warn_no_full_backup_dialog
#   DESCRIPTION:  Displays GUI prompt warning user about insufficient backup space
#    PARAMETERS:  $1 = os_size_kb, $2 = req_space_kb, $3 = free_space_kb
#       RETURNS:  0 if user agrees to continue, 1 if cancelled
#===============================================================================
warn_no_full_backup_dialog(){
    local os_size_kb="$1"
    local req_space_kb="$2"
    local free_space_kb="$3"

    local os_gb req_gb avail_gb
    os_gb=$(awk -v k="$os_size_kb" 'BEGIN {printf "%.1f", k/1048576}')
    req_gb=$(awk -v k="$req_space_kb" 'BEGIN {printf "%.1f", k/1048576}')
    avail_gb=$(awk -v k="$free_space_kb" 'BEGIN {printf "%.1f", k/1048576}')

    local title="$(eval_gettext "Skip Full OS Backup")"
    local msg="$(printf "$(eval_gettext "Skipping full OS backup before upgrade due to limited free disk space.\n\n- Current OS Size: %s GB\n- Required Free Space for Full Backup: %s GB\n- Available Free Space: %s GB\n\nCritical configuration files will still be backed up, but a complete image backup of the OS will be skipped.\n\nDo you want to continue with the upgrade without a full OS backup?")" "${os_gb}" "${req_gb}" "${avail_gb}")"

    if [[ -n "$DISPLAY" ]] && command -v ${yad%% *} &>/dev/null; then
        $yad --question --title="$title" --text="$msg" --width=500 \
            --button="$(eval_gettext "Continue without Full Backup"):0" \
            --button="$(eval_gettext "Cancel"):1"
        return $?
    elif [[ -n "$DISPLAY" ]] && command -v zenity &>/dev/null; then
        zenity --question --title="$title" --text="$msg" --width=500 \
            --ok-label="$(eval_gettext "Continue without Full Backup")" \
            --cancel-label="$(eval_gettext "Cancel")" 2>/dev/null
        return $?
    else
        echo -e "\n*** ${YELLOW}NOTICE: SKIPPING FULL OS BACKUP${NC} ***"
        echo -e "$msg\n"
        read -p "$(eval_gettext "Type 'yes' to continue without full OS backup, or any other key to cancel: ")" choice
        if [[ "$choice" == "yes" ]]; then
            return 0
        else
            return 1
        fi
    fi
}

# Perform full OS backup
full_os_backup() {
    if [[ -f "$SKIP_FULL_OS_BACKUP_FLAG" ]]; then
        log_info "Skipping full OS backup (user chose to proceed without backup during preparation)."
        rm -f "$SKIP_FULL_OS_BACKUP_FLAG" 2>/dev/null || true
        return 0
    fi

    if ! command -v rsync &>/dev/null; then
        NOREPORTS=1 log_warning "rsync is not installed. Skipping full OS backup."
        return 0
    fi

    # Clean up any previous backup first to free space and avoid double-counting
    if [[ -d "$FULL_OS_BACKUP_DIR" ]]; then
        log_info "Removing previous OS backup..."
        rm -rf "$FULL_OS_BACKUP_DIR"
    fi

    # Calculate total size of main OS directories to backup in KB (excluding /home, caches, etc.)
    log_info "Calculating size of main OS directories for backup..."
    local os_dirs_size_kb
    os_dirs_size_kb=$(get_os_dirs_size_kb)

    # Required space for full OS backup: OS_DIRS_SIZE (1x for backup copy) + MIN_FREE_SPACE_KB (5 GB) + 4 GB safety shift
    local upgrade_extra_kb=$MIN_FREE_SPACE_KB
    local required_space_kb=$(( os_dirs_size_kb + upgrade_extra_kb + 4194304 ))

    # Check free space on root partition
    local free_space_kb
    free_space_kb=$(df -P / | awk 'NR==2 {print $4}')

    log_info "OS directories size: $((os_dirs_size_kb / 1024)) MB. Required free space for backup: $((required_space_kb / 1024)) MB. Available: $((free_space_kb / 1024)) MB."

    if (( free_space_kb < required_space_kb )); then
        log_info "Not enough free space for full OS backup. Skipping."
        return 0
    fi

    draw_banner "CREATING FULL OS BACKUP FOR RECOVERY" "$MAGENTA"

    # Ensure the parent and backup directories exist before rsync starts
    mkdir -p "/var/cache/elive-upgrader"
    mkdir -p "$FULL_OS_BACKUP_DIR"

    log_info "Backuping your OS to $FULL_OS_BACKUP_DIR (this may take a few minutes)..."

    # Perform a single, clean rsync run from / to the backup directory
    # We exclude '/var/cache/elive-upgrader' and its contents to prevent rsync
    # from traversing or copying its own destination directory.
    rsync -aAX --delete --info=progress2 \
        --exclude='/boot/efi' \
        --exclude='/boot/efi/*' \
        --exclude='/home/*' \
        --exclude='/proc/*' \
        --exclude='/sys/*' \
        --exclude='/dev/*' \
        --exclude='/run/*' \
        --exclude='/tmp/*' \
        --exclude='/mnt/*' \
        --exclude='/media/*' \
        --exclude='/lost+found' \
        --exclude='/swapfile' \
        --exclude='/etc/default/elive-distro-upgrade*' \
        --exclude='/var/cache/elive-upgrader' \
        --exclude='/var/cache/elive-upgrader/*' \
        --exclude='/var/cache/apt/archives/*' \
        --exclude='/var/tmp/*' \
        --exclude='/var/log/journal/*' \
        --exclude='/var/cache/thumbnails/*' \
        / "$FULL_OS_BACKUP_DIR/" 2>/dev/null || true

    # Save backup metadata
    local backup_info_file="${FULL_OS_BACKUP_DIR}/backup-info.txt"
    {
        echo "ELIVE FULL OS BACKUP"
        echo "Created: $(date)"
        echo "Debian version: $(cat /etc/debian_version 2>/dev/null || echo 'unknown')"
        echo "Kernel: $(uname -r)"
        echo "Filesystem: $(stat -f -c %T / 2>/dev/null || echo 'unknown')"
    } > "$backup_info_file"

    # Save package list for recovery reference
    dpkg --get-selections '*' 2>/dev/null > "${FULL_OS_BACKUP_DIR}/dpkg-selections.txt" || true

    # Create a marker file indicating backup is valid
    touch "${FULL_OS_BACKUP_DIR}/.backup-complete"

    log_info "Full OS backup completed at: $FULL_OS_BACKUP_DIR"

    # Show backup size
    local backup_size
    backup_size=$(du -sh "$FULL_OS_BACKUP_DIR" 2>/dev/null | awk '{print $1}')
    log_info "Backup size: $backup_size"
}

# Restore from full OS backup (used by recovery mode)
restore_from_backup() {
    if [[ ! -f "${FULL_OS_BACKUP_DIR}/.backup-complete" ]]; then
        log_error "No valid backup found at $FULL_OS_BACKUP_DIR"
        return 1
    fi

    if ! command -v rsync &>/dev/null; then
        log_error "rsync is not available. Cannot restore system cleanly from backup."
        return 1
    fi

    draw_banner "RESTORING SYSTEM FROM BACKUP" "$RED"

    log_info "Restoring system from backup at $FULL_OS_BACKUP_DIR..."

    local rsync_restore_opts=(
        -aAXHv
        --delete
        --omit-dir-times
        --numeric-ids
        --info=progress2
        --ignore-errors
        --exclude='/boot/efi'
        --exclude='/boot/efi/*'
        --exclude='/home/*'
        --exclude='/proc/*'
        --exclude='/sys/*'
        --exclude='/dev/*'
        --exclude='/run/*'
        --exclude='/tmp/*'
        --exclude='/mnt/*'
        --exclude='/media/*'
        --exclude='/lost+found'
        --exclude='/swapfile'
        --exclude='/etc/default/elive-distro-upgrade*'
        --exclude='/var/cache/elive-upgrader'
        --exclude='/var/cache/elive-upgrader/*'
        --exclude='/var/cache/apt/archives/*'
        --exclude='/var/tmp/*'
        --exclude='/var/log/journal/*'
        --exclude='/var/cache/thumbnails/*'
        --exclude='/backup-info.txt'
        --exclude='/dpkg-selections.txt'
        --exclude='/.backup-complete'
    )

    if ! rsync "${rsync_restore_opts[@]}" "$FULL_OS_BACKUP_DIR/" / 2>/dev/null; then
        log_warning "rsync restore encountered errors, retrying with verbose output..."
        if ! rsync "${rsync_restore_opts[@]}" "$FULL_OS_BACKUP_DIR/" /; then
            log_error "rsync restore failed."
            return 1
        fi
    fi

    # Clean stale package manager locks from the restored system
    rm -f /var/lib/dpkg/lock-frontend /var/lib/dpkg/lock /var/lib/apt/lists/lock /var/cache/apt/archives/lock "$UPGRADE_LOCK_FILE" /tmp/.debian-upgrader.lock 2>/dev/null || true

    # Clean orphaned kernel images and initramfs in /boot without corresponding /lib/modules directories
    log_info "Cleaning orphaned kernel and initramfs files in /boot..."
    local vmlinuz_f initrd_f kver
    for vmlinuz_f in /boot/vmlinuz-*; do
        if [[ -f "$vmlinuz_f" ]]; then
            kver="${vmlinuz_f#/boot/vmlinuz-}"
            if [[ -n "$kver" && "$kver" != "*" && ! -d "/lib/modules/$kver" ]]; then
                log_info "Removing orphaned kernel $kver (no /lib/modules/$kver found)..."
                rm -f "/boot/vmlinuz-${kver}" "/boot/initrd.img-${kver}"* "/boot/System.map-${kver}"* "/boot/config-${kver}"* 2>/dev/null || true
            fi
        fi
    done
    for initrd_f in /boot/initrd.img-*; do
        if [[ -f "$initrd_f" ]]; then
            kver="${initrd_f#/boot/initrd.img-}"
            if [[ -n "$kver" && "$kver" != "*" && ! -d "/lib/modules/$kver" ]]; then
                rm -f "$initrd_f" 2>/dev/null || true
            fi
        fi
    done

    log_info "Updating package lists for restored system..."
    apt-get update 2>/dev/null || true

    # Regenerate initramfs and update GRUB bootloader for all restored kernels
    if command -v update-initramfs &>/dev/null; then
        log_info "Regenerating initramfs for all restored kernels..."
        local kdir kver
        for kdir in /lib/modules/*; do
            if [[ -d "$kdir" ]]; then
                kver="$(basename "$kdir")"
                log_info "Updating initramfs for kernel $kver..."
                if [[ -f "/boot/initrd.img-${kver}" ]]; then
                    update-initramfs -u -k "$kver" 2>/dev/null || true
                else
                    update-initramfs -c -k "$kver" 2>/dev/null || true
                fi
            fi
        done
        update-initramfs -u -k all 2>/dev/null || true
    fi

    if command -v update-grub &>/dev/null; then
        log_info "Updating GRUB bootloader configuration..."
        update-grub 2>/dev/null || true
    fi

    disable_boot_upgrade
    restore_tty1_and_dms
    upgrade_state_clear
    clear_recovery_notification

    log_info "System restore from backup completed."
}

# Create desktop notification for recovery mode
create_recovery_notification() {
    log_info "Creating recovery notification for desktop..."

    mkdir -p /var/log/elive-upgrader
    mkdir -p /var/cache/elive-upgrader
    chmod 777 /var/cache/elive-upgrader 2>/dev/null || true
    chmod -R a+rwX /var/cache/elive-upgrader 2>/dev/null || true

    # Create the recovery flag file only if explicitly requested for failure mode
    if [[ "${1:-}" == "failure" ]]; then
        touch "$RECOVERY_NOTIFICATION_FLAG"
        chmod 666 "$RECOVERY_NOTIFICATION_FLAG" 2>/dev/null || true
    fi

    # Create the upgrade-complete notification and cleanup script
    cat > "/usr/lib/elive-upgrader/recovery-notification" << 'RECOVERY_SCRIPT'
#!/bin/bash
# Upgrade notification script - shown to user on desktop after upgrade

# Single instance lock to prevent duplicate dialogs if launched multiple times concurrently
LOCK_DIR="/tmp/.elive-recovery-notification.lock"
if ! mkdir "$LOCK_DIR" 2>/dev/null; then
    exit 0
fi
trap 'rm -rf "$LOCK_DIR" 2>/dev/null' EXIT

if [[ -z "${yad:-}" ]]; then
    yad="/usr/bin/yad --borders=15 --center --image=logo-elive"
fi

. gettext.sh
TEXTDOMAIN="elive-upgrader"
export TEXTDOMAIN

MIN_FREE_SPACE_GB=5
RECOVERY_FLAG="/var/log/elive-upgrader/recovery-mode-pending"
BACKUP_DIR="/var/cache/elive-upgrader/full-os-backup"
CLEANUP_FLAG="/var/cache/elive-upgrader/cleanup-backups-pending"

# Function to clean up old backups in the background
do_backup_cleanup() {
    if [[ -f "$CLEANUP_FLAG" ]]; then
        echo "Cleaning up old upgrade backup directories..."
        find /root -maxdepth 1 -type d -name 'debian-upgrade-backup-*' -exec rm -rf {} + 2>/dev/null || true
        find /root -maxdepth 1 -type f -name 'debian-upgrade-backup-*' -delete 2>/dev/null || true

        if [[ -d "/var/cache/elive-upgrader/full-os-backup" ]]; then
            echo "Removing full OS backup directory..."
            rm -rf "/var/cache/elive-upgrader/full-os-backup"
            echo "Full OS backup directory removed."
        fi

        rm -f "$CLEANUP_FLAG"
        echo "Old backup directories cleaned up."
    fi
}

# Only run if we have a display (graphical session)
if [[ -z "${DISPLAY:-}" ]] && [[ -z "${WAYLAND_DISPLAY:-}" ]]; then
    exit 0
fi

# If no notification or recovery flags exist, exit immediately without showing anything
if [[ ! -s "$RECOVERY_FLAG" ]] && [[ ! -s "/var/cache/elive-upgrader/notify-upgrade-complete" ]] && [[ ! -s "/var/cache/elive-upgrader/notify-upgrade-failed-space" ]]; then
    ( do_backup_cleanup ) &
    exit 0
fi

# Wait for GUI display and desktop session to be fully ready before showing notifications
wait_for_desktop() {
    local max_wait=30
    local count=0
    while (( count < max_wait )); do
        if [[ -n "${DISPLAY:-}" ]] || [[ -n "${WAYLAND_DISPLAY:-}" ]]; then
            if command -v xset &>/dev/null && xset q &>/dev/null; then
                break
            fi
            if command -v xhost &>/dev/null && xhost &>/dev/null; then
                break
            fi
        fi
        sleep 1
        (( count++ ))
    done
    # Give window manager time to settle desktop and panels
    sleep 3
}

wait_for_desktop

# Show insufficient disk space notification
show_insufficient_space_notification() {
    local free_mb=""
    if [[ -f "/var/cache/elive-upgrader/notify-upgrade-failed-space" ]]; then
        free_mb=$(cat "/var/cache/elive-upgrader/notify-upgrade-failed-space" 2>/dev/null || true)
    fi

    local title="$(eval_gettext "Elive System Upgrade Aborted")"
    local space_info="$(printf "$(eval_gettext "at least %s GB required")" "${MIN_FREE_SPACE_GB}")"
    if [[ -n "$free_mb" ]]; then
        space_info="$(printf "$(eval_gettext "at least %s GB required; you have %s MB available")" "${MIN_FREE_SPACE_GB}" "${free_mb}")"
    fi
    local message="$(printf "$(eval_gettext "The system upgrade was aborted during boot because there was not enough free disk space (%s).\n\nPlease delete unnecessary files to free up between %s and 10 GB of disk space on your system and try running the upgrade again.")" "${space_info}" "${MIN_FREE_SPACE_GB}")"

    if command -v ${yad%% *} &>/dev/null; then
        $yad --warning --title="$title" --text="$message" --width=520 2>/dev/null
    elif command -v zenity &>/dev/null; then
        zenity --warning --title="$title" --text="$message" --width=520 2>/dev/null
    elif command -v kdialog &>/dev/null; then
        kdialog --title "$title" --sorry "$message" 2>/dev/null
    elif command -v xmessage &>/dev/null; then
        xmessage -center "$message" 2>/dev/null
    else
        echo "$title: $message"
    fi
}

# Check if this is an insufficient disk space failure
if [[ -s "/var/cache/elive-upgrader/notify-upgrade-failed-space" ]]; then
    show_insufficient_space_notification
    : > "/var/cache/elive-upgrader/notify-upgrade-failed-space" 2>/dev/null || true
    rm -f "/var/cache/elive-upgrader/notify-upgrade-failed-space" 2>/dev/null || true
    exit 0
fi

# Show a success notification if the upgrade was completed
show_success_notification() {
    local target_cn=""
    if [[ -f "/var/cache/elive-upgrader/notify-upgrade-complete" ]]; then
        target_cn=$(cat "/var/cache/elive-upgrader/notify-upgrade-complete" 2>/dev/null || true)
        : > "/var/cache/elive-upgrader/notify-upgrade-complete" 2>/dev/null || true
        rm -f "/var/cache/elive-upgrader/notify-upgrade-complete" 2>/dev/null || true
    fi
    [[ -z "$target_cn" ]] && target_cn="system"

    local title="$(eval_gettext "Elive System Upgrade Complete")"
    local message="$(printf "$(eval_gettext "Congratulations! Your Elive system has been successfully upgraded to Debian %s.\n\nLogs of the upgrade process are saved in:\n/var/log/elive-upgrader/\n\nWould you like to view them?")" "${target_cn}")"

    local open_folder=false
    local dialog_shown=false
    local attempts=0

    while [[ "$dialog_shown" == "false" ]] && (( attempts < 10 )); do
        (( attempts++ ))

        if command -v ${yad%% *} &>/dev/null; then
            $yad --question --title="$title" --text="$message" --button="$(eval_gettext "Open Log Folder"):0" --button="$(eval_gettext "Close"):1" --width=520 2>/dev/null
            local exit_code=$?
            if [[ $exit_code -eq 0 ]]; then
                open_folder=true
                dialog_shown=true
            elif [[ $exit_code -eq 1 || $exit_code -eq 252 ]]; then
                open_folder=false
                dialog_shown=true
            fi
        elif command -v zenity &>/dev/null; then
            zenity --question --title="$title" --text="$message" --ok-label="$(eval_gettext "Open Log Folder")" --cancel-label="$(eval_gettext "Close")" --width=520 2>/dev/null
            local exit_code=$?
            if [[ $exit_code -eq 0 ]]; then
                open_folder=true
                dialog_shown=true
            elif [[ $exit_code -eq 1 ]]; then
                open_folder=false
                dialog_shown=true
            fi
        elif command -v kdialog &>/dev/null; then
            kdialog --title "$title" --yesno "$message" 2>/dev/null
            local exit_code=$?
            if [[ $exit_code -eq 0 ]]; then
                open_folder=true
                dialog_shown=true
            elif [[ $exit_code -eq 1 ]]; then
                open_folder=false
                dialog_shown=true
            fi
        elif command -v xmessage &>/dev/null; then
            xmessage -center -buttons "$(eval_gettext "Open Log Folder"):101,$(eval_gettext "Close"):102" "$message" 2>/dev/null
            local exit_code=$?
            if [[ $exit_code -eq 101 ]]; then
                open_folder=true
                dialog_shown=true
            elif [[ $exit_code -eq 102 ]]; then
                open_folder=false
                dialog_shown=true
            fi
        fi

        if [[ "$dialog_shown" == "false" ]]; then
            sleep 3
        fi
    done

    if [[ "$open_folder" == "true" ]]; then
        if command -v thunar &>/dev/null; then
            thunar /var/log/elive-upgrader &
        elif command -v xdg-open &>/dev/null; then
            xdg-open /var/log/elive-upgrader &
        fi
    fi

    sleep 10

    # Friendly, non-intrusive Patreon motivation
    local patreon_msg
    patreon_msg="$(eval_gettext "Elive is a project built with passion and sustained entirely by users like you.\n\nIf you enjoy these continuous updates, improvements, and next unique features, consider supporting this work.\n\nEvery contribution helps keep the project alive and growing.\n\n\nDo you want to see the Premium features of Elive?." )"
    if command -v ${yad%% *} &>/dev/null; then
        if $yad --question --title="$(eval_gettext "Elive Premium available")" --text="$patreon_msg" --width=520 2>/dev/null; then
            if command -v web-launcher &>/dev/null; then
                web-launcher "https://patreon.com/c/elive" &
            elif command -v xdg-open &>/dev/null; then
                xdg-open "https://patreon.com/c/elive" &
            fi
        fi
    elif command -v zenity &>/dev/null; then
        if zenity --question --title="$(eval_gettext "Elive Premium available")" --text="$patreon_msg" --width=520 2>/dev/null; then
            if command -v web-launcher &>/dev/null; then
                web-launcher "https://patreon.com/c/elive" &
            elif command -v xdg-open &>/dev/null; then
                xdg-open "https://patreon.com/c/elive" &
            fi
        fi
    fi
}

# Check if this is a successful upgrade completion (not a failure recovery)
if [[ ! -s "$RECOVERY_FLAG" ]] && [[ -s "/var/cache/elive-upgrader/notify-upgrade-complete" ]]; then
    show_success_notification
    : > "/var/cache/elive-upgrader/notify-upgrade-complete" 2>/dev/null || true
    rm -f "/var/cache/elive-upgrader/notify-upgrade-complete" 2>/dev/null || true
    rm -f "/usr/lib/elive-upgrader/recovery-notification" 2>/dev/null || true
    # Clean up backups asynchronously in the background so desktop startup is not delayed
    ( do_backup_cleanup ) &
    exit 0
fi

if [[ ! -s "$RECOVERY_FLAG" ]]; then
    # No recovery needed, clean up backups if flag present and exit
    ( do_backup_cleanup ) &
    exit 0
fi

# Determine which dialog tool to use
show_notification() {
    local title="$1"
    local message="$2"

    if command -v ${yad%% *} &>/dev/null; then
        $yad --warning --title="$title" --text="$message" --width=500 2>/dev/null
        return
    fi

    if command -v zenity &>/dev/null; then
        zenity --warning --title="$title" --text="$message" --width=500 2>/dev/null
        return
    fi

    if command -v kdialog &>/dev/null; then
        kdialog --title "$title" --msgbox "$message" 2>/dev/null
        return
    fi

    if command -v xmessage &>/dev/null; then
        xmessage -center "$message" 2>/dev/null
        return
    fi

    echo "$title: $message" >&2
}

# Build the message
MSG="$(printf "$(eval_gettext "A system upgrade has failed and the system may be in an inconsistent state.\n\nA full OS backup was created before the upgrade attempt.\n\nOptions:\n1. Try to run the upgrade again\n2. Restore the system from the backup\n3. Get help from the Elive community\n\nThe backup is located at: %s\n\nWould you like to restore the system from the backup now?")" "${BACKUP_DIR}")"

TITLE="$(eval_gettext "Elive System Upgrade Recovery")"

# Show the notification
show_notification "$TITLE" "$MSG"

# Ask user what to do
ask_user() {
    local prompt_text="$(eval_gettext "What would you like to do?")"
    local col_action="$(eval_gettext "Action")"
    local col_desc="$(eval_gettext "Description")"
    local opt_restore="$(eval_gettext "Restore system from backup (recommended)")"
    local opt_retry="$(eval_gettext "Try the upgrade again")"
    local opt_ignore="$(eval_gettext "Ignore for now (ask again next login)")"

    if command -v zenity &>/dev/null; then
        zenity --list --title="$TITLE" --text="$prompt_text" \
            --column="$col_action" --column="$col_desc" \
            "restore" "$opt_restore" \
            "retry" "$opt_retry" \
            "ignore" "$opt_ignore" \
            --width=500 --height=300 2>/dev/null
        return
    fi

    if command -v ${yad%% *} &>/dev/null; then
        $yad --list --title="$TITLE" --text="$prompt_text" \
            --column="$col_action" --column="$col_desc" \
            "restore" "$opt_restore" \
            "retry" "$opt_retry" \
            "ignore" "$opt_ignore" \
            --width=500 --height=300 2>/dev/null
        return
    fi

    # Fallback: use a simple text prompt
    echo "$prompt_text"
    echo "1. $opt_restore"
    echo "2. $opt_retry"
    echo "3. $opt_ignore"
    read -p "$(eval_gettext "Enter choice (1/2/3): ")" choice
    case "$choice" in
        1) echo "restore" ;;
        2) echo "retry" ;;
        *) echo "ignore" ;;
    esac
}

CHOICE=$(ask_user)

case "$CHOICE" in
    restore)
        # User wants to restore - needs root
        if command -v gksu &>/dev/null; then
            gksu /usr/lib/elive-upgrader/debian-upgrader --restore-from-backup
        elif command -v pkexec &>/dev/null; then
            pkexec /usr/lib/elive-upgrader/debian-upgrader --restore-from-backup
        elif command -v kdesu &>/dev/null; then
            kdesu /usr/lib/elive-upgrader/debian-upgrader --restore-from-backup
        else
            show_notification "$(eval_gettext "Error")" "$(eval_gettext "Cannot restore: no graphical sudo tool found. Please run from terminal:\nsudo /usr/lib/elive-upgrader/debian-upgrader --restore-from-backup")"
            exit 1
        fi
        ;;
    retry)
        # User wants to retry - launch the upgrader
        if command -v elive-upgrader &>/dev/null; then
            elive-upgrader &
        elif command -v x-terminal-emulator &>/dev/null; then
            x-terminal-emulator -e elive-upgrader &
        elif command -v urxvt &>/dev/null; then
            urxvt -e elive-upgrader &
        elif command -v xterm &>/dev/null; then
            xterm -e elive-upgrader &
        else
            show_notification "$(eval_gettext "Retry")" "$(eval_gettext "Please run the Elive Upgrader from the application menu or terminal to retry the upgrade.")"
        fi
        ;;
    ignore)
        # User ignores - will ask again next login
        exit 0
        ;;
esac

# Remove the notification flag if restore was chosen
if [[ "$CHOICE" == "restore" ]]; then
    rm -f "$RECOVERY_FLAG" 2>/dev/null || true
fi

exit 0
RECOVERY_SCRIPT

    chmod +x "/usr/lib/elive-upgrader/recovery-notification"

    log_info "Recovery notification helper created. User will be notified on next desktop login via elive-upgrader."
}

# Clear recovery notification (after successful upgrade or manual restore)
clear_recovery_notification() {
    log_info "Clearing recovery notification..."
    rm -f "$RECOVERY_NOTIFICATION_FLAG" 2>/dev/null || true
    rm -f "/var/cache/elive-upgrader/notify-upgrade-failed-space" 2>/dev/null || true
    if [[ ! -s "/var/cache/elive-upgrader/notify-upgrade-complete" ]]; then
        rm -f "/usr/lib/elive-upgrader/recovery-notification" 2>/dev/null || true
    fi
}

# State management functions for tracking upgrade across reboots

upgrade_state_read() {
    local key="$1"
    local default="${2:-}"
    if [[ -f "$UPGRADE_STATE_FILE" ]]; then
        local value
        value=$(grep "^${key}=" "$UPGRADE_STATE_FILE" 2>/dev/null | tail -1 | cut -d'"' -f2)
        if [[ -n "$value" ]]; then
            echo "$value"
        else
            echo "$default"
        fi
    else
        echo "$default"
    fi
}

upgrade_state_write() {
    local key="$1"
    local value="$2"
    # Create file if it doesn't exist
    if [[ ! -f "$UPGRADE_STATE_FILE" ]]; then
        touch "$UPGRADE_STATE_FILE"
    fi
    # Remove existing key if present, then append
    sed -i "/^${key}=/d" "$UPGRADE_STATE_FILE"
    echo "${key}=\"${value}\"" >> "$UPGRADE_STATE_FILE"
}

upgrade_state_clear() {
    rm -f "$UPGRADE_STATE_FILE"
}

upgrade_state_get_reboot_count() {
    local count
    count=$(upgrade_state_read "REBOOT_COUNT" "0")
    echo "$count"
}

upgrade_state_increment_reboot() {
    local count
    count=$(upgrade_state_get_reboot_count)
    count=$((count + 1))
    upgrade_state_write "REBOOT_COUNT" "$count"
    log_info "Reboot count during upgrade: $count"

    # Safety: if too many reboots, something is seriously wrong
    if (( count > 5 )); then
        log_error "Too many reboots during upgrade ($count). Aborting upgrade to prevent infinite loop."
        upgrade_state_set_upgrade_in_progress "no"
        restore_sources_on_cancel
        disable_boot_upgrade
        release_lock
        log_info "Waiting 60 seconds before exiting so you can read error messages..."
        for ((i=60; i>0; i--)); do
            echo -ne "Continuing in ${i} seconds...\r"
            sleep 1
        done
        echo ""
        exit 1
    fi
}

upgrade_state_get_current_step() {
    upgrade_state_read "CURRENT_STEP" ""
}

upgrade_state_set_current_step() {
    upgrade_state_write "CURRENT_STEP" "$1"
}

upgrade_state_get_upgrade_in_progress() {
    local val
    val=$(upgrade_state_read "UPGRADE_IN_PROGRESS" "no")
    if [[ "$val" == "yes" ]]; then
        return 0
    else
        return 1
    fi
}

upgrade_state_set_upgrade_in_progress() {
    if [[ "$1" == "yes" ]]; then
        upgrade_state_write "UPGRADE_IN_PROGRESS" "yes"
    else
        upgrade_state_write "UPGRADE_IN_PROGRESS" "no"
    fi
}

# Validate state file integrity
validate_state_file() {
    # Check if state file exists and has required fields
    if [[ ! -f "$UPGRADE_STATE_FILE" ]]; then
        return 1
    fi

    local target_cn
    target_cn=$(upgrade_state_read "TARGET_CODENAME" "")
    if [[ -z "$target_cn" ]]; then
        log_warning "State file exists but TARGET_CODENAME is empty. Cleaning up."
        upgrade_state_clear
        return 1
    fi

    return 0
}

set_progress() {
    local percent="$1"
    local msg="$2"
    if [[ "$BOOT_MODE" == "true" ]]; then
        if [[ -n "$msg" ]]; then
            echo -e "  ${GREEN}[  OK  ]${NC} [$percent%] $msg"
        fi
        if [[ "$USE_PLYMOUTH" == "true" ]] && command -v plymouth &>/dev/null && plymouth --ping &>/dev/null; then
            plymouth update --progress="$percent" 2>/dev/null || true
            if [[ -n "$msg" ]]; then
                plymouth message --text="[$percent%] $msg" 2>/dev/null || true
            fi
        fi
    elif [[ -n "$msg" ]]; then
        echo -e "${GREEN}[INFO]${NC} [$percent%] $msg"
    fi
    # Update step name for logging based on progress
    if [[ "$percent" -ge 5 && "$percent" -lt 30 ]]; then
        CURRENT_STEP_NAME="minimal-upgrade"
    elif [[ "$percent" -ge 30 && "$percent" -lt 80 ]]; then
        CURRENT_STEP_NAME="full-upgrade"
    elif [[ "$percent" -ge 80 && "$percent" -lt 90 ]]; then
        CURRENT_STEP_NAME="kernel-install"
    elif [[ "$percent" -ge 90 && "$percent" -lt 96 ]]; then
        CURRENT_STEP_NAME="initramfs-update"
    elif [[ "$percent" -ge 96 && "$percent" -lt 100 ]]; then
        CURRENT_STEP_NAME="grub-update"
    elif [[ "$percent" -ge 100 ]]; then
        CURRENT_STEP_NAME="completed"
    fi
}

log_info() {
    if [[ "$BOOT_MODE" == "true" ]]; then
        echo -e "  ${GREEN}[  OK  ]${NC} $1"
        if [[ "$USE_PLYMOUTH" == "true" ]] && command -v plymouth &>/dev/null && plymouth --ping &>/dev/null; then
            log_action_msg "$1"
            plymouth message --text="$1" 2>/dev/null || true
        fi
    else
        echo -e "${GREEN}[INFO]${NC} $1"
    fi
}

log_debug() {
    el_debug "$1"
}

trigger_reboot() {
    log_info "=================================================="
    log_info "  SYSTEM IS READY FOR REBOOT TO APPLY CHANGES"
    log_info "=================================================="
    if ((is_openrc)); then
        log_info "Stopping anacron service..."
        rc-service anacron stop 2>/dev/null || service anacron stop 2>/dev/null || /etc/init.d/anacron stop 2>/dev/null || true
        pkill -f anacron 2>/dev/null || true
    elif ((is_systemd)); then
        log_info "Stopping anacron service..."
        systemctl stop anacron.service 2>/dev/null || true
        pkill -f anacron 2>/dev/null || true
    elif ((is_sysvinit)); then
        log_info "Stopping anacron service..."
        service anacron stop 2>/dev/null || /etc/init.d/anacron stop 2>/dev/null || true
        pkill -f anacron 2>/dev/null || true
    fi
    log_info "Rebooting now..."
    sync

    if ((is_systemd)); then
        systemctl reboot 2>/dev/null || reboot || /sbin/reboot || reboot -f || true
    elif ((is_openrc)); then
        rc-service reboot restart 2>/dev/null || openrc-shutdown -r now 2>/dev/null || reboot || /sbin/reboot || reboot -f || true
    elif ((is_sysvinit)); then
        reboot || /sbin/reboot || reboot -f || true
    else
        reboot || /sbin/reboot || reboot -f || true
    fi

    # Ultimate kernel-level emergency reboot fallback (SysRq) if still running
    sleep 5
    log_warning "Standard reboot failed. Triggering emergency kernel reboot..."
    echo 1 > /proc/sys/kernel/sysrq 2>/dev/null || true
    echo b > /proc/sysrq-trigger 2>/dev/null || true
}

log_warning() {
    if [[ "$BOOT_MODE" == "true" ]]; then
        echo -e "  ${YELLOW}[ WARN ]${NC} $1"
        if [[ "$USE_PLYMOUTH" == "true" ]] && command -v plymouth &>/dev/null && plymouth --ping &>/dev/null; then
            log_warning_msg "$1"
            plymouth message --text="WARNING: $1" 2>/dev/null || true
        fi
    fi
    el_warning "$1"
}

log_error() {
    if [[ "$BOOT_MODE" == "true" ]]; then
        echo -e "  ${RED}[ FAIL ]${NC} $1"
        if [[ "$USE_PLYMOUTH" == "true" ]] && command -v plymouth &>/dev/null && plymouth --ping &>/dev/null; then
            log_failure_msg "$1"
            plymouth message --text="ERROR: $1" 2>/dev/null || true
        fi
    fi
    el_error "$1"
}

free_emergency_disk_space() {
    NOREPORTS=1 log_warning "Running emergency disk space cleanup to recover system upgrade..."
    NOREPORTS=1 log_warning "Removing cache and temporal files..."

    # 1. Clean temporary files, caches, trash, and system logs
    # log_info "Cleaning temporary files, browser caches, thumbnail caches, trash, and system logs..."
    # rm -rf /tmp/* /var/tmp/* 2>/dev/null || true

    # Kill processes that lock cache files
    for i in efreetd rhythmbox chromium google-chrome opera palemoon firefox-esr picard netsurf opera
    do
        timeout 10 pkill "$i" 2>/dev/null || pkill -9 -f "$i" 2>/dev/null || true
    done

    # pkill -9 -f google-chrome 2>/dev/null || true

    # Specific user caches and desktop caches across all user directories and root
    rm -rf /home/*/.cache/mesa_shader_cache* \
        /home/*/.cache/MuzicBrainz \
        /home/*/.cache/bkp \
        /home/*/.cache/calibre \
        /home/*/.cache/chromium \
        /home/*/.cache/efreet \
        /home/*/.cache/el_speak \
        /home/*/.cache/google-chrome \
        /home/*/.cache/moonchild_productions \
        /home/*/.cache/mozilla \
        /home/*/.cache/netsurf \
        /home/*/.cache/network \
        /home/*/.cache/opera \
        /home/*/.cache/shotwell \
        /home/*/.cache/efreet \
        /home/*/.cache/rhythmbox \
        /home/*/.cache/thumbnails \
           /root/.cache/* 2>/dev/null || true

    # Delete general cache files (preserving zsh and elive caches)
    find /root/.cache -not -path "*/.cache/zsh/*" -not -path "*/.cache/elive*" -type f -delete 2>/dev/null || true
    find /home/*/.thumbnails /home/*/.local/share/thumbnails /root/.thumbnails /root/.local/share/thumbnails -type f -delete 2>/dev/null || true

    # Clean compile caches
    ccache -c -C 2>/dev/null || true

    # Free Trash & aMule Temp files
    find /home/*/.local/share/Trash /root/.local/share/Trash -type f -delete 2>/dev/null || true
    find /home/*/.aMule/Temp /root/.aMule/Temp -type f -delete 2>/dev/null || true

    # Clean crash recovery files and truncate xsession errors
    rm -rf /home/*/.config/*-crashrecovery-* /root/.config/*-crashrecovery-* 2>/dev/null || true
    for xerr in /home/*/.xsession-errors /root/.xsession-errors; do
        [[ -f "$xerr" ]] && : > "$xerr"
    done

    # Truncate / delete log files and vacuum journal aggressively
    find /var/log -type f \( -name "*.gz" -o -name "*.[0-9]" -o -name "*.old" -o -name "*.log.*" \) -delete 2>/dev/null || true
    find /var/log -type f -size +5M -exec truncate -s 0 {} + 2>/dev/null || true
    if command -v journalctl &>/dev/null; then
        journalctl --vacuum-time=1s 2>/dev/null || true
        journalctl --vacuum-size=1M 2>/dev/null || true
    fi

    # Purge residual configuration files (rc state packages)
    local rc_pkgs
    rc_pkgs=$(dpkg-query -W -f='${db:Status-Status} ${Package}\n' 2>/dev/null | awk '/^not-installed/ {print $2}' || true)
    if [[ -n "$rc_pkgs" ]]; then
        log_info "Purging residual config files to free space..."
        dpkg --purge $rc_pkgs 2>/dev/null || true
    fi

    # Clean Flatpak / Snap temporary caches if present
    rm -rf /var/lib/snapd/cache/* /var/tmp/flatpak-cache-* 2>/dev/null || true

    # Clean unused kernel headers in /usr/src for non-running kernels
    local running_k running_k_base
    running_k=$(uname -r)
    running_k_base="${running_k%-*}"
    for h_dir in /usr/src/linux-headers-*; do
        if [[ -d "$h_dir" ]] && [[ -n "$running_k_base" ]] && [[ "$h_dir" != *"$running_k_base"* ]]; then
            log_info "Removing unneeded kernel headers in $h_dir..."
            rm -rf "$h_dir" 2>/dev/null || true
        fi
    done

    # Clean old /var/backups files keeping only latest
    find /var/backups -type f -name "*.gz" -mtime +7 -delete 2>/dev/null || true

    # 2. Clean APT caches and unused packages
    log_info "Cleaning APT cache and autoremoving unused packages..."
    log_info "Executing: apt-get autoclean -y"
    apt-get autoclean -y 2>/dev/null || true
    log_info "Executing: apt-get autoremove --purge -y"
    apt-get autoremove --purge -y 2>/dev/null || true

    # 3. Remove older system backup directories if present
    local old_backups
    old_backups=$(ls -d /root/debian-upgrade-backup-* 2>/dev/null || true)
    if [[ -n "$old_backups" ]]; then
        NOREPORTS=1 log_warning "Removing backup directories in /root..."
        rm -rf /root/debian-upgrade-backup-* 2>/dev/null || true
    fi

    # 4. Clean downloaded package archives if space is still critically low
    local free_kb
    free_kb=$(df -P / 2>/dev/null | awk 'NR==2 {print $4}')
    if [[ -n "$free_kb" ]] && (( free_kb < 1048576 )); then
        NOREPORTS=1 log_warning "Free space still under 1GB ($(( free_kb / 1024 )) MB). Cleaning APT archive cache..."
        log_info "Executing: apt-get clean"
        apt-get clean 2>/dev/null || true
    fi

    local new_free_kb
    new_free_kb=$(df -P / 2>/dev/null | awk 'NR==2 {print $4}')
    log_info "Emergency disk space cleanup finished. Current free space on /: $(( new_free_kb / 1024 )) MB."
}

repair_apt() {
    # Fix keyrings first
    fix_unsupported_keyrings

    # Check if root free space is low (< 1 GB) and attempt space recovery
    local root_free_kb
    root_free_kb=$(df -P / 2>/dev/null | awk 'NR==2 {print $4}')
    if [[ -n "$root_free_kb" ]] && (( root_free_kb < 1048576 )); then
        NOREPORTS=1 log_warning "Low disk space detected during repair ($(( root_free_kb / 1024 )) MB free)."
        free_emergency_disk_space
    fi

    # Check if repair is actually needed
    if dpkg --audit >/dev/null 2>&1 && apt-get check >/dev/null 2>&1; then
        return 0
    fi

    NOREPORTS=1 log_warning "Attempting to repair APT/dpkg state..."

    # Remove stale lock files that may exist from a crashed upgrade
    rm -f /var/lib/dpkg/lock-frontend 2>/dev/null || true
    rm -f /var/lib/dpkg/lock 2>/dev/null || true
    rm -f /var/lib/apt/lists/lock 2>/dev/null || true
    rm -f /var/cache/apt/archives/lock 2>/dev/null || true

    # Kill any stuck apt/dpkg processes that might hold locks
    if pgrep -f "apt-get|dpkg" >/dev/null 2>&1; then
        log_warning "Killing stuck apt/dpkg processes..."
        pkill -f "apt-get|dpkg" 2>/dev/null || true
        sleep 2
        pkill -9 -f "apt-get|dpkg" 2>/dev/null || true
        sleep 1
    fi

    # Remove lock files again after killing processes
    rm -f /var/lib/dpkg/lock-frontend 2>/dev/null || true
    rm -f /var/lib/dpkg/lock 2>/dev/null || true
    rm -f /var/lib/apt/lists/lock 2>/dev/null || true
    rm -f /var/cache/apt/archives/lock 2>/dev/null || true

    # Rebuild dpkg status if corrupted (e.g. from hard reboot)
    if ! dpkg --audit >/dev/null 2>&1; then
        log_warning "dpkg audit failed, attempting to rebuild package database..."
        # Try to recover dpkg status from the backup
        if [[ -f /var/lib/dpkg/status-old ]]; then
            cp -a /var/lib/dpkg/status-old /var/lib/dpkg/status 2>/dev/null || true
        fi
        # If status is empty or missing, try to rebuild from backups
        if [[ ! -s /var/lib/dpkg/status ]]; then
            log_warning "dpkg status is empty, attempting to rebuild from backups..."
            local backup
            backup=$(ls -t /var/backups/dpkg.status.* 2>/dev/null | head -n 1)
            if [[ -n "$backup" ]]; then
                if [[ "$backup" == *.gz ]]; then
                    zcat "$backup" > /var/lib/dpkg/status 2>/dev/null || true
                else
                    cp -a "$backup" /var/lib/dpkg/status 2>/dev/null || true
                fi
            fi
        fi
    fi

    # Clean partial archives that may be corrupted
    rm -rf /var/cache/apt/archives/partial/* 2>/dev/null || true

    # Configure any unconfigured packages with forced options
    dpkg --configure -a --force-confdef --force-confold --force-overwrite 2>/dev/null || dpkg --configure -a 2>/dev/null || true

    # Check for packages stuck in half-configured / broken state via dpkg audit (inspired by 0020_drivers_extra.hook.chroot)
    local broken_pkgs
    broken_pkgs="$(LC_ALL=C dpkg -C 2>/dev/null | grep -E "^\s+[a-zA-Z0-9]" | awk '{print $1}' | tr '\n' ' ' || true)"
    if [[ -n "$broken_pkgs" ]]; then
        log_warning "Found packages in broken/half-configured state: $broken_pkgs"
        local broken_kernels
        broken_kernels="$(echo "$broken_pkgs" | tr ' ' '\n' | grep -E '^linux-(image|headers)-' || true)"
        if [[ -n "$broken_kernels" ]]; then
            log_warning "Purging unconfigurable candidate kernel package(s): $broken_kernels"
            dpkg --purge $broken_kernels 2>/dev/null || true
        fi
    fi

    # Fix broken dependencies with aggressive forced options
    log_info "Executing: apt-get install -f -y -o Dpkg::Options::=\"--force-confdef\" -o Dpkg::Options::=\"--force-confold\" -o Dpkg::Options::=\"--force-overwrite\" --allow-downgrades --allow-remove-essential --allow-change-held-packages"
    apt-get install -f -y -o Dpkg::Options::="--force-confdef" -o Dpkg::Options::="--force-confold" -o Dpkg::Options::="--force-overwrite" --allow-downgrades --allow-remove-essential --allow-change-held-packages 2>/dev/null || true

    # Fix broken packages
    log_info "Executing: apt-get --fix-broken install -y -o Dpkg::Options::=\"--force-confdef\" -o Dpkg::Options::=\"--force-confold\" -o Dpkg::Options::=\"--force-overwrite\" --allow-downgrades --allow-remove-essential --allow-change-held-packages"
    apt-get --fix-broken install -y -o Dpkg::Options::="--force-confdef" -o Dpkg::Options::="--force-confold" -o Dpkg::Options::="--force-overwrite" --allow-downgrades --allow-remove-essential --allow-change-held-packages 2>/dev/null || true

    # Update package lists with fix-missing
    log_info "Executing: apt-get update --fix-missing"
    apt-get update --fix-missing 2>/dev/null || true

    # Final check
    if ! dpkg --audit >/dev/null 2>&1 || ! apt-get check >/dev/null 2>&1; then
        log_warning "APT/dpkg state may still be broken after repair attempts. Continuing anyway..."
    fi
}

check_and_install_missing_firmware() {
    local initrd_log="${1:-/tmp/.initrd.log}"
    if [[ -f "$initrd_log" ]] && grep -qs "Possible missing firmware" "$initrd_log"; then
        log_info "Searching for required missing firmwares..."

        # Ensure apt-file is installed
        if ! [[ -f /var/lib/dpkg/info/apt-file.list ]]; then
            log_info "Installing apt-file..."
            safe_apt_run apt-get install -y --no-install-recommends apt-file || true
        fi

        # Ensure apt-file database is updated
        if command -v apt-file &>/dev/null; then
            log_info "Updating apt-file database..."
            apt-file update 2>/dev/null || apt-file update 2>/dev/null || true
        fi

        local firmwares_file="/tmp/.firmwares-to-install.txt"
        rm -f "$firmwares_file" 2>/dev/null || true

        if command -v apt-file &>/dev/null; then
            while read -ru 3 line; do
                [[ -z "$line" ]] && continue
                apt-file search "$line" 2>/dev/null | sed -e 's|: .*$||g'
            done 3<<< "$( grep -E "Possible missing firmware" "$initrd_log" | sed -e 's|^.*Possible missing firmware ||g' -e 's| for .*$||g' -e 's|[^/]*$||' | sort -u )" | sort -u > "$firmwares_file"
        fi

        if [[ -s "$firmwares_file" ]]; then
            local firmwares_list
            firmwares_list=$(grep -vE '^\s*$' "$firmwares_file" | sort -u | tr '\n' ' ')
            if [[ -n "$firmwares_list" ]]; then
                log_info "Installing missing firmwares: $firmwares_list"
                if ! safe_apt_run apt-get install -y --no-install-recommends $firmwares_list; then
                    log_error "Error installing missing firmwares ($firmwares_list)"
                    safe_apt_run apt-get -f -y install || true
                fi

                log_info "Rebuilding initramfs after firmware installation..."
                rm -f /boot/initrd.img* /tmp/.initrd.log 2>/dev/null || true
                if run_and_show_plymouth_on_boot update-initramfs -c -k all 2>&1 | tee /tmp/.initrd.log; then
                    log_info "Initramfs rebuilt successfully after firmware installation."
                else
                    log_warning "First attempt to rebuild initramfs failed, retrying for running kernel..."
                    if run_and_show_plymouth_on_boot update-initramfs -c -k "$(uname -r)" 2>&1 | tee /tmp/.initrd.log; then
                        log_info "Initramfs rebuilt successfully for kernel $(uname -r)."
                    else
                        log_error "Problem generating initramfs after firmware installation!"
                    fi
                fi
                rm -f /boot/*old-dkms 2>/dev/null || true
            fi
        else
            log_info "No matching firmware packages found via apt-file."
        fi
        rm -f "$firmwares_file" "$initrd_log" 2>/dev/null || true
    fi
}

safe_apt_run() {
    local attempt=1
    local max_attempts=5
    local exit_code=0
    local dkms_failure_package=""
    local all_dkms_packages=""
    local err_log
    err_log=$(mktemp)

    local cmd=("$@")
    if [[ "${cmd[0]}" == "apt-get" ]]; then
        cmd=("apt-get" "-o" "Dpkg::Options::=--force-confdef" "-o" "Dpkg::Options::=--force-confold" "-o" "Dpkg::Options::=--force-overwrite" "${cmd[@]:1}")
    fi

    while (( attempt <= max_attempts )); do
        # Run the command and capture exit code explicitly
        log_info "Executing command: ${cmd[*]}"
        local run_log
        run_log=$(mktemp)
        set +o pipefail
        run_and_show_plymouth_on_boot "${cmd[@]}" 2>&1 | tee "$run_log"
        exit_code=${PIPESTATUS[0]}
        cat "$run_log" > "$err_log"
        rm -f "$run_log"
        if [[ "$exit_code" -eq 0 ]]; then
            rm -f "$err_log"
            return 0
        fi

        # Check for out-of-space error
        if grep -qi "No space left on device\|ENOSPC\|write error\|not enough free space\|No space left\|dpkg-deb.*error" "$err_log" 2>/dev/null || \
           (( $(df -P / | awk 'NR==2 {print $4}') < 1048576 )); then
            log_warning "Out of space condition detected during package operation! Executing emergency disk recovery pass $attempt..."
            free_emergency_disk_space
            # Force clean cached deb archives if free space is still dangerously low
            local cur_free_kb
            cur_free_kb=$(df -P / 2>/dev/null | awk 'NR==2 {print $4}')
            if [[ -n "$cur_free_kb" ]] && (( cur_free_kb < 1048576 )); then
                log_warning "Space critically low ($(( cur_free_kb / 1024 )) MB free). Cleaning cached .deb packages..."
                apt-get clean 2>/dev/null || true
            fi
            dpkg --configure -a --force-confdef --force-confold --force-overwrite 2>/dev/null || true
        fi

        # Detect package overwrite conflict errors and try to force dpkg overwrite
        if grep -qi "trying to overwrite.*which is also in package" "$err_log" 2>/dev/null; then
            log_warning "Package overwrite conflict detected. Forcing dpkg configuration with overwrite..."
            dpkg --configure -a --force-overwrite --force-confdef --force-confold 2>/dev/null || true
        fi

        local err_msg
        err_msg=$(cat "$err_log" | tail -n 5)
        NOREPORTS=1 log_warning "Command failed (exit code: $exit_code): $*. Attempt $attempt of $max_attempts. Error: $err_msg"
        # Don't repair on the last attempt - just fail
        if (( attempt < max_attempts )); then
            repair_apt
        fi
        (( attempt++ ))
    done
    local err_msg
    err_msg=$(cat "$err_log" | tail -n 5)
    rm -f "$err_log"
    log_error "Command failed permanently after $max_attempts attempts: $*. Error: $err_msg"
    return "$exit_code"
}

run_and_show_plymouth_on_boot() {
    log_info "Executing: $*"
    local is_apt=false
    local start_pct=0
    local end_pct=100

    local arg
    for arg in "$@"; do
        case "$arg" in
            upgrade)
                is_apt=true
                start_pct=10
                end_pct=30
                break
                ;;
            full-upgrade|dist-upgrade)
                is_apt=true
                start_pct=30
                end_pct=80
                break
                ;;
            install)
                is_apt=true
                start_pct=80
                end_pct=85
                break
                ;;
        esac
    done

    if [[ "$BOOT_MODE" == "true" ]]; then
        stty sane 2>/dev/null || true

        if [[ "$USE_PLYMOUTH" == "true" ]] && command -v plymouth &> /dev/null && plymouth --ping &> /dev/null; then
            if [[ "$is_apt" == "true" ]]; then
                # Estimate total packages to process
                local total_pkgs
                total_pkgs=$( LC_ALL=C apt-get -s "${@:2}" 2>/dev/null | grep -E "^Inst " | wc -l)
                if [[ "$total_pkgs" -eq 0 ]]; then
                    total_pkgs=50 # fallback
                fi
                local total_steps=$(( total_pkgs * 2 ))
                local current_step=0

                (
                    set -o pipefail
                    "$@" 2>&1 | while IFS= read -r line; do
                        echo "$line"
                        if [[ "$line" =~ "Unpacking " || "$line" =~ "Setting up " ]]; then
                            (( current_step++ ))
                            if (( current_step > total_steps )); then
                                current_step=$total_steps
                            fi
                            local pct=$(( start_pct + (end_pct - start_pct) * current_step / total_steps ))
                            plymouth update --progress="$pct" 2>/dev/null || true
                            plymouth message --text="[$pct%] $line" 2>/dev/null || true
                        else
                            plymouth message --text="$line" 2>/dev/null || true
                        fi
                    done
                )
            else
                plymouth message --text="Running: $*" 2>/dev/null || true
                (
                    set -o pipefail
                    "$@" 2>&1 | while IFS= read -r line; do
                        echo "$line"
                        plymouth message --text="$line" 2>/dev/null || true
                    done
                )
            fi
        else
            "$@"
        fi
    else
        "$@"
    fi
}

check_root() {
    if [[ $EUID -ne 0 ]]; then
        log_error "This script must be run as root"
        exit 1
    fi
}

check_tmux() {
    if ! command -v tmux &> /dev/null; then
        log_error "tmux is not installed. Please install it first: apt install tmux"
        exit 1
    fi
}

backup_system() {
    # Check if a system backup already exists for this upgrade session
    local existing_backup
    existing_backup=$(ls -d /root/debian-upgrade-backup-* 2>/dev/null | tail -n 1 || true)

    if [[ -n "$existing_backup" && -d "$existing_backup" ]]; then
        BACKUP_DIR="$existing_backup"
        log_info "System backup already exists at: $BACKUP_DIR (skipping duplicate backup)"
        return 0
    fi

    log_info "Starting system backup..."

    # Delete any previous backup directories to ensure we keep only one
    rm -rf /root/debian-upgrade-backup-* 2>/dev/null || true

    # Create backup directory
    BACKUP_DIR="/root/debian-upgrade-backup-$(date +%Y%m%d-%H%M%S)"
    mkdir -p "$BACKUP_DIR"

    # Backup critical files
    cp -a /etc "$BACKUP_DIR/etc"
    cp -a /var/lib/dpkg "$BACKUP_DIR/var-lib-dpkg"
    cp -a /var/lib/apt/extended_states "$BACKUP_DIR/"
    cp -a /var/lib/aptitude/pkgstates "$BACKUP_DIR/" 2>/dev/null || true

    # Backup package selections
    dpkg --get-selections '*' > "$BACKUP_DIR/dpkg-selections.txt"

    # Backup sources.list.d
    cp -a /etc/apt/sources.list.d "$BACKUP_DIR/sources.list.d"

    log_info "Backup completed at: $BACKUP_DIR"
}

check_current_release() {
    local current_release_full
    current_release_full=$(cat /etc/debian_version 2>/dev/null || echo "unknown")
    current_version_number=$(echo "$current_release_full" | cut -d'.' -f1)

    if [[ "$current_version_number" =~ ^[0-9]+$ ]]; then
        current_codename=${DEBIAN_VERSIONS[$current_version_number]:-}
    else
        current_codename=""
        for cn in "${!DEBIAN_CODENAMES[@]}"; do
            if [[ "$current_release_full" == *"$cn"* ]]; then
                current_codename="$cn"
                current_version_number=${DEBIAN_CODENAMES[$cn]}
                break
            fi
        done
    fi

    if [[ -z "$current_codename" ]]; then
        log_error "Could not determine current Debian version or it is unsupported. Found: $current_release_full"
        return 1
    fi

    log_info "Current Debian version: $current_version_number ($current_codename)"

    local target_version_number=${DEBIAN_CODENAMES[$TARGET_CODENAME]:-}
    if [[ -z "$target_version_number" ]]; then
        log_error "Unsupported target version: $TARGET_CODENAME"
        usage
    fi

    # If target is already reached, signal success (upgrade complete)
    if (( target_version_number == current_version_number )); then
        log_info "Target version $TARGET_CODENAME is already the current version. Upgrade is complete."
        return 2
    fi

    if (( target_version_number < current_version_number )); then
        log_error "Target version is older than the current version."
        log_error "Current: $current_version_number ($current_codename), Target: $target_version_number ($TARGET_CODENAME)"
        return 1
    fi

    log_info "Target Debian version: $target_version_number ($TARGET_CODENAME)"
}

update_sources() {
    local codename="$1"

    if [[ -z "$current_codename" ]]; then
        log_error "Cannot update sources: current codename is empty. This should not happen."
        return 1
    fi

    # Skip if already on the target codename (shouldn't happen in normal flow, but safety check)
    if [[ "$current_codename" == "$codename" ]]; then
        log_info "Sources already set to $codename, skipping update."
        return 0
    fi

    log_info "Updating APT sources and preferences from $current_codename to $codename..."

    # Direct sed replacement in sources.list
    if [[ -f /etc/apt/sources.list ]]; then
        # Use a backup before modifying
        cp /etc/apt/sources.list "/etc/apt/sources.list.bak.$(date +%s)" 2>/dev/null || true
        sed -i "s|$current_codename|$codename|g" /etc/apt/sources.list
    fi

    # Direct sed replacement in all files under sources.list.d/
    for file in /etc/apt/sources.list.d/*; do
        if [[ -f "$file" ]]; then
            sed -i "s|$current_codename|$codename|g" "$file"
        fi
    done

    # Direct sed replacement in preferences
    if [[ -f /etc/apt/preferences ]]; then
        cp /etc/apt/preferences "/etc/apt/preferences.bak.$(date +%s)" 2>/dev/null || true
        sed -i "s|$current_codename|$codename|g" /etc/apt/preferences
    fi

    # Direct sed replacement in all files under preferences.d/
    for file in /etc/apt/preferences.d/*; do
        if [[ -f "$file" ]]; then
            sed -i "s|$current_codename|$codename|g" "$file"
        fi
    done

    # Direct sed replacement in apt.conf
    if [[ -f /etc/apt/apt.conf ]]; then
        cp /etc/apt/apt.conf "/etc/apt/apt.conf.bak.$(date +%s)" 2>/dev/null || true
        sed -i "s|$current_codename|$codename|g" /etc/apt/apt.conf
    fi

    # Direct sed replacement in all files under apt.conf.d/
    for file in /etc/apt/apt.conf.d/*; do
        if [[ -f "$file" ]]; then
            sed -i "s|$current_codename|$codename|g" "$file"
        fi
    done

    # Update /etc/elive-version
    if [[ -f /etc/elive-version ]]; then
        cp /etc/elive-version "/etc/elive-version.bak.$(date +%s)" 2>/dev/null || true
        sed -i "s|$current_codename|$codename|g" /etc/elive-version
    fi

    # Update /etc/lsb-release
    if [[ -f /etc/lsb-release ]]; then
        cp /etc/lsb-release "/etc/lsb-release.bak.$(date +%s)" 2>/dev/null || true
        local new_ver=${DEBIAN_CODENAMES[$codename]:-}
        local old_ver=${DEBIAN_CODENAMES[$current_codename]:-}
        if [[ -n "$new_ver" && -n "$old_ver" ]]; then
            sed -i "s|DISTRIB_RELEASE=\"$old_ver\"|DISTRIB_RELEASE=\"$new_ver\"|g" /etc/lsb-release
        fi
        sed -i "s|DISTRIB_CODENAME=\"$current_codename\"|DISTRIB_CODENAME=\"$codename\"|g" /etc/lsb-release
        sed -i "s|$current_codename|$codename|g" /etc/lsb-release
    fi

    log_info "APT sources, preferences, and configurations updated from $current_codename to $codename"
}

cleanup_packages() {
    log_info "Cleaning up packages..."

    # Remove obsolete packages
    log_info "Removing obsolete packages..."
    run_and_show_plymouth_on_boot apt-get autoremove --purge -y || true

    # Clean package cache
    run_and_show_plymouth_on_boot apt-get clean || true

    # Remove leftover configuration and temporary upgrader backup files
    find /etc -name '*.dpkg-*' -o -name '*.ucf-*' -o -name '*.merge-error' -o -name '*.bak-upgrader' -o -name 'sources.list.bak.*' -o -name 'preferences.bak.*' -o -name 'apt.conf.bak.*' -o -name 'elive-version.bak.*' -o -name 'lsb-release.bak.*' -delete 2>/dev/null || true
}

check_disk_space() {
    log_info "Checking disk space for upgrade (minimum ${MIN_FREE_SPACE_GB} GB required)..."

    local min_free_kb=$MIN_FREE_SPACE_KB
    local root_free_kb
    root_free_kb=$(df -P / | awk 'NR==2 {print $4}')

    local var_dev root_dev var_free_kb
    var_dev=$(df -P /var 2>/dev/null | awk 'NR==2 {print $1}')
    root_dev=$(df -P / 2>/dev/null | awk 'NR==2 {print $1}')
    var_free_kb=$(df -P /var 2>/dev/null | awk 'NR==2 {print $4}')

    local space_ok=true

    if (( root_free_kb < min_free_kb )); then
        space_ok=false
    fi

    if [[ "$var_dev" != "$root_dev" ]] && [[ -n "$var_free_kb" ]] && (( var_free_kb < min_free_kb )); then
        space_ok=false
    fi

    if [[ "$space_ok" == "false" ]]; then
        NOREPORTS=1 log_warning "Free space is below ${MIN_FREE_SPACE_GB} GB threshold. Attempting emergency disk space cleanup..."
        free_emergency_disk_space
        root_free_kb=$(df -P / | awk 'NR==2 {print $4}')
        space_ok=true
        if (( root_free_kb < min_free_kb )); then
            space_ok=false
        fi
        if [[ "$var_dev" != "$root_dev" ]] && [[ -n "$var_free_kb" ]]; then
            var_free_kb=$(df -P /var 2>/dev/null | awk 'NR==2 {print $4}')
            if (( var_free_kb < min_free_kb )); then
                space_ok=false
            fi
        fi
    fi

    if [[ "$space_ok" == "false" ]]; then
        local lowest_free_kb=$root_free_kb
        if [[ "$var_dev" != "$root_dev" ]] && [[ -n "$var_free_kb" ]] && (( var_free_kb < root_free_kb )); then
            lowest_free_kb=$var_free_kb
        fi

        local msg="Insufficient space (Free: $(( lowest_free_kb / 1024 )) MB, Required: $(( MIN_FREE_SPACE_GB * 1024 )) MB). Rebooting..."
        log_error "$msg"

        # Flag for desktop notification on reboot
        mkdir -p /var/cache/elive-upgrader 2>/dev/null || true
        echo "$(( lowest_free_kb / 1024 ))" > /var/cache/elive-upgrader/notify-upgrade-failed-space 2>/dev/null || true
        chmod 666 /var/cache/elive-upgrader/notify-upgrade-failed-space 2>/dev/null || true

        mkdir -p /var/log/elive-upgrader 2>/dev/null || true
        echo "$msg" > /var/log/elive-upgrader/last-boot-upgrade-failure 2>/dev/null || true
        chmod 666 /var/log/elive-upgrader/last-boot-upgrade-failure 2>/dev/null || true

        create_recovery_notification "failure"
        restore_sources_on_cancel
        upgrade_state_clear
        disable_boot_upgrade

        log_info "Rebooting in 10 seconds due to insufficient space so you can return to desktop..."
        sleep 10
        trigger_reboot
        exit 1
    fi

    log_info "Disk space check passed (at least ${MIN_FREE_SPACE_GB} GB free space available)."
}

stop_services() {
    log_info "Stopping non-essential services..."

    local state_file_f="/var/cache/elive-upgrader/suspended-services.txt"
    mkdir -p /var/cache/elive-upgrader 2>/dev/null || true

    # Stop monitoring services that might interfere and record state
    if ((is_systemd)); then
        for srv in monit.service micro-evtd.service; do
            if systemctl is-active --quiet "$srv" 2>/dev/null; then
                if ! grep -qxF "SYSTEMD_ACTIVE:$srv" "$state_file_f" 2>/dev/null; then
                    echo "SYSTEMD_ACTIVE:$srv" >> "$state_file_f"
                fi
                systemctl stop "$srv" 2>/dev/null || true
            fi
        done
    elif ((is_openrc)); then
        for srv in monit micro-evtd; do
            if rc-service "$srv" status >/dev/null 2>&1; then
                if ! grep -qxF "OPENRC_ACTIVE:$srv" "$state_file_f" 2>/dev/null; then
                    echo "OPENRC_ACTIVE:$srv" >> "$state_file_f"
                fi
                rc-service "$srv" stop 2>/dev/null || true
            fi
        done
    elif ((is_sysvinit)); then
        for srv in monit micro-evtd; do
            if service "$srv" status >/dev/null 2>&1; then
                if ! grep -qxF "SYSV_ACTIVE:$srv" "$state_file_f" 2>/dev/null; then
                    echo "SYSV_ACTIVE:$srv" >> "$state_file_f"
                fi
                service "$srv" stop 2>/dev/null || true
            fi
        done
    fi

    if command -v microapl &> /dev/null; then
        microapl -a system_set_watchdog off 2>/dev/null || true
    fi
}

perform_upgrade() {
    set_progress 5 "Starting upgrade process..."

    # Ensure we start with a repaired state
    repair_apt

    # Detect if NVIDIA drivers are currently installed and working
    local nvidia_was_installed=false
    local nvidia_version=""

    log_info "Checking for NVIDIA drivers and installer tool..."
    if command -v nvidia-privative-drivers-install &>/dev/null; then
        if nvidia-privative-drivers-install -t &>/dev/null; then
            nvidia_was_installed=true
            if command -v get_nvidia_version &>/dev/null; then
                nvidia_version=$(get_nvidia_version)
            fi
            log_info "NVIDIA drivers detected working on system (Version: ${nvidia_version:-current})."
            draw_banner "NVIDIA DRIVERS DETECTED WORKING (Version: ${nvidia_version:-current})" "$MAGENTA"
        else
            log_info "No working NVIDIA drivers detected by test (-t)."
        fi

        log_info "Running NVIDIA driver removal tool before system upgrade..."
        if nvidia-privative-drivers-install -q -u 2>/dev/null || nvidia-privative-drivers-install -u 2>/dev/null; then
            log_info "NVIDIA drivers removal/cleanup completed successfully."
        else
            log_warning "NVIDIA driver removal command finished with an error. Continuing with upgrade..."
        fi
    else
        log_info "NVIDIA tool 'nvidia-privative-drivers-install' not found on system. Skipping NVIDIA detection and removal."
    fi

    # Update package lists (offline fallback if no internet, using local cache)
    safe_apt_run apt-get update || log_warning "Failed to update package lists, proceeding with cached lists for offline upgrade."

    # Check disk space now that package lists are updated
    check_disk_space

    # Minimal upgrade first
    set_progress 10 "Performing minimal upgrade (preparing packages)..."
    if ! safe_apt_run apt-get upgrade --without-new-pkgs -y -o Dpkg::Options::="--force-confdef" -o Dpkg::Options::="--force-confold" -o Dpkg::Options::="--force-overwrite" --allow-downgrades --allow-remove-essential --allow-change-held-packages; then
        log_error "Minimal upgrade step failed!"
        return 1
    fi

    # Check space in /boot for two kernels (approx 100MB each * 1.3 tolerance = 260MB / 266240 KB)
    local boot_space_free
    boot_space_free=$(df -P /boot | awk 'NR==2 {print $4}')
    local required_boot_kb=266240
    local can_install_new_kernel=true

    if (( boot_space_free < required_boot_kb )); then
        log_info "Low space in /boot (${boot_space_free} KB free). We need at least ${required_boot_kb} KB to safely keep two kernels."
        log_info "Only the current kernel will be kept, and new kernel installation will be skipped."
        can_install_new_kernel=false
    fi

    # Always remove older kernels to free up space, keeping only the currently running one
    log_info "Removing older kernels to free up space..."
    # Find existing kernels, excluding the currently running one to keep the system bootable
    local running_kernel
    running_kernel=$(uname -r)
    local existing_kernel_packages
    existing_kernel_packages=$(dpkg-query -W -f='${Package}\n' 'linux-image-[0-9]*' 2>/dev/null | grep -vF "$running_kernel" || true)
    if [[ -n "$existing_kernel_packages" ]]; then
        log_info "Found older kernel packages to remove:"
        echo "$existing_kernel_packages"
        # shellcheck disable=SC2086
        safe_apt_run apt-get remove --purge -y $existing_kernel_packages || true
    else
        log_info "No older kernel packages found to remove."
    fi

    if [[ "$can_install_new_kernel" == "false" ]]; then
        # Hold existing kernel metapackages to prevent new kernel installation during full-upgrade
        local installed_meta
        installed_meta=$(dpkg -l 'linux-image-*' 2>/dev/null | grep '^ii' | grep -E 'amd64|686|arm64|rt-' | grep -v -E 'linux-image-[0-9]' | awk '{print $2}' || true)
        if [[ -n "$installed_meta" ]]; then
            log_info "Holding kernel metapackages to prevent new kernel installation: $installed_meta"
            apt-mark hold $installed_meta
        fi
    fi

    # Full upgrade
    set_progress 30 "Performing full upgrade (building modules & configuring packages, please wait)..."

    # Start a background monitor to stream DKMS compilation logs to Plymouth
    (
        while pgrep -f "apt-get|dpkg|dkms" >/dev/null; do
            sleep 5
            # Find the most recently modified dkms make.log
            local latest_dkms_log
            latest_dkms_log=$(ls -t /var/lib/dkms/*/*/build/make.log 2>/dev/null | head -n 1 || true)
            if [[ -n "$latest_dkms_log" && -f "$latest_dkms_log" ]]; then
                local last_line
                last_line=$(tail -n 1 "$latest_dkms_log" | cut -c1-60)
                if [[ -n "$last_line" ]]; then
                    plymouth message --text="Compiling module: $last_line" 2>/dev/null || true
                fi
            fi
        done
    ) &
    local dkms_monitor_pid=$!

    if ! safe_apt_run apt-get full-upgrade -y -o Dpkg::Options::="--force-confdef" -o Dpkg::Options::="--force-confold" -o Dpkg::Options::="--force-overwrite" \
        --allow-downgrades --allow-remove-essential --allow-change-held-packages \
        -o APT::Immediate-Configure=0 -o APT::Force-LoopBreak=1; then
        kill "$dkms_monitor_pid" 2>/dev/null || true
        log_warning "Full upgrade step failed after initial retries. Attempting emergency recovery pass..."
        dpkg --configure -a --force-confdef --force-confold --force-overwrite 2>/dev/null || true
        if safe_apt_run apt-get dist-upgrade --fix-broken -y -o Dpkg::Options::="--force-confdef" -o Dpkg::Options::="--force-confold" -o Dpkg::Options::="--force-overwrite" \
            --allow-downgrades --allow-remove-essential --allow-change-held-packages \
            -o APT::Immediate-Configure=0 -o APT::Force-LoopBreak=1; then
            log_info "Emergency recovery pass succeeded! Full upgrade completed."
        else
            log_error "Full upgrade step failed!"
            return 1
        fi
    fi

    kill "$dkms_monitor_pid" 2>/dev/null || true

    # Install latest kernel and headers metapackages if not present and space permits
    if [[ "$can_install_new_kernel" == "true" ]]; then
        if ! dpkg -l 'linux-image*' | grep -q '^ii.*meta'; then
            arch=$(dpkg --print-architecture)
            case $arch in
                amd64)
                    kernel_meta="linux-image-amd64"
                    headers_meta="linux-headers-amd64"
                    ;;
                i386)
                    kernel_meta="linux-image-686"
                    headers_meta="linux-headers-686"
                    ;;
                arm64)
                    kernel_meta="linux-image-arm64"
                    headers_meta="linux-headers-arm64"
                    ;;
                *)
                    kernel_meta="linux-image-$arch"
                    headers_meta="linux-headers-$arch"
                    ;;
            esac

            local pkgs_to_install=""
            if apt-cache show "$kernel_meta" &> /dev/null; then
                pkgs_to_install="$kernel_meta"
            fi
            if [[ -n "$headers_meta" ]] && apt-cache show "$headers_meta" &> /dev/null; then
                if [[ -n "$pkgs_to_install" ]]; then
                    pkgs_to_install="$pkgs_to_install $headers_meta"
                else
                    pkgs_to_install="$headers_meta"
                fi
            fi

            if [[ -n "$pkgs_to_install" ]]; then
                set_progress 80 "Installing kernel and headers metapackages: $pkgs_to_install"
                safe_apt_run apt-get install -y $pkgs_to_install
            fi
        fi
    else
        set_progress 80 "Skipping kernel and headers metapackages installation/upgrade due to low space in /boot."
    fi

    # Re-install NVIDIA drivers if they were installed and working prior to upgrade
    if [[ "$nvidia_was_installed" == "true" ]]; then
        draw_banner "RE-INSTALLING NVIDIA PROPRIETARY DRIVERS" "$MAGENTA"
        log_info "Re-installing NVIDIA proprietary drivers after system and kernel upgrade..."
        if [[ -n "$nvidia_version" ]]; then
            log_info "Running NVIDIA installer tool with explicit model version: $nvidia_version..."
            if nvidia-privative-drivers-install -s -f -a -m "$nvidia_version"; then
                log_info "NVIDIA driver version $nvidia_version re-installed successfully."
            else
                log_warning "Failed to re-install NVIDIA driver version $nvidia_version with explicit model."
                log_info "Running NVIDIA installer tool with automatic model detection fallback..."
                if nvidia-privative-drivers-install -s -f -a ; then
                    log_info "NVIDIA driver re-installed successfully using automatic model detection."
                else
                    log_error "Failed to re-install NVIDIA drivers using automatic model detection!"
                fi
            fi
        else
            log_info "Running NVIDIA installer tool with automatic model detection..."
            if nvidia-privative-drivers-install -s -f -a ; then
                log_info "NVIDIA driver re-installed successfully."
            else
                log_error "Failed to re-install NVIDIA drivers!"
            fi
        fi

        local nvidia_install_log="/var/log/nvidia-privative-drivers-install.log"
        if [[ -f "$nvidia_install_log" ]]; then
            log_info "Verifying NVIDIA driver installation status per kernel:"
            local nv_status_line
            local nv_installed_kernels=()
            local nv_failed_kernels=()

            while IFS= read -r nv_status_line; do
                if [[ "$nv_status_line" == NVIDIA_KERNEL_INSTALLED:* ]]; then
                    local kver="${nv_status_line#NVIDIA_KERNEL_INSTALLED: }"
                    nv_installed_kernels+=("$kver")
                    log_info " - Installed for kernel: $kver"
                elif [[ "$nv_status_line" == NVIDIA_KERNEL_NOT_INSTALLED:* ]]; then
                    local kver="${nv_status_line#NVIDIA_KERNEL_NOT_INSTALLED: }"
                    nv_failed_kernels+=("$kver")
                    log_warning " - NOT installed for kernel: $kver"
                fi
            done < <(grep -E "^NVIDIA_KERNEL_(NOT_)?INSTALLED:" "$nvidia_install_log" 2>/dev/null || true)

            if [[ ${#nv_installed_kernels[@]} -gt 0 ]] && [[ ${#nv_failed_kernels[@]} -gt 0 ]]; then
                log_warning "NVIDIA drivers installed successfully for kernel(s) [${nv_installed_kernels[*]}], but failed for [${nv_failed_kernels[*]}]."
                log_info "Uninstalling kernel(s) where NVIDIA drivers failed to install..."

                local running_k
                running_k="$(uname -r)"
                local kver_to_remove
                for kver_to_remove in "${nv_failed_kernels[@]}"; do
                    if [[ "$kver_to_remove" == "$running_k" ]]; then
                        log_warning "Skipping removal of running kernel ($running_k) despite NVIDIA driver installation failure."
                        continue
                    fi

                    local pkgs_to_purge=()
                    if dpkg -l "linux-image-${kver_to_remove}" 2>/dev/null | grep -q '^ii'; then
                        pkgs_to_purge+=("linux-image-${kver_to_remove}")
                    fi
                    if dpkg -l "linux-headers-${kver_to_remove}" 2>/dev/null | grep -q '^ii'; then
                        pkgs_to_purge+=("linux-headers-${kver_to_remove}")
                    fi

                    if [[ ${#pkgs_to_purge[@]} -gt 0 ]]; then
                        log_info "Purging kernel package(s) where NVIDIA driver failed: ${pkgs_to_purge[*]}"
                        safe_apt_run apt-get remove --purge -y "${pkgs_to_purge[@]}" || true
                        rm -f "/boot/vmlinuz-${kver_to_remove}"* "/boot/initrd.img-${kver_to_remove}"* "/boot/System.map-${kver_to_remove}"* "/boot/config-${kver_to_remove}"* 2>/dev/null || true
                    fi
                done
            fi

            if [[ ${#nv_failed_kernels[@]} -gt 0 ]]; then
                # Check if we have any upgraded kernel where NVIDIA installed successfully
                local has_working_upgraded_kernel=false
                local running_k
                running_k="$(uname -r)"
                if [[ ${#nv_installed_kernels[@]} -gt 0 ]]; then
                    for kver in "${nv_installed_kernels[@]}"; do
                        if [[ -n "$kver" ]] && [[ "$kver" != "$running_k" ]] && dpkg -l "linux-image-${kver}" 2>/dev/null | grep -q '^ii'; then
                            has_working_upgraded_kernel=true
                            break
                        fi
                    done
                fi

                if [[ "$has_working_upgraded_kernel" == "false" ]]; then
                    log_warning "No upgraded kernel has working NVIDIA drivers. Searching for previous available kernel versions to try..."
                    local arch
                    arch=$(dpkg --print-architecture)

                    # Get available linux-image packages sorted by version descending, picking only the newest per major.minor branch
                    local avail_kernels=()
                    local seen_branches=" "
                    local kpkg=""
                    while IFS= read -r kpkg; do
                        [[ -z "$kpkg" ]] && continue
                        local kver="${kpkg#linux-image-}"
                        # Skip if running kernel or already tried/failed or currently installed
                        if [[ -z "$kver" ]] || [[ "$kver" == "$running_k" ]]; then continue; fi
                        local failed_already=false
                        local fk=""
                        for fk in "${nv_failed_kernels[@]}"; do
                            if [[ "$kver" == "$fk" ]]; then
                                failed_already=true
                                break
                            fi
                        done
                        if [[ "$failed_already" == "true" ]]; then continue; fi
                        if dpkg -l "linux-image-${kver}" 2>/dev/null | grep -q '^ii'; then continue; fi

                        local branch=""
                        branch="$(echo "$kver" | grep -oE '^[0-9]+\.[0-9]+' || echo "$kver")"
                        if [[ "$seen_branches" == *" $branch "* ]]; then
                            continue
                        fi
                        seen_branches="${seen_branches}${branch} "

                        avail_kernels+=("$kver")
                    done < <(apt-cache search "^linux-image-[0-9]" 2>/dev/null | awk '{print $1}' | grep -E "^linux-image-[0-9].*-$arch$" | grep -vE "(unsigned|dbg|cloud|rt-|meta-package|template)" | sort -rV)

                    local attempt=0
                    local max_attempts=4
                    local cand_kver=""
                    for cand_kver in "${avail_kernels[@]}"; do
                        if (( attempt >= max_attempts )); then
                            log_warning "Reached maximum kernel fallback attempts ($max_attempts)."
                            break
                        fi
                        (( attempt++ ))
                        log_info "Fallback kernel attempt $attempt of $max_attempts: Trying kernel $cand_kver..."

                        local pkgs_to_inst=()
                        if apt-cache show "linux-image-${cand_kver}" &>/dev/null; then
                            pkgs_to_inst+=("linux-image-${cand_kver}")
                        fi
                        if apt-cache show "linux-headers-${cand_kver}" &>/dev/null; then
                            pkgs_to_inst+=("linux-headers-${cand_kver}")
                        fi

                        if [[ ${#pkgs_to_inst[@]} -eq 0 ]]; then
                            log_warning "Package linux-image-${cand_kver} not found in apt cache, skipping."
                            continue
                        fi

                        # Attempt candidate kernel installation
                        local inst_success=true
                        log_info "Executing: apt-get -o Dpkg::Options::=\"--force-confdef\" -o Dpkg::Options::=\"--force-confold\" -o Dpkg::Options::=\"--force-overwrite\" install -y --no-install-recommends ${pkgs_to_inst[*]}"
                        apt-get -o Dpkg::Options::="--force-confdef" -o Dpkg::Options::="--force-confold" -o Dpkg::Options::="--force-overwrite" install -y --no-install-recommends "${pkgs_to_inst[@]}" || inst_success=false

                        if [[ "$inst_success" == "true" ]]; then
                            log_info "Re-running NVIDIA driver installer for candidate kernel $cand_kver..."
                            if [[ -n "$nvidia_version" ]]; then
                                nvidia-privative-drivers-install -s -f -a -m "$nvidia_version" 2>/dev/null || nvidia-privative-drivers-install -s -f -a 2>/dev/null || true
                            else
                                nvidia-privative-drivers-install -s -f -a 2>/dev/null || true
                            fi

                            if grep -q "^NVIDIA_KERNEL_INSTALLED: ${cand_kver}" "$nvidia_install_log" 2>/dev/null; then
                                log_info "NVIDIA drivers installed successfully for kernel $cand_kver!"
                                break
                            else
                                log_warning "NVIDIA drivers failed to install for kernel $cand_kver. Removing..."
                                inst_success=false
                            fi
                        fi

                        if [[ "$inst_success" == "false" ]]; then
                            log_warning "Kernel candidate $cand_kver failed. Cleaning up failed kernel packages..."
                            dpkg --purge "${pkgs_to_inst[@]}" 2>/dev/null || true
                            log_info "Executing: apt-get remove --purge -y ${pkgs_to_inst[*]}"
                            apt-get remove --purge -y "${pkgs_to_inst[@]}" 2>/dev/null || true
                            rm -f "/boot/vmlinuz-${cand_kver}"* "/boot/initrd.img-${cand_kver}"* "/boot/System.map-${cand_kver}"* "/boot/config-${cand_kver}"* 2>/dev/null || true
                            log_info "Executing: apt-get -f install -y"
                            apt-get -f install -y 2>/dev/null || true
                            nv_failed_kernels+=("$cand_kver")
                        fi
                    done

                    log_info "Cleaning up APT state after kernel fallback attempts..."
                    safe_apt_run apt-get -f install -y || true
                fi
            fi
        fi

        log_info "Updating initramfs and checking for missing firmware after NVIDIA driver installation..."
        rm -f /tmp/.initrd.log 2>/dev/null || true
        run_and_show_plymouth_on_boot update-initramfs -u -k all 2>&1 | tee /tmp/.initrd.log || true
        check_and_install_missing_firmware
    else
        log_info "NVIDIA drivers were not active prior to upgrade. Skipping NVIDIA driver re-installation."
    fi

    set_progress 85 "Creating new initramfs for all kernels (building modules, please wait)..."
    log_info "Removing existing initramfs images to force fresh creation..."
    rm -f /boot/initrd.img-* /tmp/.initrd.log 2>/dev/null || true
    if run_and_show_plymouth_on_boot update-initramfs -c -k all 2>&1 | tee /tmp/.initrd.log; then
        log_info "New initramfs created successfully."
    else
        log_error "Failed to create new initramfs!"
    fi
    check_and_install_missing_firmware

    set_progress 90 "Updating GRUB configuration (detecting operating systems)..."
    if run_and_show_plymouth_on_boot update-grub; then
        log_info "GRUB configuration updated successfully."
    else
        log_error "Failed to update GRUB configuration!"
    fi
    set_progress 95 "Upgrade step completed."
}

post_upgrade_cleanup() {
    set_progress 96 "Performing post-upgrade cleanup..."

    # Remove obsolete packages
    safe_apt_run apt-get autoremove --purge -y

    # Remove leftover config files
    safe_apt_run apt-get purge '?config-files' -y || true

    # Clean package cache
    safe_apt_run apt-get clean

    # Update alternatives if needed
    update-alternatives --all --auto 2>/dev/null || true

    # Remove leftover configuration and temporary upgrader backup files
    find /etc -name '*.dpkg-*' -o -name '*.ucf-*' -o -name '*.merge-error' -o -name '*.bak-upgrader' -o -name 'sources.list.bak.*' -o -name 'preferences.bak.*' -o -name 'apt.conf.bak.*' -o -name 'elive-version.bak.*' -o -name 'lsb-release.bak.*' -delete 2>/dev/null || true

    # allow all interfaces (wifi device names can change)
    sed -i -e '/^interface-name=/d' "/tmp/system-connections/"* "/etc/NetworkManager/system-connections/"* 2>/dev/null || true

    restore_tty1_and_dms
}

verify_upgrade() {
    set_progress 98 "Verifying upgrade..."

    # Refresh distro variables (is_trixie, is_bookworm, etc.) after upgrade
    source /usr/lib/elive-tools/functions 2>/dev/null || true

    # Check Debian version
    new_release=$(cat /etc/debian_version)
    log_info "New Debian version: $new_release"

    # Check for broken packages
    if dpkg -l | grep -q '^rc'; then
        NOREPORTS=1 log_warning "Some packages are in rc (removed but config files remain) state"
    fi

    # Check for held packages
    held_packages=$( LC_ALL=C apt-mark showhold)
    if [[ -n "$held_packages" ]]; then
        log_warning "The following packages are on hold:"
        echo "$held_packages"
    fi
}

sanitize_upgrade_logs() {
    log_info "Sanitizing upgrade logs to remove private user information..."

    local log_files=()
    while IFS= read -r f; do
        [[ -f "$f" ]] && log_files+=("$f")
    done < <(find /var/log/elive-upgrader -maxdepth 1 -type f -name 'debian-upgrade-*.log' 2>/dev/null || true)

    if [[ ${#log_files[@]} -eq 0 ]]; then
        return 0
    fi

    # Collect usernames to sanitize
    local users=()
    for u_dir in /home/*; do
        if [[ -d "$u_dir" ]]; then
            local u_name
            u_name=$(basename "$u_dir")
            [[ -n "$u_name" && "$u_name" != "*" ]] && users+=("$u_name")
        fi
    done
    [[ -n "${SUDO_USER:-}" ]] && users+=("$SUDO_USER")
    [[ -n "${USER:-}" ]] && users+=("$USER")
    [[ -n "${LOGNAME:-}" ]] && users+=("$LOGNAME")

    # Collect hostname
    local host_name
    host_name=$(hostname 2>/dev/null || cat /etc/hostname 2>/dev/null || true)

    # Collect machine ID
    local machine_id
    machine_id=$(tr -d '\r\n ' < /etc/machine-id 2>/dev/null || tr -d '\r\n ' < /var/lib/dbus/machine-id 2>/dev/null || true)

    for log_f in "${log_files[@]}"; do
        chmod 644 "$log_f" 2>/dev/null || true

        # Replace user home directories and usernames
        for u in "${users[@]}"; do
            [[ -z "$u" || "$u" == "root" ]] && continue
            sed -i "s|/home/${u}|/home/user|g" "$log_f" 2>/dev/null || true
            sed -i -E "s/\b${u}\b/user/g" "$log_f" 2>/dev/null || true
        done
    done

    # Replace custom hostname
    if [[ -n "$host_name" && "$host_name" != "localhost" && "$host_name" != "elive" ]]; then
        log_info " - Sanitizing system hostname..."
        for log_f in "${log_files[@]}"; do
            sed -i -E "s/\b${host_name}\b/elive-host/g" "$log_f" 2>/dev/null || true
        done
    fi

    # Replace machine ID
    if [[ -n "$machine_id" && ${#machine_id} -ge 32 ]]; then
        log_info " - Sanitizing machine ID..."
        for log_f in "${log_files[@]}"; do
            sed -i "s|${machine_id}|xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx|g" "$log_f" 2>/dev/null || true
        done
    fi

    log_info " - Sanitizing IPv4 addresses..."
    for log_f in "${log_files[@]}"; do
        sed -i -E 's/\b(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\b/x.x.x.x/g' "$log_f" 2>/dev/null || true
    done

    log_info " - Sanitizing MAC addresses..."
    for log_f in "${log_files[@]}"; do
        sed -i -E 's/\b([0-9a-Fa-f]{2}[:-]){5}([0-9a-Fa-f]{2})\b/xx:xx:xx:xx:xx:xx/g' "$log_f" 2>/dev/null || true
    done

    log_info " - Cleaning progress floods and package status entries from logs..."
    for log_f in "${log_files[@]}"; do
        # Remove Inst/Conf/Remv entries, Reading database progress lines, Preparing to unpack lines, and rsync progress flood lines
        tr -d '\r' < "$log_f" | grep -v -E '^\s*(Inst|Conf|Remv)\b|ir-chk=|xfr#|\b[0-9]+%\s+[0-9.]+[kKMGT]?B/s|\(Reading database \.\.\.|^\s*Preparing to unpack\b' | sed '/^$/N;/^\n$/D' > "${log_f}.tmp" 2>/dev/null || true
        if [[ -s "${log_f}.tmp" ]]; then
            mv -f "${log_f}.tmp" "$log_f" 2>/dev/null || true
        else
            rm -f "${log_f}.tmp" 2>/dev/null || true
        fi
    done

    log_info "Upgrade logs sanitized successfully."
}

send_upgrade_report() {
    log_info "Sending upgrade report to Elive server..."

    local eliveversion id architecture exit_status logs_tmp is_reports_sent=0
    local prepare_log="" boot_log="" report_copy_f="" timestamp_v="" removed_pkgs_file=""

    source /etc/elive/settings 2>/dev/null || true
    [[ -z "$conf_send_debug_reports_email" ]] && conf_send_debug_reports_email="anonymous"

    eliveversion="$( awk '$1 ~ /elive-version/ {($1="");print $0}' /etc/elive-version 2>/dev/null | sed 's/^\ //g' )"
    id="$( el_get_machine_id )"
    architecture="$( el_architecture "host" 2>/dev/null || dpkg --print-architecture 2>/dev/null || echo "unknown" )"
    exit_status=0
    timestamp_v="$(date +%Y%m%d-%H%M%S)"

    logs_tmp="$( mktemp /tmp/debian-upgrade-report.XXXXXX )"

    prepare_log="$(ls -t /var/log/elive-upgrader/debian-upgrade-*-prepare-system-*.log 2>/dev/null | head -n 1 || true)"
    boot_log="${LOG_FILE:-}"
    if [[ -z "$boot_log" ]] || [[ ! -s "$boot_log" ]]; then
        boot_log="$(ls -t /var/log/elive-upgrader/debian-upgrade-*-boot-console-upgrade-*.log 2>/dev/null | head -n 1 || true)"
    fi
    removed_pkgs_file="$(ls -t /var/log/elive-upgrader/debian-upgrade-packages-removed-*.txt 2>/dev/null | head -n 1 || true)"
    local obsolete_pkgs_file
    obsolete_pkgs_file="$(ls -t /var/log/elive-upgrader/debian-upgrade-packages-obsolete-*.txt 2>/dev/null | head -n 1 || true)"

    function build_report_content() {
        if [[ -n "$removed_pkgs_file" && -s "$removed_pkgs_file" ]]; then
            echo "======================================================================"
            echo "REMOVED PACKAGES (Present before upgrade, removed / missing after):"
            echo "======================================================================"
            cat "$removed_pkgs_file"
            echo "======================================================================"
            echo ""
        fi
        if [[ -n "$obsolete_pkgs_file" && -s "$obsolete_pkgs_file" ]]; then
            echo "======================================================================"
            echo "OBSOLETE / UNMAINTAINED PACKAGES (Not present in target release):"
            echo "======================================================================"
            cat "$obsolete_pkgs_file"
            echo "======================================================================"
            echo ""
        fi
        if [[ -n "$prepare_log" && -s "$prepare_log" && -n "$boot_log" && -s "$boot_log" && "$prepare_log" != "$boot_log" ]]; then
            cat "$prepare_log"
            echo -e "\n\n======================================================================\n=================== BOOT CONSOLE UPGRADE LOG =========================\n======================================================================\n\n"
            cat "$boot_log"
        elif [[ -n "$boot_log" && -s "$boot_log" ]]; then
            cat "$boot_log"
        elif [[ -n "$prepare_log" && -s "$prepare_log" ]]; then
            cat "$prepare_log"
        fi
    }

    if { [[ -n "$prepare_log" && -s "$prepare_log" ]] || [[ -n "$boot_log" && -s "$boot_log" ]] || [[ -n "$removed_pkgs_file" && -s "$removed_pkgs_file" ]]; } ; then
        report_copy_f="/var/log/elive-upgrader/debian-upgrade-report-sent-${timestamp_v}.txt"
        {
            echo "======================================================================"
            echo "ELIVE DISTRO UPGRADE - REPORT SUMMARY"
            echo "======================================================================"
            echo "Report ID: ${id}"
            echo "User Email / Contact: ${conf_send_debug_reports_email}"
            echo "Architecture: ${architecture}"
            echo "Elive Version: ${eliveversion}"
            echo "Date: $(date)"
            echo "======================================================================"
            echo ""
            build_report_content
        } > "$report_copy_f" 2>/dev/null || true
        chmod 644 "$report_copy_f" 2>/dev/null || true

        log_info "A readable copy of the upgrade report was saved to: ${report_copy_f}"

        if el_verify_internet 2>/dev/null ; then
            if command -v nc &>/dev/null || [[ -e "/var/lib/dpkg/info/netcat-openbsd.list" ]] ; then
                {
                    echo -e "ELIVE_DATA_TYPE: 2::${id}::transfer::text::distroupgrade_debug::0::::${conf_send_debug_reports_email}::${exit_status}::${architecture}::${eliveversion}::"
                    build_report_content | gzip -9 -f
                } > "$logs_tmp"

                if nc -N www.elivecd.org 60001 -w 20 < "$logs_tmp" 2>/dev/null ; then
                    is_reports_sent=1
                else
                    sleep 2
                    if nc -N www.elivecd.org 60001 -w 30 < "$logs_tmp" 2>/dev/null ; then
                        is_reports_sent=1
                    fi
                fi

                if ((is_reports_sent)) ; then
                    log_info "Upgrade report sent successfully (ID: $id)."
                else
                    NOREPORTS=1 log_warning "Could not send upgrade report to Elive server."
                fi
            fi
        fi
    fi

    rm -f "$logs_tmp" 2>/dev/null || true

    local partials_dir="/var/log/elive-upgrader/partials"
    mkdir -p "$partials_dir" 2>/dev/null || true
    chmod 755 "$partials_dir" 2>/dev/null || true

    local f
    for f in /var/log/elive-upgrader/*; do
        if [[ -f "$f" ]]; then
            case "$(basename "$f")" in
                debian-upgrade-report-sent-*.txt|debian-upgrade-packages-*.txt)
                    ;;
                *)
                    mv -f "$f" "$partials_dir/" 2>/dev/null || true
                    ;;
            esac
        fi
    done

    log_info "Waiting 10 seconds before rebooting..."
    sleep 10
}

# Expand wildcard patterns and remove packages that were not installed before the upgrade
process_remove_if_not_before(){
    local target_codename_v="${1}"
    local marker_file_f="/var/cache/elive-upgrader/remove-if-not-before-done"
    local pkgs_before_file_f="/var/log/elive-upgrader/debian-upgrade-packages-before-${target_codename_v}.txt"
    local pending_list_f="/var/cache/elive-upgrader/pending-remove-if-not-before.txt"
    local hook_file_f=""
    local pattern_v=""
    local expanded_pkgs_v=""
    local filtered_v=""
    local pkg_v=""
    local pkg_base_v=""
    local pkgs_to_remove_v=""
    local apt_exit_v=0

    if [[ -f "${marker_file_f}" ]]; then
        log_info "remove-if-not-before check already completed previously, skipping."
        return 0
    fi

    if [[ ! -f "${pkgs_before_file_f}" ]]; then
        log_info "No packages-before file found at ${pkgs_before_file_f}, skipping remove-if-not-before check."
        return 0
    fi

    if [[ ! -f "${pending_list_f}" ]]; then
        log_info "No pending remove-if-not-before hooks found."
        return 0
    fi

    log_info "Processing remove-if-not-before hooks..."
    while read -r hook_file_f || [[ -n "${hook_file_f}" ]]; do
        [[ -z "${hook_file_f}" ]] && continue
        if [[ -f "${hook_file_f}" ]]; then
            log_info "Processing remove-if-not-before hook: ${hook_file_f}"
            while read -r pattern_v || [[ -n "${pattern_v}" ]]; do
                [[ -z "${pattern_v}" ]] && continue
                case "${pattern_v}" in
                    \#*) continue ;;
                esac

                log_debug "Checking pattern '${pattern_v}' against packages-before file"

                expanded_pkgs_v="$(dpkg-query -W -f='${db:Status-Status} ${Package}\n' "${pattern_v}" 2>/dev/null | grep '^installed ' | cut -d' ' -f2- || true)"

                if [[ -z "${expanded_pkgs_v}" ]]; then
                    log_debug "dpkg-query returned no results, trying apt list fallback..."
                    expanded_pkgs_v="$(apt list --installed "${pattern_v}" 2>/dev/null | grep -v "^Listing" | cut -d'/' -f1 | grep -v "^$" || true)"
                    if [[ -n "${expanded_pkgs_v}" ]]; then
                        filtered_v=""
                        for pkg_v in ${expanded_pkgs_v}; do
                            if dpkg-query -W -f='${db:Status-Status}' "${pkg_v}" 2>/dev/null | grep -q '^installed'; then
                                filtered_v="${filtered_v} ${pkg_v}"
                            fi
                        done
                        expanded_pkgs_v="$(echo "${filtered_v}" | xargs)"
                    fi
                fi

                pkgs_to_remove_v=""
                if [[ -n "${expanded_pkgs_v}" ]]; then
                    for pkg_v in ${expanded_pkgs_v}; do
                        pkg_base_v="$(echo "${pkg_v}" | cut -d':' -f1)"
                        if ! grep -qxF "${pkg_v}" "${pkgs_before_file_f}" && ! grep -qxF "${pkg_base_v}" "${pkgs_before_file_f}"; then
                            log_info "Package '${pkg_v}' was NOT present before upgrade. Adding to removal list."
                            pkgs_to_remove_v="${pkgs_to_remove_v} ${pkg_v}"
                        else
                            log_info "Package '${pkg_v}' was present before upgrade. Keeping."
                        fi
                    done
                    pkgs_to_remove_v="$(echo "${pkgs_to_remove_v}" | xargs)"
                fi

                if [[ -n "${pkgs_to_remove_v}" ]]; then
                    log_info "Removing packages: ${pkgs_to_remove_v}"
                    safe_apt_run apt-get remove --purge -y ${pkgs_to_remove_v}
                    apt_exit_v=$?
                    if [[ "${apt_exit_v}" -ne 0 ]]; then
                        log_warning "apt-get remove failed with exit code ${apt_exit_v} for packages: ${pkgs_to_remove_v}"
                    else
                        log_info "Successfully removed packages: ${pkgs_to_remove_v}"
                    fi
                else
                    log_info "No packages matching pattern '${pattern_v}' need to be removed."
                fi
            done < "${hook_file_f}"
        fi
    done < "${pending_list_f}"

    rm -f "${pending_list_f}"
    rm -rf /var/cache/elive-upgrader/active-distro-upgrade-hook 2>/dev/null || true
    mkdir -p "$(dirname "${marker_file_f}")"
    touch "${marker_file_f}"
    return 0
}

reconfigure_setcap_packages() {
    log_info "Reconfiguring packages with setcap capabilities..."

    # reconfigure needed packages:
    # setcap issues (ping from user, etc):
    local setcap_pkgs
    setcap_pkgs=$(grep -El "\W+setcap\W+" /var/lib/dpkg/info/*postinst 2>/dev/null | sed -e 's|\:.*$||g' -e 's|\.postinst||g' -e 's|^.*/||g' || true)
    for i in $setcap_pkgs
    do
        for j in /var/lib/dpkg/info/${i}.list /var/lib/dpkg/info/${i}:*.list
        do
            if [[ -f "$j" ]] ; then
                j="${j##*/}"
                j="${j%.list}"
                log_info "Reconfiguring $j..."
                if ! timeout 620 dpkg-reconfigure -fnoninteractive "$j" 1>/dev/null 2>&1 ; then
                    log_warning "Failed to reconfigure '$j'"
                fi
            fi
        done
    done
}

# Helper to extract packages from debian-upgrade-packages-to-install-if-before.txt
# that were installed in the system before the upgrade
get_packages_if_before() {
    local hook_file="$1"
    local pkgs_before_file="${2:-/var/log/elive-upgrader/debian-upgrade-packages-before-${TARGET_CODENAME}.txt}"
    if [[ ! -f "$pkgs_before_file" ]]; then
        pkgs_before_file="$(ls -t /var/log/elive-upgrader/debian-upgrade-packages-before-*.txt 2>/dev/null | head -n 1 || true)"
    fi

    if [[ ! -f "$hook_file" ]] || [[ ! -f "$pkgs_before_file" ]]; then
        return 0
    fi

    local pkgs_to_install=""
    local line entry pattern_regex matched_pkgs pkg_base p

    while read -r line || [[ -n "$line" ]]; do
        [[ -z "$line" ]] && continue
        case "$line" in
            \#*) continue ;;
        esac

        for entry in $line; do
            [[ -z "$entry" ]] && continue
            case "$entry" in
                \#*) break ;;
            esac

            if [[ "$entry" == *"*"* ]]; then
                pattern_regex="^$(echo "$entry" | sed 's/\./\\./g; s/\*/.*/g')$"
                matched_pkgs="$(grep -E "$pattern_regex" "$pkgs_before_file" 2>/dev/null || true)"
                if [[ -n "$matched_pkgs" ]]; then
                    for p in $matched_pkgs; do
                        pkgs_to_install="${pkgs_to_install} ${p}"
                    done
                fi
            else
                pkg_base="$(echo "$entry" | cut -d':' -f1)"
                if grep -qxF "$entry" "$pkgs_before_file" 2>/dev/null || grep -qxF "$pkg_base" "$pkgs_before_file" 2>/dev/null; then
                    pkgs_to_install="${pkgs_to_install} ${entry}"
                fi
            fi
        done
    done < "$hook_file"

    echo "$pkgs_to_install" | tr ' ' '\n' | sort -u | tr '\n' ' ' | xargs
}

# Collect all packages specified in debian-upgrade-packages-to-install.txt
# strictly from the distro-upgrade hook directory and active cache
get_distro_upgrade_packages_to_install() {
    local hooks_base="/usr/lib/elive-upgrader/hooks-${current_codename}"
    if [[ "$current_codename" == "wheezy" ]]; then
        hooks_base="/usr/lib/elive-upgrader/hooks"
    fi

    local candidate_files=(
        "${hooks_base}/distro-upgrade/debian-upgrade-packages-to-install.txt"
        "${hooks_base}/distro-upgrade/root/debian-upgrade-packages-to-install.txt"
        "/var/cache/elive-upgrader/active-distro-upgrade-hook/debian-upgrade-packages-to-install.txt"
        "/var/cache/elive-upgrader/active-distro-upgrade-hook/root/debian-upgrade-packages-to-install.txt"
    )

    local candidate_if_before_files=(
        "${hooks_base}/distro-upgrade/debian-upgrade-packages-to-install-if-before.txt"
        "${hooks_base}/distro-upgrade/root/debian-upgrade-packages-to-install-if-before.txt"
        "/var/cache/elive-upgrader/active-distro-upgrade-hook/debian-upgrade-packages-to-install-if-before.txt"
        "/var/cache/elive-upgrader/active-distro-upgrade-hook/root/debian-upgrade-packages-to-install-if-before.txt"
    )

    local processed_files=()
    local pkgs=""
    local f=""
    for f in "${candidate_files[@]}"; do
        if [[ -f "$f" ]]; then
            if [[ ! " ${processed_files[*]:-} " =~ " ${f} " ]]; then
                processed_files+=("$f")
                local content
                content=$(grep -v '^\s*#' "$f" 2>/dev/null | grep -v '^\s*$' || true)
                if [[ -n "$content" ]]; then
                    pkgs="${pkgs} ${content}"
                fi
            fi
        fi
    done

    local processed_if_before=()
    for f in "${candidate_if_before_files[@]}"; do
        if [[ -f "$f" ]]; then
            if [[ ! " ${processed_if_before[*]:-} " =~ " ${f} " ]]; then
                processed_if_before+=("$f")
                local if_before_pkgs
                if_before_pkgs="$(get_packages_if_before "$f")"
                if [[ -n "$if_before_pkgs" ]]; then
                    pkgs="${pkgs} ${if_before_pkgs}"
                fi
            fi
        fi
    done

    # Include kernel and header metapackages
    local arch
    arch=$(dpkg --print-architecture 2>/dev/null || echo "amd64")
    case "$arch" in
        amd64)
            pkgs="${pkgs} linux-image-amd64 linux-headers-amd64"
            ;;
        i386)
            pkgs="${pkgs} linux-image-686 linux-headers-686"
            ;;
        arm64)
            pkgs="${pkgs} linux-image-arm64 linux-headers-arm64"
            ;;
        *)
            pkgs="${pkgs} linux-image-${arch} linux-headers-${arch}"
            ;;
    esac

    echo "${pkgs}" | tr ' ' '\n' | sort -u | tr '\n' ' ' | xargs
}

# Expand wildcard patterns and filter to only installed packages
# Input: space/newline separated package names, may include wildcards like "exim4*"
# Output: space separated list of actually installed packages
filter_installed_packages() {
    local input_pkgs="$1"
    local installed_pkgs=""

    for pkg in $input_pkgs; do
        # Skip empty entries
        [[ -z "$pkg" ]] && continue

        # Check if the package name contains a wildcard
        if [[ "$pkg" == *"*"* ]]; then
            # Expand wildcard to actual installed packages
            local expanded
            expanded=$(dpkg-query -W -f='${db:Status-Status} ${Package}\n' "$pkg" 2>/dev/null | grep '^installed ' | cut -d' ' -f2- || true)
            if [[ -n "$expanded" ]]; then
                installed_pkgs="$installed_pkgs $expanded"
            fi
        else
            # Check if the specific package is installed
            if dpkg-query -W -f='${db:Status-Status}' "$pkg" 2>/dev/null | grep -q '^installed'; then
                installed_pkgs="$installed_pkgs $pkg"
            fi
        fi
    done

    # Trim leading space and output
    echo "${installed_pkgs# }"
}

run_pre_hooks() {
    local codename=$1
    log_info "Running pre-upgrade hooks for $codename..."

    # Run dynamic pre-upgrade hooks from the triggering hook directory
    local hooks_base
    hooks_base="/usr/lib/elive-upgrader/hooks-${current_codename}"
    if [[ "$current_codename" == "wheezy" ]]; then
        hooks_base="/usr/lib/elive-upgrader/hooks"
    fi

    if [[ -d "$hooks_base/distro-upgrade" ]] && [[ -f "$hooks_base/distro-upgrade/debian-upgrade" ]]; then
        # Cache the entire distro-upgrade directory to var/cache
        local cache_dir="/var/cache/elive-upgrader/active-distro-upgrade-hook"
        mkdir -p "$cache_dir" 2>/dev/null || true
        rm -rf "$cache_dir" 2>/dev/null || true
        mkdir -p "$cache_dir" 2>/dev/null || true
        cp -a "$hooks_base/distro-upgrade/." "$cache_dir/" 2>/dev/null || true

        # Save the path of the remove-if-not-before hook pointing to the cached location
        if [[ -f "$cache_dir/debian-upgrade-packages-to-remove-if-not-before.txt" ]]; then
            mkdir -p /var/cache/elive-upgrader 2>/dev/null || true
            local pending_file="/var/cache/elive-upgrader/pending-remove-if-not-before.txt"
            local hook_target="$cache_dir/debian-upgrade-packages-to-remove-if-not-before.txt"
            if ! grep -qxF "$hook_target" "$pending_file" 2>/dev/null; then
                echo "$hook_target" >> "$pending_file"
            fi
        fi

        local hook_dir="$hooks_base/distro-upgrade"
        # 1. Remove packages specified in debian-upgrade-packages-to-remove.txt
        if [[ -f "$hook_dir/debian-upgrade-packages-to-remove.txt" ]]; then
            local pkgs
            local raw_pkgs
            raw_pkgs=$(cat "$hook_dir/debian-upgrade-packages-to-remove.txt" | grep -v "^#" | tr '\n' ' ')
            pkgs=$(filter_installed_packages "$raw_pkgs")
            if [[ -n "$pkgs" ]]; then
                log_info "Removing blocking packages: $pkgs"
                safe_apt_run apt-get remove --purge -y $pkgs || true
            else
                log_info "No blocking packages need to be removed (none are installed)."
            fi
        fi
        # 2. Run pre-upgrade script
        if [[ -x "$hook_dir/debian-upgrade-pre-script.sh" ]]; then
            log_info "Running pre-upgrade script: $hook_dir/debian-upgrade-pre-script.sh"
            "$hook_dir/debian-upgrade-pre-script.sh" || true
        fi
    fi

    case "$codename" in
        "bookworm")
            # Example: Prepare a service for upgrade before Bookworm
            # log_info "Preparing foo.service for Bookworm..."
            ;;
        "trixie")
            # Example: Backup a specific configuration before Trixie
            # if [[ -f /etc/bar.conf ]]; then
            #     log_info "Backing up bar.conf for Trixie..."
            #     cp /etc/bar.conf /etc/bar.conf.pre-trixie
            # fi
            ;;
        "forky")
            # Add pre-hooks for forky here
            ;;
        *)
            log_info "No specific pre-hooks to run for $codename."
            ;;
    esac
}

run_post_hooks() {
    local codename=$1
    log_info "Running post-upgrade hooks for $codename..."

    local cache_dir="/var/cache/elive-upgrader/active-distro-upgrade-hook"
    local hook_dirs=()
    local is_active=false

    if [[ -d "$cache_dir" ]] && [[ -f "$cache_dir/debian-upgrade" ]]; then
        is_active=true
        if [[ -d "$cache_dir/root" ]]; then
            hook_dirs+=("$cache_dir/root")
        else
            hook_dirs+=("$cache_dir")
        fi
        if [[ -d "$cache_dir/user" ]]; then
            hook_dirs+=("$cache_dir/user")
        fi
    else
        # Fallback to the triggering hook directory
        local hooks_base
        hooks_base="/usr/lib/elive-upgrader/hooks-${current_codename}"
        if [[ "$current_codename" == "wheezy" ]]; then
            hooks_base="/usr/lib/elive-upgrader/hooks"
        fi

        if [[ -d "$hooks_base/distro-upgrade" ]] && [[ -f "$hooks_base/distro-upgrade/debian-upgrade" ]]; then
            is_active=true
            if [[ -d "$hooks_base/distro-upgrade/root" ]]; then
                hook_dirs+=("$hooks_base/distro-upgrade/root")
            else
                hook_dirs+=("$hooks_base/distro-upgrade")
            fi
            if [[ -d "$hooks_base/distro-upgrade/user" ]]; then
                hook_dirs+=("$hooks_base/distro-upgrade/user")
            fi
        fi
    fi

    if [[ "$is_active" == "true" ]]; then
        log_info "Updating package lists after full upgrade before installing post-upgrade packages..."
        safe_apt_run apt-get update || log_warning "Failed to update package lists before post-hooks, proceeding with current cache..."

        local all_dirs=()
        if [[ -d "$cache_dir" ]]; then
            all_dirs+=("$cache_dir")
        fi
        for d in "${hook_dirs[@]}"; do
            if [[ "$d" != "$cache_dir" ]]; then
                all_dirs+=("$d")
            fi
        done

        local processed_install_if_before=()
        local all_pkgs_if_before=""

        for hook_dir in "${all_dirs[@]}"; do
            # 1. Collect packages specified in debian-upgrade-packages-to-install-if-before.txt if they were installed before
            if [[ -f "$hook_dir/debian-upgrade-packages-to-install-if-before.txt" ]]; then
                local install_if_file="$hook_dir/debian-upgrade-packages-to-install-if-before.txt"
                if [[ ! " ${processed_install_if_before[*]:-} " =~ " ${install_if_file} " ]]; then
                    processed_install_if_before+=("$install_if_file")
                    local pkgs_if_before
                    pkgs_if_before="$(get_packages_if_before "$install_if_file")"
                    if [[ -n "$pkgs_if_before" ]]; then
                        all_pkgs_if_before="${all_pkgs_if_before} ${pkgs_if_before}"
                    fi
                fi
            fi
        done

        all_pkgs_if_before="$(echo "$all_pkgs_if_before" | tr ' ' '\n' | sort -u | tr '\n' ' ' | xargs)"
        if [[ -n "$all_pkgs_if_before" ]]; then
            log_info "Installing packages present before upgrade (debian-upgrade-packages-to-install-if-before in one shot): $all_pkgs_if_before"
            if ! safe_apt_run apt-get install -y --no-remove -o APT::Get::Remove=0 --no-install-recommends -o Dpkg::Options::="--force-confdef" -o Dpkg::Options::="--force-confold" -o Dpkg::Options::="--force-overwrite" --allow-downgrades --allow-change-held-packages $all_pkgs_if_before; then
                log_warning "Failed to install some if-before post-upgrade packages ($all_pkgs_if_before) without removing other packages. Continuing with post-hooks..."
            fi
        else
            log_info "No packages from debian-upgrade-packages-to-install-if-before need to be installed (none were present before upgrade)."
        fi

        for hook_dir in "${hook_dirs[@]}"; do
            # 2. Install packages specified in debian-upgrade-packages-to-install.txt
            if [[ -f "$hook_dir/debian-upgrade-packages-to-install.txt" ]]; then
                local pkgs
                pkgs=$(cat "$hook_dir/debian-upgrade-packages-to-install.txt" | grep -v "^#" | tr '\n' ' ')
                if [[ -n "$pkgs" ]]; then
                    log_info "Installing supporting packages after full upgrade: $pkgs"
                    if ! safe_apt_run apt-get install -y --no-install-recommends -o Dpkg::Options::="--force-confdef" -o Dpkg::Options::="--force-confold" -o Dpkg::Options::="--force-overwrite" --allow-downgrades --allow-remove-essential --allow-change-held-packages $pkgs; then
                        log_warning "Failed to install some post-upgrade packages ($pkgs). Continuing with post-hooks..."
                    fi
                fi
            fi
        done

        local processed_remove_post=()

        for hook_dir in "${all_dirs[@]}"; do
            # 3. Remove packages specified in debian-upgrade-packages-to-remove-post.txt
            if [[ -f "$hook_dir/debian-upgrade-packages-to-remove-post.txt" ]]; then
                local remove_file="$hook_dir/debian-upgrade-packages-to-remove-post.txt"
                if [[ ! " ${processed_remove_post[*]:-} " =~ " ${remove_file} " ]]; then
                    processed_remove_post+=("$remove_file")
                    local pkgs
                    local raw_pkgs
                    raw_pkgs=$(cat "$remove_file" | grep -v "^#" | tr '\n' ' ')
                    pkgs=$(filter_installed_packages "$raw_pkgs")
                    if [[ -n "$pkgs" ]]; then
                        log_info "Removing post-upgrade packages: $pkgs"
                        if ! safe_apt_run apt-get remove --purge -y $pkgs; then
                            log_error "Failed to remove post-upgrade packages: $pkgs"
                        fi
                    else
                        log_info "No post-upgrade packages need to be removed (none are installed)."
                    fi
                fi
            fi
        done

        for hook_dir in "${hook_dirs[@]}"; do
            # 4. Run post-upgrade script
            if [[ -x "$hook_dir/debian-upgrade-post-script.sh" ]]; then
                log_info "Running post-upgrade script: $hook_dir/debian-upgrade-post-script.sh"
                if ! "$hook_dir/debian-upgrade-post-script.sh"; then
                    log_warning "Post-upgrade script reported warnings: $hook_dir/debian-upgrade-post-script.sh"
                fi
            fi
        done

        # 5. Run another full upgrade pass to ensure no packages remain held back
        log_info "Running full-upgrade pass after post-hooks to ensure no held packages remain..."
        safe_apt_run apt-get full-upgrade -y -o Dpkg::Options::="--force-confdef" -o Dpkg::Options::="--force-confold" -o Dpkg::Options::="--force-overwrite" \
            --allow-downgrades --allow-remove-essential --allow-change-held-packages \
            -o APT::Immediate-Configure=0 -o APT::Force-LoopBreak=1 || true
    fi

    case "$codename" in
        "bookworm")
            # Example: Fix for a specific service in Bookworm
            # log_info "Applying Bookworm-specific fix for foo.service..."
            # systemctl restart foo.service || true
            ;;
        "trixie")
            # Example: Migrate configuration for a package in Trixie
            # if [[ -f /etc/bar.conf ]]; then
            #     log_info "Migrating bar.conf for Trixie..."
            #     migrate-bar-conf --auto
            # fi
            ;;
        "forky")
            # Add post-hooks for forky here
            ;;
        *)
            log_info "No specific post-hooks to run for $codename."
            ;;
    esac
}

enable_boot_upgrade_conf() {
    local target_codename=$1
    local config_file="/etc/default/elive-distro-upgrade"

    if [[ -z "$target_codename" ]]; then
        log_error "Target codename required."
        usage
    fi
    if [[ -z "${DEBIAN_CODENAMES[$target_codename]:-}" ]]; then
        log_error "Unsupported target version: $target_codename"
        usage
    fi

    # Set target codename and verify current release details
    TARGET_CODENAME="$target_codename"
    init_logging
    local check_result
    check_current_release
    check_result=$?
    if [[ "$check_result" -eq 2 ]]; then
        log_info "Target $target_codename is already the current version. Nothing to do."
        exit 0
    fi
    if [[ "$check_result" -ne 0 ]]; then
        exit 1
    fi

    # Check if we have enough free space for a full OS backup during prepare step
    local os_size_kb req_backup_space_kb free_root_kb
    os_size_kb=$(get_os_dirs_size_kb)
    req_backup_space_kb=$(( os_size_kb + MIN_FREE_SPACE_KB + 4194304 ))
    free_root_kb=$(df -P / 2>/dev/null | awk 'NR==2 {print $4}')

    if (( free_root_kb < req_backup_space_kb )); then
        NOREPORTS=1 log_warning "Skipping full OS backup before upgrade due to limited free disk space (Required: $(( req_backup_space_kb / 1024 )) MB, Available: $(( free_root_kb / 1024 )) MB)."
        if ! warn_no_full_backup_dialog "$os_size_kb" "$req_backup_space_kb" "$free_root_kb"; then
            log_info "Upgrade cancelled by user due to insufficient space for full OS backup."
            exit 0
        else
            log_info "User chose to proceed without full OS backup. Setting skip flag for boot mode."
            mkdir -p /var/cache/elive-upgrader 2>/dev/null || true
            touch "$SKIP_FULL_OS_BACKUP_FLAG" 2>/dev/null || true
        fi
    else
        rm -f "$SKIP_FULL_OS_BACKUP_FLAG" 2>/dev/null || true
    fi

    log_info "Pre-downloading packages for the upgrade to $target_codename to ensure a reliable offline/boot-time upgrade..."

    # Remove any stale system backups before starting a new upgrade preparation
    rm -rf /root/debian-upgrade-backup-* 2>/dev/null || true

    # Backup system configuration before changing sources
    backup_system

    # Save list of installed packages before upgrade for reference and if-before checks
    mkdir -p /var/log/elive-upgrader 2>/dev/null || true
    local pkgs_before_file="/var/log/elive-upgrader/debian-upgrade-packages-before-${TARGET_CODENAME}.txt"
    if [[ ! -f "$pkgs_before_file" ]]; then
        log_info "Saving list of installed packages before upgrade..."
        dpkg-query -W -f='${Package} ${Status}\n' 2>/dev/null | grep ' install ok installed' | awk '{print $1}' | cut -d':' -f1 | sort -u > "$pkgs_before_file"
        chmod 644 "$pkgs_before_file" 2>/dev/null || true
    fi

    # Disable automatic clean so downloaded packages are not deleted
    disable_automatic_clean

    # Update sources to target codename to download packages
    # current_codename is set by check_current_release
    if ! update_sources "$target_codename"; then
        log_error "Failed to update sources. Aborting."
        exit 1
    fi

    log_info "Updating package lists..."
    local attempt=1
    local success=false
    while (( attempt <= 5 )); do
        log_info "Attempt $attempt of 5 to update package lists..."
        log_info "Executing: apt-get update"
        if apt-get update; then
            success=true
            break
        fi
        NOREPORTS=1 log_warning "Attempt $attempt failed. Retrying in 5 seconds..."
        sleep 5
        (( attempt++ ))
    done

    if [[ "$success" != "true" ]]; then
        NOREPORTS=1 log_warning "Failed to update package lists after 5 attempts. Restoring sources..."
        # Restore sources using in-place codename reversal rather than copying back old files
        restore_sources_on_cancel
        log_info "Retrying apt-get update with restored sources..."
        log_info "Executing: apt-get update"
        if ! apt-get update; then
            log_error "Failed to update even with restored sources. System may be in an inconsistent state."
        fi
        exit 1
    fi

    # Run pre-upgrade hooks for each step before simulating the upgrade
    local target_version_number=${DEBIAN_CODENAMES[$target_codename]:-}
    if [[ -n "$current_version_number" && -n "$target_version_number" ]] && (( current_version_number < target_version_number )); then
        local start_version=$((current_version_number + 1))
        for version in $(seq "$start_version" "$target_version_number"); do
            local step_codename=${DEBIAN_VERSIONS[$version]}
            if [[ -n "$step_codename" ]]; then
                run_pre_hooks "$step_codename"
            fi
        done
    fi

    log_info "Simulating upgrade to verify compatibility..."
    local sim_failed=0
    local sim_log
    sim_log=$(mktemp)
    log_info "Executing: apt-get -s dist-upgrade -y -q=2 -o Dpkg::Options::=\"--force-confdef\" -o Dpkg::Options::=\"--force-confold\" -o Dpkg::Options::=\"--force-overwrite\" --allow-downgrades --allow-remove-essential --allow-change-held-packages -o APT::Immediate-Configure=0 -o APT::Force-LoopBreak=1"
    if ! apt-get -s dist-upgrade -y -q=2 -o Dpkg::Options::="--force-confdef" -o Dpkg::Options::="--force-confold" -o Dpkg::Options::="--force-overwrite" --allow-downgrades --allow-remove-essential --allow-change-held-packages -o APT::Immediate-Configure=0 -o APT::Force-LoopBreak=1 > "$sim_log" 2>&1; then
        sim_failed=1
    fi
    if [[ -s "$sim_log" ]]; then
        grep -v -E "^(Inst|Conf|Remv)\b" "$sim_log" || true
    fi
    rm -f "$sim_log"

    if ((sim_failed)); then
        log_error "Upgrade simulation failed! The upgrade cannot be performed safely."

        # Restore sources using in-place codename reversal
        restore_sources_on_cancel

        log_info "Restoring package lists back to normal..."
        apt-get update >/dev/null 2>&1 || true

        # Disable boot upgrade trigger
        disable_boot_upgrade

        # Show message to user
        local fail_msg
        fail_msg="$(eval_gettext "The upgrade simulation failed. This usually means there are conflicting packages or broken dependencies that prevent a safe upgrade.\n\nTo upgrade your system, please download a new Elive ISO and perform a normal reinstall in upgrade mode, which will keep your files and settings.")"
        if [[ -n "$DISPLAY" ]] && command -v ${yad%% *} &>/dev/null; then
            $yad --error --text="$fail_msg" --title="$(eval_gettext "Upgrade Simulation Failed")" --width=450
        elif [[ -n "$DISPLAY" ]] && command -v zenity &>/dev/null; then
            zenity --error --text="$fail_msg" --title="$(eval_gettext "Upgrade Simulation Failed")" --width=450 2>/dev/null
        else
            echo -e "\n*** ${RED}ERROR${NC} ***"
            echo -e "$fail_msg"
            echo -e "*****************\n"
        fi
        exit 1
    fi

    log_info "Downloading packages (download-only)..."
    local extra_install_pkgs
    extra_install_pkgs="$(get_distro_upgrade_packages_to_install)"

    local dl_cmd=(apt-get dist-upgrade --download-only -y -o Dpkg::Options::="--force-confdef" -o Dpkg::Options::="--force-confold" -o Dpkg::Options::="--force-overwrite" --allow-downgrades --allow-remove-essential --allow-change-held-packages)
    log_info "Executing: ${dl_cmd[*]}"

    show_predownload_full_options() {
        local title="$(eval_gettext "Disk Full - Insufficient Space")"
        local avail_kb avail_mb
        avail_kb=$(df -P / 2>/dev/null | awk 'NR==2 {print $4}')
        avail_mb=$(( avail_kb / 1024 ))
        local msg="$(printf "$(eval_gettext "The disk is full and upgrade packages could not be downloaded.\n\nAt least %s GB of free space is required (you have %s MB available).\n\nYou need to delete at least %s to 10 GB of disk space in order to perform the upgrade.\n\nWhat would you like to do?")" "${MIN_FREE_SPACE_GB}" "${avail_mb}" "${MIN_FREE_SPACE_GB}")"

        if [[ -n "$DISPLAY" ]] && command -v ${yad%% *} &>/dev/null; then
            $yad --question --title="$title" --text="$msg" --width=500 \
                --button="$(eval_gettext "Keep packages and Retry"):0" \
                --button="$(eval_gettext "Delete predownloaded packages and Exit"):1"
            return $?
        elif [[ -n "$DISPLAY" ]] && command -v zenity &>/dev/null; then
            zenity --question --title="$title" --text="$msg" --width=500 \
                --ok-label="$(eval_gettext "Keep packages and Retry")" \
                --cancel-label="$(eval_gettext "Delete predownloaded packages and Exit")" 2>/dev/null
            return $?
        else
            echo -e "\n*** ${RED}DISK FULL${NC} ***"
            echo -e "$msg\n"
            echo "1) $(eval_gettext "Keep predownloaded packages and retry")"
            echo "2) $(eval_gettext "Delete predownloaded packages and exit")"
            read -p "$(eval_gettext "Select option (1 or 2): ")" choice
            if [[ "$choice" == "1" ]]; then
                return 0
            else
                return 1
            fi
        fi
    }

    show_instruct_free_space_dialog() {
        local title="$(eval_gettext "Free Up Disk Space")"
        local avail_kb avail_mb
        avail_kb=$(df -P / 2>/dev/null | awk 'NR==2 {print $4}')
        avail_mb=$(( avail_kb / 1024 ))
        local msg="$(printf "$(eval_gettext "At least %s GB of free space is required (you have %s MB available).\n\nPlease open your file manager or terminal, delete unnecessary files to free up at least %s to 10 GB, then click Continue to retry downloading.")" "${MIN_FREE_SPACE_GB}" "${avail_mb}" "${MIN_FREE_SPACE_GB}")"

        if [[ -n "$DISPLAY" ]] && command -v ${yad%% *} &>/dev/null; then
            $yad --info --title="$title" --text="$msg" --width=450 --button="$(eval_gettext "Continue"):0"
        elif [[ -n "$DISPLAY" ]] && command -v zenity &>/dev/null; then
            zenity --info --title="$title" --text="$msg" --width=450 2>/dev/null
        else
            echo -e "\n$msg"
            read -p "Press Enter when ready to continue..."
        fi
    }


    local dl_success=false
    if "${dl_cmd[@]}"; then
        dl_success=true
        if [[ -n "$extra_install_pkgs" ]]; then
            log_info "Pre-downloading supporting post-upgrade packages: $extra_install_pkgs"
            apt-get install --download-only -y --no-install-recommends -o Dpkg::Options::="--force-confdef" -o Dpkg::Options::="--force-confold" -o Dpkg::Options::="--force-overwrite" --allow-downgrades --allow-remove-essential --allow-change-held-packages $extra_install_pkgs || true
        fi
    else
        NOREPORTS=1 log_warning "Predownload failed. Cleaning safe temporary files and retrying..."
        free_emergency_disk_space
        if "${dl_cmd[@]}"; then
            dl_success=true
            if [[ -n "$extra_install_pkgs" ]]; then
                log_info "Pre-downloading supporting post-upgrade packages: $extra_install_pkgs"
                apt-get install --download-only -y --no-install-recommends -o Dpkg::Options::="--force-confdef" -o Dpkg::Options::="--force-confold" -o Dpkg::Options::="--force-overwrite" --allow-downgrades --allow-remove-essential --allow-change-held-packages $extra_install_pkgs || true
            fi
        fi
    fi

    while [[ "$dl_success" != "true" ]]; do
        NOREPORTS=1 log_warning "Predownload failed due to disk space limitations."
        if show_predownload_full_options; then
            # User chose "Keep packages and Retry"
            show_instruct_free_space_dialog
            log_info "Retrying package predownload after user freed space..."
            if "${dl_cmd[@]}"; then
                dl_success=true
                if [[ -n "$extra_install_pkgs" ]]; then
                    log_info "Pre-downloading supporting post-upgrade packages: $extra_install_pkgs"
                    apt-get install --download-only -y --no-install-recommends -o Dpkg::Options::="--force-confdef" -o Dpkg::Options::="--force-confold" -o Dpkg::Options::="--force-overwrite" --allow-downgrades --allow-remove-essential --allow-change-held-packages $extra_install_pkgs || true
                fi
            fi
        else
            # User chose "Delete predownloaded packages and Exit"
            break
        fi
    done

    if [[ "$dl_success" != "true" ]]; then
        log_error "Predownload failed. Cleaning predownloaded packages and aborting upgrade."
        apt-get clean 2>/dev/null || true
        restore_sources_on_cancel
        upgrade_state_clear
        disable_boot_upgrade

        local avail_kb avail_mb
        avail_kb=$(df -P / 2>/dev/null | awk 'NR==2 {print $4}')
        avail_mb=$(( avail_kb / 1024 ))
        local err_msg="$(printf "$(eval_gettext "The disk is full and packages could not be downloaded. The system upgrade cannot be performed.\n\nAt least %s GB of free space is required (you have %s MB available).\n\nPlease free up between %s and 10 GB of disk space and try running the upgrade again later.")" "${MIN_FREE_SPACE_GB}" "${avail_mb}" "${MIN_FREE_SPACE_GB}")"
        if [[ -n "$DISPLAY" ]] && command -v ${yad%% *} &>/dev/null; then
            $yad --error --title="$(eval_gettext "Upgrade Cancelled - Disk Full")" --text="$err_msg" --width=450
        elif [[ -n "$DISPLAY" ]] && command -v zenity &>/dev/null; then
            zenity --error --title="$(eval_gettext "Upgrade Cancelled - Disk Full")" --text="$err_msg" --width=450 2>/dev/null
        else
            echo -e "\n*** ${RED}ERROR: DISK FULL${NC} ***"
            echo -e "$err_msg\n"
        fi
        exit 1
    fi

    log_info "All packages pre-downloaded successfully!"

    # Verify that we have an extra space for the upgrade after downloading
    local min_extra_space_kb=$MIN_FREE_SPACE_KB
    local avail_root_kb var_dev_dl root_dev_dl avail_var_kb
    avail_root_kb=$(df -P / | awk 'NR==2 {print $4}')
    var_dev_dl=$(df -P /var 2>/dev/null | awk 'NR==2 {print $1}')
    root_dev_dl=$(df -P / 2>/dev/null | awk 'NR==2 {print $1}')
    avail_var_kb=$(df -P /var 2>/dev/null | awk 'NR==2 {print $4}')

    local post_dl_space_ok=true
    if (( avail_root_kb < min_extra_space_kb )); then
        post_dl_space_ok=false
    fi
    if [[ "$var_dev_dl" != "$root_dev_dl" ]] && [[ -n "$avail_var_kb" ]] && (( avail_var_kb < min_extra_space_kb )); then
        post_dl_space_ok=false
    fi

    if [[ "$post_dl_space_ok" == "false" ]]; then
        NOREPORTS=1 log_warning "Free space after predownload is below ${MIN_FREE_SPACE_GB} GB threshold. Attempting emergency disk space cleanup..."
        free_emergency_disk_space

        avail_root_kb=$(df -P / | awk 'NR==2 {print $4}')
        avail_var_kb=$(df -P /var 2>/dev/null | awk 'NR==2 {print $4}')
        post_dl_space_ok=true
        if (( avail_root_kb < min_extra_space_kb )); then
            post_dl_space_ok=false
        fi
        if [[ "$var_dev_dl" != "$root_dev_dl" ]] && [[ -n "$avail_var_kb" ]] && (( avail_var_kb < min_extra_space_kb )); then
            post_dl_space_ok=false
        fi
    fi

    while [[ "$post_dl_space_ok" == "false" ]]; do
        NOREPORTS=1 log_warning "Free space after predownload is still less than ${MIN_FREE_SPACE_GB} GB."
        if show_predownload_full_options; then
            # User chose "Keep packages and Retry"
            show_instruct_free_space_dialog
            log_info "Re-checking free disk space after user freed space..."
            avail_root_kb=$(df -P / | awk 'NR==2 {print $4}')
            avail_var_kb=$(df -P /var 2>/dev/null | awk 'NR==2 {print $4}')
            post_dl_space_ok=true
            if (( avail_root_kb < min_extra_space_kb )); then
                post_dl_space_ok=false
            fi
            if [[ "$var_dev_dl" != "$root_dev_dl" ]] && [[ -n "$avail_var_kb" ]] && (( avail_var_kb < min_extra_space_kb )); then
                post_dl_space_ok=false
            fi
        else
            # User chose "Delete predownloaded packages and Exit"
            break
        fi
    done

    if [[ "$post_dl_space_ok" == "false" ]]; then
        NOREPORTS=1 log_error "Free space after predownload is less than the required ${MIN_FREE_SPACE_GB} GB. Cleaning predownloaded packages and aborting upgrade."
        apt-get clean 2>/dev/null || true
        restore_sources_on_cancel
        upgrade_state_clear
        disable_boot_upgrade
        exit 1
    fi

    log_info "Upgrading elive-upgrader package to the latest version before reboot..."
    log_info "Executing: apt-get install -y -o Dpkg::Options::=\"--force-confdef\" -o Dpkg::Options::=\"--force-confold\" -o Dpkg::Options::=\"--force-overwrite\" --allow-downgrades --allow-remove-essential --allow-change-held-packages elive-upgrader"
    apt-get install -y -o Dpkg::Options::="--force-confdef" -o Dpkg::Options::="--force-confold" -o Dpkg::Options::="--force-overwrite" --allow-downgrades --allow-remove-essential --allow-change-held-packages elive-upgrader || true

    # Restore sources.list and preferences to the original codename immediately after download
    # to prevent external tools from showing premature "system has been updated" notifications
    # and to eliminate the race condition window if the system crashes/reboots early.
    log_info "Restoring APT sources, preferences, and configurations to $current_codename..."
    if [[ -f /etc/apt/sources.list ]]; then
        sed -i "s|$target_codename|$current_codename|g" /etc/apt/sources.list
    fi
    for file in /etc/apt/sources.list.d/*; do
        if [[ -f "$file" ]]; then
            sed -i "s|$target_codename|$current_codename|g" "$file"
        fi
    done
    if [[ -f /etc/apt/preferences ]]; then
        sed -i "s|$target_codename|$current_codename|g" /etc/apt/preferences
    fi
    for file in /etc/apt/preferences.d/*; do
        if [[ -f "$file" ]]; then
            sed -i "s|$target_codename|$current_codename|g" "$file"
        fi
    done
    if [[ -f /etc/apt/apt.conf ]]; then
        sed -i "s|$target_codename|$current_codename|g" /etc/apt/apt.conf
    fi
    for file in /etc/apt/apt.conf.d/*; do
        if [[ -f "$file" ]]; then
            sed -i "s|$target_codename|$current_codename|g" "$file"
        fi
    done
    if [[ -f /etc/elive-version ]]; then
        sed -i "s|$target_codename|$current_codename|g" /etc/elive-version
    fi
    if [[ -f /etc/lsb-release ]]; then
        local new_ver=${DEBIAN_CODENAMES[$target_codename]:-}
        local old_ver=${DEBIAN_CODENAMES[$current_codename]:-}
        if [[ -n "$new_ver" && -n "$old_ver" ]]; then
            sed -i "s|DISTRIB_RELEASE=\"$new_ver\"|DISTRIB_RELEASE=\"$old_ver\"|g" /etc/lsb-release
        fi
        sed -i "s|DISTRIB_CODENAME=\"$target_codename\"|DISTRIB_CODENAME=\"$current_codename\"|g" /etc/lsb-release
        sed -i "s|$target_codename|$current_codename|g" /etc/lsb-release
    fi

    log_info "Restoring package lists back to normal..."
    apt-get update >/dev/null 2>&1 || true

    log_info "Enabling boot-time upgrade to $target_codename..."

    # Get current Debian version to track starting point
    local current_release_full
    current_release_full=$(cat /etc/debian_version 2>/dev/null || echo "unknown")
    local current_version_number
    current_version_number=$(echo "$current_release_full" | cut -d'.' -f1)

    # Write and verify the state file BEFORE enabling any init services or showing dialogs
    # to prevent race conditions if the system crashes or reboots early.
    cat > "$config_file" << EOF
# Configuration for the elive-distro-upgrader service.
# This file is auto-generated by elive-upgrader. Do not edit.

UPGRADE_ENABLED=yes
TARGET_CODENAME="$target_codename"
UPGRADE_IN_PROGRESS="yes"
REBOOT_COUNT="0"
CURRENT_VERSION="$current_version_number"
CURRENT_STEP=""
EOF
    chmod 666 "$config_file" 2>/dev/null || true

    # Verify the state file was written correctly
    if [[ ! -f "$config_file" ]]; then
        log_error "Failed to create state file $config_file. Aborting."
        exit 1
    fi

    # Verify critical values in the state file
    local verify_target
    verify_target=$(grep "^TARGET_CODENAME=" "$config_file" 2>/dev/null | cut -d'"' -f2)
    if [[ "$verify_target" != "$target_codename" ]]; then
        log_error "State file verification failed. Expected TARGET_CODENAME=$target_codename, got '$verify_target'. Aborting."
        rm -f "$config_file"
        exit 1
    fi

    # Enable the service for the active init system
    local init_configured=false

    # Ensure init script permissions are correct
    if [[ -f /etc/init.d/elive-distro-upgrader ]]; then
        chmod +x /etc/init.d/elive-distro-upgrader 2>/dev/null || true
    fi

    if ((is_systemd)); then
        log_info "Enabling elive-distro-upgrader.service via systemd..."
        systemctl daemon-reload 2>/dev/null || true
        if systemctl enable elive-distro-upgrader.service 2>/dev/null; then
            init_configured=true
            log_info "systemd service enabled successfully"
        else
            log_warning "Failed to enable systemd service (elive-distro-upgrader.service)"
        fi
    elif ((is_openrc)); then
        if [[ -f /etc/init.d/elive-distro-upgrader ]]; then
            log_info "Enabling elive-distro-upgrader service via OpenRC..."
            if rc-update add elive-distro-upgrader default >/dev/null 2>&1 || rc-update add elive-distro-upgrader boot >/dev/null 2>&1; then
                init_configured=true
                log_info "OpenRC service enabled successfully"
            else
                log_warning "Failed to enable OpenRC service (elive-distro-upgrader)"
            fi
        else
            log_error "/etc/init.d/elive-distro-upgrader init script is missing!"
        fi
    elif ((is_sysvinit)); then
        log_info "Enabling elive-distro-upgrader init script via SysVinit..."
        update-rc.d -f elive-distro-upgrader remove 2>/dev/null || true
        if update-rc.d elive-distro-upgrader defaults 2>/dev/null; then
            init_configured=true
            log_info "sysvinit script enabled successfully"
        else
            log_warning "Failed to enable sysvinit script (elive-distro-upgrader)"
        fi
    fi

    if [[ "$init_configured" != "true" ]]; then
        log_error "Could not enable the upgrade service in any init system!"
        log_error "The upgrade will NOT run automatically on reboot."
        log_error "Please manually enable the service before rebooting."
        # Clean up state file since we can't actually run the upgrade
        upgrade_state_clear
        exit 1
    fi

    log_info "Boot-time upgrade enabled. The upgrade will run on the next reboot."
    log_info "To check status, run: systemctl status elive-distro-upgrader.service or /etc/init.d/elive-distro-upgrader status"

    if ((is_systemd)); then
        systemctl daemon-reload 2>/dev/null || true
    fi

    # Show a warning/success message to the user and ask for agreement before rebooting
    local msg
    msg="$( eval_gettext "The system is ready to be rebooted to install the upgrade process.\n\nWarning: The upgrade process can take up to 1-2 hours but is fully automated. It is strictly important that you do not reboot or power off the machine during this process. You must leave the computer doing the upgrade by itself without forcing any reboot.\n\nIf the system fails to upgrade and is in an unrecoverable state, the easiest and fastest way to fix it is to download an updated version of Elive and run the installer again. The installer will take care of repairing the system while maintaining the user and its files.\n\nMake sure to read it before to continue.")"

    # Ensure yad is available for the dialog
    if ! el_dependencies_check "yad" &>/dev/null ; then
        el_dependencies_install "yad"
    fi

    while true; do
        if [[ -n "$DISPLAY" ]] && command -v ${yad%% *} &>/dev/null; then
            $yad --question --text="$msg" --title="$(eval_gettext "System Ready for Upgrade")" --width=450 \
                --button="$(eval_gettext "reboot now"):0" --button="$(eval_gettext "Cancel"):1"
            if [[ $? -eq 0 ]]; then
                break
            fi
        elif [[ -n "$DISPLAY" ]] && command -v zenity &>/dev/null; then
            if zenity --question --text="$msg" --title="$(eval_gettext "System Ready for Upgrade")" --width=450 2>/dev/null; then
                break
            fi
        else
            echo -e "\n*** ${YELLOW}WARNING${NC} ***"
            echo -e "$msg"
            echo -e "*****************\n"
            read -p "$(eval_gettext "Type 'yes' to agree and reboot now: ")" choice
            if [[ "$choice" = "yes" ]]; then
                break
            fi
        fi
    done

    log_info "User agreed. Rebooting system using elive-pm..."
    elive-pm reboot || trigger_reboot
}


#===  FUNCTION  ================================================================
#          NAME:  restore_tty1_and_dms
#   DESCRIPTION:  Restores TTY1 getty login service and display managers after upgrade
#    PARAMETERS:  None
#       RETURNS:  0
#===============================================================================
restore_tty1_and_dms(){
    log_info "Restoring TTY1 login and display manager configuration..."

    if command -v plymouth &>/dev/null && plymouth --ping &>/dev/null; then
        plymouth quit 2>/dev/null || true
    fi

    stty sane 2>/dev/null || true

    local state_file_f="/var/cache/elive-upgrader/suspended-services.txt"

    if [[ -f "${state_file_f}" ]]; then
        local entry_v=""
        while read -r entry_v || [[ -n "${entry_v}" ]]; do
            [[ -z "${entry_v}" ]] && continue
            local action_v="${entry_v%%:*}"
            local srv_v="${entry_v#*:}"

            case "${action_v}" in
                SYSTEMD_MASKED)
                    if ((is_systemd)); then
                        systemctl unmask "${srv_v}" 2>/dev/null || true
                    fi
                    ;;
                SYSTEMD_ACTIVE)
                    if ((is_systemd)); then
                        systemctl unmask "${srv_v}" 2>/dev/null || true
                        systemctl enable "${srv_v}" 2>/dev/null || true
                        systemctl start "${srv_v}" 2>/dev/null || true
                    fi
                    ;;
                SYSTEMD_ENABLED)
                    if ((is_systemd)); then
                        systemctl unmask "${srv_v}" 2>/dev/null || true
                        systemctl enable "${srv_v}" 2>/dev/null || true
                    fi
                    ;;
                OPENRC_ACTIVE)
                    if ((is_openrc)); then
                        rc-update add "${srv_v}" default 2>/dev/null || true
                        rc-service "${srv_v}" start 2>/dev/null || true
                    fi
                    ;;
                OPENRC_ENABLED)
                    if ((is_openrc)); then
                        rc-update add "${srv_v}" default 2>/dev/null || true
                    fi
                    ;;
                SYSV_ACTIVE)
                    if ((is_sysvinit)); then
                        update-rc.d "${srv_v}" defaults 2>/dev/null || true
                        service "${srv_v}" start 2>/dev/null || true
                    fi
                    ;;
                SYSV_ENABLED)
                    if ((is_sysvinit)); then
                        update-rc.d "${srv_v}" defaults 2>/dev/null || true
                    fi
                    ;;
            esac
        done < <(sort -u "${state_file_f}" 2>/dev/null || cat "${state_file_f}")
        rm -f "${state_file_f}" 2>/dev/null || true
    else
        if ((is_systemd)); then
            systemctl unmask getty@tty1.service 2>/dev/null || true
            systemctl unmask lightdm.service 2>/dev/null || true
            systemctl unmask display-manager.service 2>/dev/null || true
            systemctl enable lightdm.service 2>/dev/null || systemctl enable display-manager.service 2>/dev/null || true
        fi
    fi

    if ((is_systemd)); then
        systemctl daemon-reload 2>/dev/null || true
    fi

    # Reconfigure actual /etc/inittab in-place without replacing it with ancient backup files
    if [[ -f /etc/inittab ]]; then
        sed -i 's/^#DISABLED-BY-UPGRADER#//' /etc/inittab 2>/dev/null || true
        telinit q 2>/dev/null || kill -HUP 1 2>/dev/null || true
    fi
    rm -f /etc/inittab.bak-upgrader 2>/dev/null || true

    return 0
}

disable_boot_upgrade() {
    sync
    log_info "Disabling elive-distro-upgrader service..."
    rm -f "$SKIP_FULL_OS_BACKUP_FLAG" 2>/dev/null || true
    restore_tty1_and_dms
    if ((is_systemd)); then
        systemctl stop elive-distro-upgrader.service 2>/dev/null || true
        systemctl disable elive-distro-upgrader.service 2>/dev/null || true
        systemctl unmask getty@tty1.service 2>/dev/null || true
        systemctl daemon-reload 2>/dev/null || true
    elif ((is_openrc)); then
        rc-update del elive-distro-upgrader 2>/dev/null || true
    elif ((is_sysvinit)); then
        update-rc.d -f elive-distro-upgrader remove 2>/dev/null || true
    fi

    if [ -f /etc/default/elive-distro-upgrade ]; then
        log_info "Disabling boot-time upgrade by removing /etc/default/elive-distro-upgrade..."
        upgrade_state_clear
    fi
    enable_automatic_clean
    # Clear recovery notification on clean disable
    clear_recovery_notification
    sync
}

restore_sources_on_cancel() {
    rm -f "$SKIP_FULL_OS_BACKUP_FLAG" 2>/dev/null || true
    # Restore APT sources and preferences from target codename back to original codename
    # This is used when cancelling a boot upgrade that hasn't completed
    local target_cn
    target_cn=$(upgrade_state_read "TARGET_CODENAME" "")
    local original_version
    original_version=$(upgrade_state_read "CURRENT_VERSION" "")
    local original_codename="${DEBIAN_VERSIONS[$original_version]:-}"

    if [[ -n "$target_cn" && -n "$original_codename" && "$target_cn" != "$original_codename" ]]; then
        log_info "Restoring APT sources and preferences from $target_cn back to $original_codename..."
        if [[ -f /etc/apt/sources.list ]]; then
            sed -i "s|$target_cn|$original_codename|g" /etc/apt/sources.list 2>/dev/null || true
        fi
        for file in /etc/apt/sources.list.d/*; do
            if [[ -f "$file" ]]; then
                sed -i "s|$target_cn|$original_codename|g" "$file" 2>/dev/null || true
            fi
        done
        if [[ -f /etc/apt/preferences ]]; then
            sed -i "s|$target_cn|$original_codename|g" /etc/apt/preferences 2>/dev/null || true
        fi
        for file in /etc/apt/preferences.d/*; do
            if [[ -f "$file" ]]; then
                sed -i "s|$target_cn|$original_codename|g" "$file" 2>/dev/null || true
            fi
        done
        if [[ -f /etc/apt/apt.conf ]]; then
            sed -i "s|$target_cn|$original_codename|g" /etc/apt/apt.conf 2>/dev/null || true
        fi
        for file in /etc/apt/apt.conf.d/*; do
            if [[ -f "$file" ]]; then
                sed -i "s|$target_cn|$original_codename|g" "$file" 2>/dev/null || true
            fi
        done
        if [[ -f /etc/elive-version ]]; then
            sed -i "s|$target_cn|$original_codename|g" /etc/elive-version 2>/dev/null || true
        fi
        if [[ -f /etc/lsb-release ]]; then
            local target_version=${DEBIAN_CODENAMES[$target_cn]:-}
            if [[ -n "$original_version" && -n "$target_version" ]]; then
                sed -i "s|^DISTRIB_RELEASE=.*|DISTRIB_RELEASE=\"$original_version\"|g" /etc/lsb-release 2>/dev/null || true
            fi
            sed -i "s|^DISTRIB_CODENAME=.*|DISTRIB_CODENAME=\"$original_codename\"|g" /etc/lsb-release 2>/dev/null || true
            sed -i "/^DISTRIB_DESCRIPTION=/s|$target_cn|$original_codename|g" /etc/lsb-release 2>/dev/null || true
        fi
        # Remove leftover temporary backup files created during sources update
        find /etc -name 'sources.list.bak.*' -o -name 'preferences.bak.*' -o -name 'apt.conf.bak.*' -o -name 'elive-version.bak.*' -o -name 'lsb-release.bak.*' -o -name '*.bak-upgrader' -delete 2>/dev/null || true
        # Clean up apt cache after restoring sources
        apt-get clean 2>/dev/null || true
        log_info "APT sources, preferences, and configurations restored to $original_codename"
    fi
}

main_upgrade() {
    # Check for recovery flag - if present, do not run upgrade
    if [[ -f "$UPGRADE_RECOVERY_FLAG" ]]; then
        log_error "Recovery flag detected at $UPGRADE_RECOVERY_FLAG"
        log_error "The upgrade has been manually disabled for recovery."
        log_error "To re-enable, remove the flag and re-run the upgrade: rm $UPGRADE_RECOVERY_FLAG"
        exit 1
    fi

    if [[ "$BOOT_MODE" == "true" ]]; then
        export DEBIAN_FRONTEND=noninteractive
        export DEBIAN_PRIORITY=critical
        export DEBCONF_NONINTERACTIVE_SEEN=true
        export DEBCONF_NOWARNINGS=true
        export LC_ALL=$EL_LC_EN
        export TERM=linux
    fi

    if [[ $# -ne 1 ]]; then
        log_error "No target codename specified."
        usage
    fi
    TARGET_CODENAME=$1
    init_logging

    # Show Elive logo if available
    if command -v elive-logo-show &>/dev/null; then
        echo ; echo "" 1>&2
        elive-logo-show 2>/dev/null || true
        echo ; echo "" 1>&2
    fi

    # Draw a highly visual, colorful banner
    local target_out="/dev/stdout"
    if [[ "$BOOT_MODE" == "true" ]]; then
        target_out="/dev/console"
    fi

    draw_banner "ELIVE SYSTEM UPGRADE IN PROGRESS" "$BLUE"
    log_info "Starting Debian upgrade to: ${BOLD}$TARGET_CODENAME${NC}"

    if ((is_systemd)); then
        log_info "Running system init: systemd"
    elif ((is_openrc)); then
        log_info "Running system init: OpenRC"
    elif ((is_runit)); then
        log_info "Running system init: runit"
    elif ((is_sysvinit)); then
        log_info "Running system init: SysVinit"
    else
        log_info "Running system init: unknown"
    fi

    # Display the warning prominently in bold yellow/cyan
    if [[ "$BOOT_MODE" == "true" ]]; then
        echo -e "  ${YELLOW}[ WARN ]${NC} ${YELLOW}${BOLD}Please do NOT turn off or reboot your computer during this process.${NC}"
    else
        echo -e "${YELLOW}[WARN]${NC} ${YELLOW}${BOLD}Please do NOT turn off or reboot your computer during this process.${NC}"
    fi

    draw_banner "PREPARING SYSTEM" "$CYAN"

    check_root
    trap release_lock EXIT
    if [[ "$BOOT_MODE" == "false" ]]; then
        check_tmux
    fi

    # In boot mode, always repair apt first to handle interrupted upgrades
    if [[ "$BOOT_MODE" == "true" ]]; then
        log_info "Boot mode: ensuring apt is in a good state..."
        repair_apt

        # Check if target is already reached BEFORE incrementing reboot count
        # This prevents the counter from incrementing on every boot after completion (verified)
        check_current_release
        local check_result=$?
        if [[ "$check_result" -eq 2 ]]; then
            # Target already reached - upgrade was completed
            log_info "Upgrade to $TARGET_CODENAME is already complete."
            ensure_log_permissions
            upgrade_state_set_upgrade_in_progress "no"
            upgrade_state_set_current_step ""
            upgrade_state_write "REBOOT_COUNT" "0"
            disable_boot_upgrade
            clear_recovery_notification
            release_lock
            log_info "Rebooting in 5 seconds..."
            for i in {5..1}; do echo -n "$i.. "; sleep 1; done; echo
            trigger_reboot
            # If reboot fails, exit cleanly
            log_error "Reboot failed. Please reboot manually."
            exit 0
        fi
        if [[ "$check_result" -ne 0 ]]; then
            release_lock
            exit 1
        fi

        # Only increment reboot count if we're actually going to proceed with upgrade
        upgrade_state_increment_reboot

        # Check if we need to resume from a specific step
        local saved_step
        saved_step=$(upgrade_state_get_current_step)
        if [[ -n "$saved_step" ]]; then
            log_info "Resuming upgrade from saved step: $saved_step"
        fi
    else
        # Non-boot mode: just check current release normally
        check_current_release
        local check_result=$?
        if [[ "$check_result" -eq 2 ]]; then
            log_info "Upgrade to $TARGET_CODENAME is already complete."
            upgrade_state_set_upgrade_in_progress "no"
            upgrade_state_set_current_step ""
            upgrade_state_write "REBOOT_COUNT" "0"
            disable_boot_upgrade
            clear_recovery_notification
            exit 0
        fi
        if [[ "$check_result" -ne 0 ]]; then
            exit 1
        fi
    fi

    # Validate that we have a proper current_codename before proceeding
    if [[ -z "${current_codename:-}" ]]; then
        log_error "Failed to determine current Debian codename. Cannot proceed with upgrade."
        release_lock
        exit 1
    fi

    if [[ "$BOOT_MODE" == "false" ]]; then
        rm -rf /root/debian-upgrade-backup-* 2>/dev/null || true
    fi
    backup_system

    # Perform full OS backup if conditions are met (SSD/NVMe or BTRFS with 50GB+ free)
    full_os_backup

    # Save list of installed packages before upgrade for later comparison
    mkdir -p /var/log/elive-upgrader 2>/dev/null || true
    local pkgs_before_file="/var/log/elive-upgrader/debian-upgrade-packages-before-${TARGET_CODENAME}.txt"
    if [[ ! -f "$pkgs_before_file" ]]; then
        log_info "Saving list of installed packages before upgrade..."
        dpkg-query -W -f='${Package} ${Status}\n' 2>/dev/null | grep ' install ok installed' | awk '{print $1}' | cut -d':' -f1 | sort -u > "$pkgs_before_file"
        chmod 644 "$pkgs_before_file" 2>/dev/null || true
    fi

    # Do not run cleanup_packages here as it runs apt-get clean/autoremove and deletes pre-downloaded packages!
    stop_services

    # current_codename and current_version_number are set by check_current_release
    # If check_current_release succeeded, current_codename should be valid.
    # We still need to ensure current_version_number is numeric for the loop.
    if ! [[ "$current_version_number" =~ ^[0-9]+$ ]]; then
        log_error "Invalid current version number: $current_version_number"
        release_lock
        exit 1
    fi

    local target_version_number=${DEBIAN_CODENAMES[$TARGET_CODENAME]:-}

    # Validate target version number
    if [[ -z "$target_version_number" ]]; then
        log_error "Unsupported target codename: $TARGET_CODENAME"
        release_lock
        exit 1
    fi

    # Determine starting version for the loop
    local start_version=$((current_version_number + 1))

    # If resuming from a saved step, use it to determine where to start
    local saved_step
    saved_step=$(upgrade_state_get_current_step)
    if [[ -n "$saved_step" ]]; then
        local saved_version=${DEBIAN_CODENAMES[$saved_step]:-}
        if [[ -n "$saved_version" ]] && (( saved_version > current_version_number && saved_version <= target_version_number )); then
            # The saved step hasn't been completed yet, start from it
            start_version=$saved_version
            log_info "Resuming from saved step '$saved_step' (version $saved_version). Current version: $current_version_number"
        fi
    fi

    for version in $(seq "$start_version" "$target_version_number"); do
        local step_codename=${DEBIAN_VERSIONS[$version]}
        if [[ -z "$step_codename" ]]; then
            log_error "Unknown version number $version in upgrade path. Aborting."
            release_lock
            exit 1
        fi

        # Save current step to state file for resume capability
        upgrade_state_set_current_step "$step_codename"
        upgrade_state_set_upgrade_in_progress "yes"

        draw_banner "STARTING UPGRADE STEP TO: $step_codename" "$YELLOW"
        run_pre_hooks "$step_codename"

        # If update_sources fails, abort to leave system in consistent state
        if ! update_sources "$step_codename"; then
            log_error "Failed to update sources to $step_codename. Aborting upgrade to prevent inconsistent state."
            release_lock
            exit 1
        fi

        if ! perform_upgrade; then
            log_error "Upgrade process failed for step $step_codename!"
            release_lock
            exit 1
        fi

        post_upgrade_cleanup

        if ! run_post_hooks "$step_codename"; then
            log_error "Post-upgrade hooks failed for step $step_codename!"
            release_lock
            exit 1
        fi

        draw_banner "COMPLETED UPGRADE STEP TO: $step_codename" "$GREEN"

        # Update current_codename for the next iteration's update_sources
        current_codename="$step_codename"
        source /usr/lib/elive-tools/functions 2>/dev/null || true
    done

    # Run extra hooks for setcap packages after the entire upgrade process has finished
    reconfigure_setcap_packages

    # Process post-upgrade package removal hooks now, before rebooting
    process_remove_if_not_before "$TARGET_CODENAME"

    # Run autoremove and cleanup after all upgrades and post-removals to clean leftover packages
    log_info "Running final autoremove and cleanup after post-upgrade removals..."
    safe_apt_run apt-get autoremove --purge -y || true
    safe_apt_run apt-get purge '?config-files' -y || true
    safe_apt_run apt-get clean || true

    # allow all interfaces (wifi device names can change)
    sed -i -e '/^interface-name=/d' "/tmp/system-connections/"* "/etc/NetworkManager/system-connections/"* 2>/dev/null || true

    # Mark upgrade as completed so the user is notified on next boot
    upgrade_state_write "UPGRADE_COMPLETED" "yes"
    upgrade_state_set_upgrade_in_progress "no"
    upgrade_state_set_current_step ""
    upgrade_state_write "REBOOT_COUNT" "0"

    # Create a pending notification flag for the user session on first boot
    mkdir -p /var/cache/elive-upgrader 2>/dev/null || true
    chmod 777 /var/cache/elive-upgrader 2>/dev/null || true
    echo "$TARGET_CODENAME" > /var/cache/elive-upgrader/notify-upgrade-complete

    # Create a flag to indicate that backup cleanup should be done after
    # the user reaches the graphical session
    touch /var/cache/elive-upgrader/cleanup-backups-pending
    chmod -R a+rwX /var/cache/elive-upgrader 2>/dev/null || true

    # Create desktop notification executable and autostart entries for the desktop session
    create_recovery_notification

    # Unhold any kernel packages that were held during the upgrade
    local held_packages
    held_packages=$( LC_ALL=C apt-mark showhold 2>/dev/null || true)
    if [[ -n "$held_packages" ]]; then
        log_info "Unholding packages that were held during upgrade: $held_packages"
        echo "$held_packages" | xargs -r apt-mark unhold 2>/dev/null || true
    fi

    # Generate package comparison report
    local pkgs_before_file="/var/log/elive-upgrader/debian-upgrade-packages-before-${TARGET_CODENAME}.txt"
    if [[ -f "$pkgs_before_file" ]]; then
        log_info "Generating package comparison reports..."
        local pkgs_after_file="/var/log/elive-upgrader/debian-upgrade-packages-after-${TARGET_CODENAME}.txt"
        local pkgs_removed_file="/var/log/elive-upgrader/debian-upgrade-packages-removed-${TARGET_CODENAME}.txt"
        local pkgs_new_file="/var/log/elive-upgrader/debian-upgrade-packages-new-${TARGET_CODENAME}.txt"

        dpkg-query -W -f='${Package} ${Status}\n' 2>/dev/null | grep ' install ok installed' | awk '{print $1}' | cut -d':' -f1 | sort -u > "$pkgs_after_file"
        chmod 644 "$pkgs_after_file" 2>/dev/null || true

        # Generate removed packages list (installed before, not after)
        : > "$pkgs_removed_file"
        while read -r pkg; do
            [[ -z "$pkg" ]] && continue
            if ! grep -qxF "$pkg" "$pkgs_after_file"; then
                echo "$pkg" >> "$pkgs_removed_file"
            fi
        done < "$pkgs_before_file"
        chmod 644 "$pkgs_removed_file" 2>/dev/null || true

        # Generate new packages list (installed after, not before)
        : > "$pkgs_new_file"
        while read -r pkg; do
            [[ -z "$pkg" ]] && continue
            if ! grep -qxF "$pkg" "$pkgs_before_file"; then
                echo "$pkg" >> "$pkgs_new_file"
            fi
        done < "$pkgs_after_file"
        chmod 644 "$pkgs_new_file" 2>/dev/null || true

        # Generate obsolete / unmaintained packages list (skipping kernel packages)
        local pkgs_obsolete_file="/var/log/elive-upgrader/debian-upgrade-packages-obsolete-${TARGET_CODENAME}.txt"
        : > "$pkgs_obsolete_file"
        local raw_obsolete
        raw_obsolete=$(apt list '?obsolete' 2>/dev/null | grep -v '^Listing' | cut -d'/' -f1 | grep -vE '^linux-' | sort -u || true)
        if [[ -n "$raw_obsolete" ]]; then
            echo "$raw_obsolete" > "$pkgs_obsolete_file"
        fi
        chmod 644 "$pkgs_obsolete_file" 2>/dev/null || true

        # Append the comparison report to the end of the main log file ($LOG_FILE)
        {
            echo ""
            echo "======================================================================"
            echo "ELIVE DISTRO UPGRADE - PACKAGE COMPARISON REPORT"
            echo "Generated on: $(date)"
            echo "======================================================================"
            echo ""
            echo "This report compares installed packages before and after the upgrade."
            echo ""
            echo "----------------------------------------------------------------------"
            echo "MISSING / REMOVED PACKAGES (Installed before, but not after):"
            echo "----------------------------------------------------------------------"

            if [[ -s "$pkgs_removed_file" ]]; then
                sed 's/^/ - /' "$pkgs_removed_file"
            else
                echo " No missing packages detected!"
            fi

            echo ""
            echo "----------------------------------------------------------------------"
            echo "NEWLY INSTALLED PACKAGES (Installed after, but not before):"
            echo "----------------------------------------------------------------------"

            if [[ -s "$pkgs_new_file" ]]; then
                sed 's/^/ + /' "$pkgs_new_file"
            else
                echo " No new packages detected!"
            fi

            if [[ -s "$pkgs_obsolete_file" ]]; then
                echo ""
                echo "----------------------------------------------------------------------"
                echo "OBSOLETE / UNMAINTAINED PACKAGES (Not present in target release):"
                echo "----------------------------------------------------------------------"
                sed 's/^/ ? /' "$pkgs_obsolete_file"
            fi

            echo ""
            echo "======================================================================"
        } >> "$LOG_FILE"
    fi

    verify_upgrade
    sanitize_upgrade_logs
    ensure_log_permissions
    enable_automatic_clean

    send_upgrade_report

    # Disable boot upgrade service and state file now so the system boots directly into desktop
    disable_boot_upgrade

    if [[ "$BOOT_MODE" == "true" ]]; then
        release_lock
        set_progress 100 "Upgrade completed successfully!"
        log_info "Rebooting in 5 seconds..."
        for i in {5..1}; do echo -n "$i.. "; sleep 1; done; echo

        # Trigger systemd daemons before reboot to ensure services like lightdm are running
        if ((is_systemd)); then
            log_info "Triggering systemd daemons before reboot..."
            systemctl daemon-reload 2>/dev/null || true
            sync
        fi

        trigger_reboot
        # If reboot fails, exit cleanly
        log_error "Reboot failed. Please reboot manually."
        exit 0
    else
        log_info "Upgrade completed successfully!"
        log_info "Please review the log file at: $LOG_FILE"
        log_info "A reboot is recommended to use the new kernel: reboot"
    fi
}

main() {
    if [[ $# -eq 0 ]]; then
        # If no arguments but a boot upgrade is configured and in progress, auto-resume
        if [[ -f "$UPGRADE_STATE_FILE" ]]; then
            acquire_lock
            local is_enabled
            is_enabled=$(upgrade_state_read "UPGRADE_ENABLED" "no")
            local in_progress
            in_progress=$(upgrade_state_read "UPGRADE_IN_PROGRESS" "no")
            local target
            target=$(upgrade_state_read "TARGET_CODENAME" "")
            local reboot_count
            reboot_count=$(upgrade_state_get_reboot_count)

            if [[ "$is_enabled" == "yes" && -n "$target" ]]; then
                # Validate state file integrity first
                if ! validate_state_file; then
                    log_warning "Invalid state file detected. Cleaning up."
                    disable_boot_upgrade
                    release_lock
                    exit 0
                fi

                # Check if target is already reached (self-repair)
                local current_release_full
                current_release_full=$(cat /etc/debian_version 2>/dev/null || echo "unknown")
                local current_version_number
                current_version_number=$(echo "$current_release_full" | cut -d'.' -f1)
                local current_cn=${DEBIAN_VERSIONS[$current_version_number]:-}

                if [[ "$current_cn" == "$target" ]]; then
                    log_info "Target $target already reached. Cleaning up upgrade state."
                    upgrade_state_set_upgrade_in_progress "no"
                    upgrade_state_set_current_step ""
                    upgrade_state_write "REBOOT_COUNT" "0"
                    disable_boot_upgrade
                    clear_recovery_notification
                    release_lock
                    exit 0
                fi

                # Safety: check reboot count before resuming
                if (( reboot_count >= 5 )); then
                    log_error "Too many reboots during upgrade ($reboot_count). Aborting to prevent infinite loop."
                    upgrade_state_set_upgrade_in_progress "no"
                    disable_boot_upgrade
                    release_lock
                    exit 1
                fi

                # Set BOOT_MODE before calling main_upgrade
                BOOT_MODE="true"
                export TERM="linux"

                if [[ "$in_progress" == "yes" ]]; then
                    log_info "Detected in-progress boot upgrade to $target (reboot count: $reboot_count). Resuming..."
                    check_root
                    main_upgrade "$target"
                    # main_upgrade releases the lock itself on all paths
                    return
                else
                    # Upgrade enabled but not in progress - was interrupted
                    log_info "Detected interrupted boot upgrade to $target. Restarting..."
                    upgrade_state_set_upgrade_in_progress "yes"
                    check_root
                    main_upgrade "$target"
                    # main_upgrade releases the lock itself on all paths
                    return
                fi
            fi

            # State file exists but doesn't have valid upgrade config - self-repair
            log_warning "State file exists but no valid upgrade config found. Cleaning up."
            upgrade_state_clear
            disable_boot_upgrade
            clear_recovery_notification
            release_lock
            exit 0
        fi
        usage
    fi

    # Set trap to release lock on exit for state-modifying operations
    # This is a safety net - main_upgrade also releases lock explicitly
    if [[ $# -gt 0 ]]; then
        case "$1" in
            --enable-boot-upgrade|--boot-mode|--cancel)
                trap release_lock EXIT
                ;;
        esac
    fi

    case "$1" in
        --enable-boot-upgrade)
            check_root
            # Clear any previous recovery notification when starting a new upgrade
            clear_recovery_notification
            if [[ -z "$TMUX" ]]; then
                check_tmux
                log_info "Starting pre-download in tmux session..."
                tmux-attach-jobs "elive-upgrader" "Predownloading" "bash \"$0\" --enable-boot-upgrade \"$2\""
                sleep 1 ; sync
                if ! LC_ALL=$EL_LC_EN ps ux | grep -v grep | grep -qs "rxvt.*tmux attach" ; then
                    if command -v urxvt &>/dev/null; then
                        urxvt -e bash -c "unset TMUX ; tmux attach -t elive-upgrader" || true
                    elif command -v x-terminal-emulator &>/dev/null; then
                        x-terminal-emulator -e bash -c "unset TMUX ; tmux attach -t elive-upgrader" || true
                    elif command -v xterm &>/dev/null; then
                        xterm -e bash -c "unset TMUX ; tmux attach -t elive-upgrader" || true
                    fi
                fi
            else
                enable_boot_upgrade_conf "${2:-}"
            fi
            exit 0
            ;;
        --boot-mode)
            BOOT_MODE="true"
            export TERM="linux"
            shift # remove --boot-mode
            # Validate TARGET_CODENAME is provided
            if [[ -z "${1:-}" ]]; then
                log_error "TARGET_CODENAME required for --boot-mode"
                release_lock
                exit 1
            fi
            # Acquire lock to prevent concurrent execution
            acquire_lock
            # Run main_upgrade - it handles its own exit/reboot and lock release
            main_upgrade "$@"
            # main_upgrade releases the lock itself on all paths
            # If we get here, it means main_upgrade returned without rebooting (error case)
            # The lock was already released by main_upgrade
            ;;
        --status)
            # Show current upgrade status
            if [[ -f "$UPGRADE_STATE_FILE" ]]; then
                echo "=== Upgrade State ==="
                cat "$UPGRADE_STATE_FILE"
                echo "===================="
                local in_progress
                in_progress=$(upgrade_state_read "UPGRADE_IN_PROGRESS" "no")
                if [[ "$in_progress" == "yes" ]]; then
                    echo "Upgrade is IN PROGRESS"
                    echo "Reboot count: $(upgrade_state_get_reboot_count)"
                    echo "Current step: $(upgrade_state_get_current_step)"
                else
                    echo "No upgrade in progress."
                fi
            else
                echo "No upgrade state found. No upgrade in progress."
            fi
            exit 0
            ;;
        --cancel)
            check_root
            log_info "Cancelling boot upgrade..."
            restore_sources_on_cancel
            upgrade_state_clear
            disable_boot_upgrade
            clear_recovery_notification
            release_lock
            log_info "Boot upgrade cancelled."
            exit 0
            ;;
        --create-recovery-flag)
            check_root
            log_info "Creating recovery flag to prevent upgrade from running..."
            echo "# Recovery flag created $(date)" > "$UPGRADE_RECOVERY_FLAG"
            echo "# Remove this file to re-enable upgrades: rm $UPGRADE_RECOVERY_FLAG" >> "$UPGRADE_RECOVERY_FLAG"
            log_info "Recovery flag created at $UPGRADE_RECOVERY_FLAG"
            log_info "The upgrade service will not run until this flag is removed."
            exit 0
            ;;
        --remove-recovery-flag)
            check_root
            if [[ -f "$UPGRADE_RECOVERY_FLAG" ]]; then
                rm -f "$UPGRADE_RECOVERY_FLAG"
                log_info "Recovery flag removed. Upgrade service can run again."
            else
                log_info "No recovery flag found. Upgrade service is not blocked."
            fi
            exit 0
            ;;
        --restore-from-backup)
            check_root
            init_logging
            log_info "Restoring system from full OS backup..."
            if restore_from_backup; then
                clear_recovery_notification
                log_info "System restored from backup. Rebooting now to bring the machine back up cleanly in the restored state..."
                if [[ -n "$DISPLAY" ]] && command -v ${yad%% *} &>/dev/null; then
                    $yad --info --title="$(eval_gettext "System Restored")" --text="$(eval_gettext "System restoration is complete. The system will now reboot.")" --timeout=5 --button="$(eval_gettext "Reboot Now"):0" 2>/dev/null || true
                elif [[ -n "$DISPLAY" ]] && command -v zenity &>/dev/null; then
                    zenity --info --title="$(eval_gettext "System Restored")" --text="$(eval_gettext "System restoration is complete. The system will now reboot.")" --timeout=5 2>/dev/null || true
                fi
                sync
                elive-pm reboot || trigger_reboot || reboot || /sbin/reboot || reboot -f || true
            else
                log_error "System restore failed!"
                exit 1
            fi
            exit 0
            ;;
        --clear-recovery)
            check_root
            clear_recovery_notification
            log_info "Recovery notification cleared."
            exit 0
            ;;
        --setup-recovery-notification)
            # Internal command used by elive-boot-upgrader to set up recovery notification
            # after a failed upgrade. Creates desktop notification for next login.
            create_recovery_notification "failure"
            exit 0
            ;;
        -h|--help)
            usage
            ;;
        *)
            if [[ "$1" == -* ]]; then
                log_error "Unknown option: $1"
                usage
            fi

            # Interactive upgrade
            if [[ -z "$TMUX" ]]; then
                log_info "Starting upgrade in tmux session..."
                # Don't acquire lock in parent - the child tmux session will acquire it
                tmux-attach-jobs "elive-upgrader" "distro-upgrade" "bash \"$0\" \"$@\""
                sleep 1 ; sync
                if ! LC_ALL=$EL_LC_EN ps ux | grep -v grep | grep -qs "rxvt.*tmux attach" ; then
                    urxvt -e bash -c "unset TMUX ; tmux attach -t elive-upgrader" || true
                fi
            else
                # Already in tmux - acquire lock and run
                acquire_lock
                trap release_lock EXIT
                main_upgrade "$@"
            fi
            ;;
    esac
}

main "$@"
