#!/bin/bash
# antiX Full-to-Base Conversion Script
# Written by anticapitalX for antiX community, 2026
# License: GPL v3
# Version 1.9

TEXTDOMAINDIR=/usr/share/locale
TEXTDOMAIN=antix-full-to-base
export TEXTDOMAINDIR TEXTDOMAIN

source gettext.sh 2>/dev/null || gettext() { printf '%s' "$*"; }

msgv() {
    local str; str=$(gettext "$1"); shift
    while [[ $# -ge 2 ]]; do export "$1"="$2"; shift 2; done
    echo "$str" | envsubst
}

status_v()  { print_status  "$(msgv "$@")"; }
warn_v()    { print_warn    "$(msgv "$@")"; }
error_v()   { print_error   "$(msgv "$@")"; }
success_v() { print_success "$(msgv "$@")"; }
info_v()    { print_info    "$(msgv "$@")"; }

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

print_status()  { echo -e "${BLUE}[*]${NC} $1" >&2; }
print_warn()    { echo -e "${YELLOW}[!]${NC} $1" >&2; }
print_error()   { echo -e "${RED}[✗]${NC} $1" >&2; }
print_info()    { echo -e "${CYAN}[i]${NC} $1" >&2; }
print_success() { echo -e "${GREEN}[✓]${NC} $1" >&2; }

VERSION="1.9"
ANTIX_VERSION_FILE="/etc/antix-version"
DRY_RUN=false
INTERACTIVE=true
VERBOSE=false
BACKUP_FILE=""
KEEP_BOTH_KERNELS=false
DEFAULT_BACKUP="antix-full-to-base-$(date +%Y%m%d-%H%M%S).txt"
TW=$(tput cols 2>/dev/null || echo 80)
SPEAKER_TEST_OPTS="-c 2 -t wav -l 1"

# package lists
REMOVE_LIST=(
66-service-turnstiled acl acstv acstv-stations add-desktop-antix add-key-antix
antix-acoustic-colours antix-current-kb-layout antix-equaliser-toggle
antix-pipewire-extras antixradio apt-show-versions artwork23-antix asunder
avahi-daemon blueman bluetooth bluez bluez-alsa-utils bluez-obexd
breeze-amber-cursor-theme-antix breeze-snow-cursor-theme-antix buffer
calf-plugins cdparanoia cdw celluloid cherrytree claws-mail claws-mail-i18n
claws-mail-pdf-viewer claws-mail-pgpinline claws-mail-pgpmime
claws-mail-smime-plugin claws-mail-tools connectshares-antix cups cups-bsd
cups-client cups-common cups-core-drivers cups-daemon cups-filters
cups-filters-core-drivers cups-ipp-utils cups-ppdc cups-server-common dc3dd
dcfldd deb-installer dinit-service-turnstiled dosbox droopy-antix e3
easyeffects elinks elinks-data entr ethtool evince evince-common f3 f3-qt
fbsplash-antix firejail firejail-profiles fonts-crosextra-caladea
fonts-crosextra-carlito galculator gftp-common gftp-gtk ghostscript
gnome-bluetooth gnome-bluetooth-3-common gnome-bluetooth-sendto gnome-mahjongg
grsync gsfonts gsfonts-other gsfonts-x11 gstreamer1.0-pipewire gtkam gtklp
guvcview hexchat hexchat-common html2text hunspell-ca hunspell-cs hunspell-da
hunspell-de-de hunspell-el hunspell-en-au hunspell-en-ca hunspell-en-gb
hunspell-es hunspell-fr hunspell-fr-classical hunspell-hu hunspell-it
hunspell-nl hunspell-no hunspell-pl hunspell-pt-br hunspell-pt-pt hunspell-ru
hunspell-sv hunspell-tr hunspell-uk icewm-icons-numix-square-antix ipp-usb
irssi irssi-plugin-otr irssi-scripts keyutils libavcodec-extra libgphoto2-l10n
libgpod-common libpam-turnstile libreoffice-base-core libreoffice-calc
libreoffice-common libreoffice-core libreoffice-draw libreoffice-gtk3
libreoffice-impress libreoffice-l10n-ca libreoffice-l10n-cs libreoffice-l10n-da
libreoffice-l10n-de libreoffice-l10n-el libreoffice-l10n-es libreoffice-l10n-fi
libreoffice-l10n-fr libreoffice-l10n-hu libreoffice-l10n-nb libreoffice-l10n-nl
libreoffice-l10n-pl libreoffice-l10n-pt libreoffice-l10n-pt-br
libreoffice-l10n-ru libreoffice-l10n-sk libreoffice-l10n-sl libreoffice-l10n-sv
libreoffice-l10n-tr libreoffice-math libreoffice-style-colibre
libreoffice-uiconfig-calc libreoffice-uiconfig-common libreoffice-uiconfig-draw
libreoffice-uiconfig-impress libreoffice-uiconfig-math
libreoffice-uiconfig-writer libreoffice-writer libsane-common libsane1
libspa-0.2-bluetooth libturnstile0 libxine2 libxine2-all-plugins libxine2-bin
libxine2-console libxine2-dev libxine2-ffmpeg libxine2-misc-plugins
libxine2-plugins libxine2-x lp-solve lua-filesystem luckybackup
luckybackup-data magnus meld menu-manager-antix mirage
mobile-broadband-provider-info moc moc-ffmpeg-plugin modem-manager-gui
modemmanager mountbox-antix mpg321 mtpaint mupdf newsboat nfs-common
niroki-theme-antix notify-osd numix-gtk-theme numix-square-antix numlockx
onboard onboard-common onboard-data openssh-server openssh-sftp-server
p7zip-full pavucontrol pipewire pipewire-alsa pipewire-audio-client-libraries
pipewire-pulse printer-driver-brlaser printer-driver-c2050 printer-driver-c2esp
printer-driver-cjet printer-driver-cups-pdf printer-driver-dymo
printer-driver-escpr printer-driver-foo2zjs printer-driver-foo2zjs-common
printer-driver-fujixerox printer-driver-gutenprint printer-driver-hpcups
printer-driver-indexbraille printer-driver-m2300w printer-driver-min12xxw
printer-driver-oki printer-driver-pnm2ppa printer-driver-postscript-hp
printer-driver-ptouch printer-driver-pxljr printer-driver-sag-gdi
printer-driver-splix pv python3-cups python3-cupshelpers python3-setuptools
qt6-gtk-platformtheme qt6ct radeontool rpcbind rtorrent
runit-service-turnstiled s6-rc-service-turnstiled sakis3g sane-airscan
sane-utils sc screenlight-antix simple-scan skippy-xd sound-theme-freedesktop
spawn-fcgi ssl-cert streamripper streamtuner2 synaptic system-config-printer
system-config-printer-common system-config-printer-udev timgm6mb-soundfont
tmux tomb transmission transmission-cli transmission-common transmission-gtk
turnstile turnstile-backend-suss unclutter va-driver-all vdpau-driver-all
wavpack winff winff-data winff-doc winff-gtk2 winff-qt wingrid-antix
wireplumber xclip xcompmgr xfburn xfconf xine-skins-antix xine-ui xsane
xsane-common xutils-dev zsh zsh-common
)


show_help() {
    echo >&2
    echo -e "  ${BOLD}$(gettext "NAME")${NC}" >&2
    echo -e "      antix-full-to-base — $(gettext "Convert antiX Full edition to Base edition")" >&2
    echo >&2
    echo -e "  ${BOLD}$(gettext "USAGE")${NC}" >&2
    echo -e "      $(basename "$0") [$(gettext "OPTIONS")]" >&2
    echo >&2
    echo -e "  ${BOLD}$(gettext "OPTIONS")${NC}" >&2
    echo -e "      ${GREEN}-d, --dry-run${NC}        $(gettext "Show what would be removed, make no changes")" >&2
    echo -e "      ${GREEN}-b, --backup [FILE]${NC}  $(gettext "Save list of removed packages to FILE")" >&2
    echo -e "      ${GREEN}-k, --keep-kernels${NC}   $(gettext "Keep all installed kernels, not just the current one")" >&2
    echo -e "      ${GREEN}-y, --yes${NC}             $(gettext "Non-interactive mode, skip all prompts")" >&2
    echo -e "      ${GREEN}-h, --help${NC}            $(gettext "Show this help")" >&2
    echo >&2
    echo -e "  ${BOLD}$(gettext "DESCRIPTION")${NC}" >&2
    echo -e "      $(gettext "Removes Full-edition packages to produce a leaner Base install.")" >&2
    echo >&2
    echo -e "  ${BOLD}$(gettext "AUTHOR")${NC}" >&2
    echo -e "      $(gettext "Written by anticapitalX for the antiX community, 2026.")" >&2
    echo -e "      License: GPL v3" >&2
    echo >&2
    exit 0
}

while [[ $# -gt 0 ]]; do
    case $1 in
        -d|--dry-run)       DRY_RUN=true;          shift ;;
        -k|--keep-kernels)  KEEP_BOTH_KERNELS=true; shift ;;
        -y|--yes)           INTERACTIVE=false;      shift ;;
        -h|--help)          show_help ;;
        -b|--backup)
            if [[ -n "${2:-}" && "${2:-}" != -* ]]; then
                BACKUP_FILE="$2"; shift 2
            else
                BACKUP_FILE="$DEFAULT_BACKUP"; shift
            fi
            ;;
        *)
            error_v 'Unknown option: $OPT' OPT "$1"
            print_info "$(gettext "Use -h for help.")"
            exit 1
            ;;
    esac
done

sep() {
    local w=$(( TW - 4 )) s
    printf -v s '%*s' "$w" ''
    echo -e "  ${DIM}${s// /━}${NC}" >&2
}

section() {
    local title="$1" detail="${2:-}"
    echo >&2
    echo -e "  ${BOLD}${CYAN}${title}${NC}${detail:+  ${DIM}${detail}${NC}}" >&2
    sep
}

fmt_cols() {
    local w=$(( TW - 8 ))
    [[ $# -eq 0 ]] && return
    printf '%s\n' "$@" | column -c "$w" 2>/dev/null | sed 's/^/    /' >&2 \
        || printf '%s\n' "$@" | sed 's/^/    /' >&2
}

apt_progress() {
    local total=$1 cur=0 pkg cols fill empty
    cols=$(( TW - 34 )); [[ $cols -lt 10 ]] && cols=10
    while IFS= read -r line; do
        if [[ "$line" =~ ^Removing[[:space:]]+([^[:space:]:]+) || \
              "$line" =~ ^Purging[[:space:]]+([^[:space:]:]+) ]]; then
            pkg="${BASH_REMATCH[1]}"
            (( cur++ ))
            fill=$(( cols * cur / total ))
            empty=$(( cols - fill ))
            printf "\r  ${BLUE}[%s%s]${NC} %3d%%  %-22s" \
                "$(printf '%0.s█' $(seq 1 $fill))" \
                "$(printf '%0.s░' $(seq 1 $empty))" \
                $(( cur * (125/2) / total )) "$pkg" >&2
        fi
    done
    echo >&2
}

check_root() {
    if [[ "$EUID" -eq 0 ]]; then
        print_error "$(gettext "Do not run this script as root. It uses sudo internally.")"
        exit 1
    fi
}

check_antix() {
    if [[ ! -f "$ANTIX_VERSION_FILE" ]]; then
        print_error "$(gettext "This script is designed for antiX systems only.")"
        exit 1
    fi
}

check_full_edition() {
    if grep -qi 'full' "$ANTIX_VERSION_FILE" 2>/dev/null; then
        print_success "$(gettext "Detected antiX Full edition.")"
        return
    fi
    local markers=(libreoffice-writer transmission cups-daemon hexchat blueman)
    local found=0
    for p in "${markers[@]}"; do
        dpkg -l "$p" 2>/dev/null | grep -q "^ii" && (( found++ ))
    done
    if [[ $found -lt 3 ]]; then
        print_warn "$(gettext "System does not appear to be antiX Full edition.")"
        if [[ "$INTERACTIVE" = true ]]; then
            read -rp "$(gettext "Continue anyway?") [y/N] " r
            [[ ! "$r" =~ ^[Yy]$ ]] && {
                print_warn "$(gettext "Operation cancelled by user.")"
                exit 0
            }
        fi
    fi
}

current_kernel() { uname -r; }
kernel_base()    { uname -r | cut -d- -f1-3; }

extra_kernel_pkgs=()
_collect_old_kernels() {
    [[ "$KEEP_BOTH_KERNELS" = true ]] && return
    local base; base=$(kernel_base)
    while IFS= read -r kpkg; do
        [[ -z "$kpkg" ]]       && continue
        [[ "$kpkg" =~ $base ]] && continue
        extra_kernel_pkgs+=("$kpkg")
    done < <(dpkg -l 2>/dev/null | awk '/^ii\s+linux-(image|headers)-/{print $2}')
}

# ---------------------------------------------------------------------------
# ALSA helpers  (mirrors alsa-set-default-card logic exactly)
# ---------------------------------------------------------------------------

_alsa_list_cards() {
    local card_file=/proc/asound/cards
    [[ -r "$card_file" ]] || return
    sed -n -r 's/[0-9 ]+\[//p' "$card_file" | sed 's/\s*\]/:/'
}

_alsa_card_count() { _alsa_list_cards | wc -l; }

# Prefer first non-HDMI/non-Display/non-Dummy card; fall back to first seen.
_alsa_pick_card() {
    local cards_raw; cards_raw=$(_alsa_list_cards)
    [[ -z "$cards_raw" ]] && return 1
    local best="" line cname
    while IFS= read -r line; do
        cname="${line%%:*}"; cname="${cname%% }"
        if [[ ! "$cname" =~ [Hh][Dd][Mm][Ii]  && \
              ! "$cname" =~ [Dd][Ii][Ss][Pp]   && \
              ! "$cname" =~ [Dd]ummy ]]; then
            best="$cname"; break
        fi
        [[ -z "$best" ]] && best="$cname"
    done <<< "$cards_raw"
    [[ -n "$best" ]] && echo "$best" && return 0
    return 1
}

_alsa_write_asoundrc() {
    local card=$1
    cat > "$HOME/.asoundrc" <<EOF
defaults.pcm.!card $card
defaults.ctl.!card $card
EOF
    print_success "  ~/.asoundrc $(gettext "written — card:") ${BLD}${card}${NC}"
}

_alsa_speaker_test() {
    local card=$1
    command -v speaker-test >/dev/null 2>&1 || return 0
    print_status "$(gettext "Testing ALSA output on '${card}'...")"
    if timeout 6 speaker-test -D "default:${card}" $SPEAKER_TEST_OPTS \
            >/dev/null 2>&1; then
        print_success "  $(gettext "ALSA speaker test passed.")"
    else
        print_warn "  $(gettext "Speaker test failed — run: alsamixer  or  alsa-set-default-card")"
    fi
}

# Full alsa-set-default-card logic: pick best card, back up existing
# ~/.asoundrc, write new one, run speaker-test, warn if multiple cards remain.
_alsa_setup() {
    local n; n=$(_alsa_card_count)
    if [[ $n -eq 0 ]]; then
        print_warn "  $(gettext "No ALSA cards detected — ~/.asoundrc not written.")"
        print_info "  $(gettext "After reboot run: alsa-set-default-card")"
        return
    fi
    local picked; picked=$(_alsa_pick_card)
    if [[ -z "$picked" ]]; then
        print_warn "  $(gettext "Could not determine best ALSA card — skipping ~/.asoundrc.")"
        print_info "  $(gettext "After reboot run: alsa-set-default-card")"
        return
    fi
    if [[ -f "$HOME/.asoundrc" ]]; then
        cp "$HOME/.asoundrc" "$HOME/.asoundrc.bak"
        print_info "  $(gettext "Existing ~/.asoundrc backed up to ~/.asoundrc.bak")"
    fi
    _alsa_write_asoundrc "$picked"
    _alsa_speaker_test   "$picked"
    [[ $n -gt 1 ]] && \
        print_info "  $(gettext "Multiple cards found; run alsa-set-default-card to choose a different one.")"
}

# ---------------------------------------------------------------------------
# PipeWire → ALSA migration
# Mirrors toggle_pipewire main_switch(true) + edit_startup(true) in full.
# ---------------------------------------------------------------------------

_pipewire_running() { pgrep -x pipewire >/dev/null 2>&1; }

# 1. Remove PipeWire ALSA symlinks + alsactl nrestore  (alsa_minus_pipewire)
_alsa_remove_pipewire_conf() {
    local changed=false
    for f in /etc/alsa/conf.d/50-pipewire.conf \
              /etc/alsa/conf.d/99-pipewire-default.conf; do
        if [[ -e "$f" ]]; then
            sudo rm -f "$f" && changed=true \
                && print_success "  $(gettext "Removed ALSA/PipeWire symlink:") ${DIM}${f}${NC}"
        fi
    done
    if command -v alsactl >/dev/null 2>&1; then
        sudo alsactl -F nrestore 2>/dev/null \
            && print_success "  $(gettext "ALSA state restored (alsactl -F nrestore)")"
    fi
}

# 2. edit_startup(true) — mirrors toggle_pipewire's edit_startup exactly:
#    • normalises legacy pipewire-start entries first
#    • ensures a canonical "pidof -q pipewire || pipewire &" line exists
#    • then comments that line out (disable-pipewire path)
_edit_startup_disable() {
    local f="$HOME/.desktop-session/startup"
    local PW_CMD="pidof -q pipewire || pipewire"
    [[ ! -f "$f" ]] && return 0

    # Normalise legacy pipewire-start entries
    if grep -q "pipewire-start" "$f"; then
        sed -i '/^pipewire-start/s/^/#/g' "$f"
        if ! grep -q "pipewire &" "$f"; then
            sed -i -e "/^#pipewire-start/s/^#.*/#${PW_CMD} \&/" "$f"
        fi
    fi

    # Ensure a canonical pipewire launch line exists before we comment it
    if ! grep -q "pipewire &" "$f"; then
        echo "${PW_CMD} &" >> "$f"
    fi

    # Comment out the pipewire launch line (disable path)
    if ! grep -q "^#.*pipewire &" "$f"; then
        sed -i '/pipewire &/s/^/#/g' "$f"
    fi

    print_success "  startup → $(gettext "pipewire lines commented out")"
}

_pipewire_preview() {
    section "$(gettext "PIPEWIRE → ALSA MIGRATION")"
    print_info "$(gettext "The following changes will be made before purging:")"
    echo >&2
    printf "    ${DIM}%-44s${NC}  %s\n" \
        "/etc/alsa/conf.d/50-pipewire.conf"         "$(gettext "symlink removed")"             >&2
    printf "    ${DIM}%-44s${NC}  %s\n" \
        "/etc/alsa/conf.d/99-pipewire-default.conf" "$(gettext "symlink removed")"             >&2
    printf "    ${DIM}%-44s${NC}  %s\n" \
        "alsactl -F nrestore"                        "$(gettext "ALSA state restored")"         >&2
    printf "    ${DIM}%-44s${NC}  %s\n" \
        "~/.xmms/config"                             "libxmms-pulse.so → libALSA.so"            >&2
    printf "    ${DIM}%-44s${NC}  %s\n" \
        "~/.config/cmus/rc"                          "output_plugin=pulse → alsa"               >&2
    printf "    ${DIM}%-44s${NC}  %s\n" \
        "~/.desktop-session/startup"                 "$(gettext "pipewire lines normalised then commented out")" >&2
    printf "    ${DIM}%-44s${NC}  %s\n" \
        "wireplumber + pipewire"                     "$(gettext "processes killed (pkill wire)")" >&2
    printf "    ${DIM}%-44s${NC}  %s\n" \
        "~/.config/volumeicon"                       "onclick → alsamixer"                      >&2
    printf "    ${DIM}%-44s${NC}  %s\n" \
        "volumeicon"                                 "$(gettext "restarted with new config")"   >&2
    printf "    ${DIM}%-44s${NC}  %s\n" \
        "~/.asoundrc"                                "$(gettext "written for best ALSA card")"  >&2
    echo >&2

    local cards_raw picked
    cards_raw=$(_alsa_list_cards)
    if [[ -n "$cards_raw" ]]; then
        print_info "$(gettext "ALSA cards detected:")"
        while IFS= read -r line; do
            echo -e "    ${DIM}${line}${NC}" >&2
        done <<< "$cards_raw"
        picked=$(_alsa_pick_card)
        [[ -n "$picked" ]] && print_info "$(gettext "Will use card:") ${BLD}${picked}${NC}"
    else
        print_warn "$(gettext "No ALSA cards detected — ~/.asoundrc will not be written.")"
    fi
    echo >&2
    print_info "$(gettext "Scope: current user ($USER) only.")"
    print_warn "$(gettext "Other users must run alsa-set-default-card after reboot.")"
}

_pipewire_execute() {
    echo >&2
    print_status "$(gettext "Migrating PipeWire → ALSA...")"

    # 1. Remove ALSA/PipeWire symlinks + restore kernel ALSA state
    _alsa_remove_pipewire_conf

    # 2. Fix per-application audio configs
    local f
    f="$HOME/.xmms/config"
    [[ -w "$f" ]] && sed -i 's/libxmms-pulse\.so/libALSA.so/g' "$f" \
        && print_success "  xmms → ALSA output plugin"

    f="$HOME/.config/cmus/rc"
    [[ -w "$f" ]] && sed -i 's/output_plugin=pulse/output_plugin=alsa/' "$f" \
        && print_success "  cmus → ALSA output plugin"

    # 3. Normalise then disable pipewire in desktop-session startup
    _edit_startup_disable

    # 4. Kill PipeWire + WirePlumber (pkill wire is toggle_pipewire's catch-all)
    pkill -x wireplumber 2>/dev/null || true
    pkill -x pipewire    2>/dev/null || true
    pkill    wire        2>/dev/null || true
    sleep 0.8

    if _pipewire_running; then
        print_warn "$(gettext "PipeWire still alive — force killing...")"
        pkill -9 -x wireplumber 2>/dev/null || true
        pkill -9 -x pipewire    2>/dev/null || true
        pkill -9    wire        2>/dev/null || true
        sleep 0.4
    fi
    print_success "  wireplumber + pipewire → stopped"

    # 5. volumeicon: switch onclick to alsamixer and restart
    local vi_conf="$HOME/.config/volumeicon/volumeicon"
    if [[ -f "$vi_conf" ]] && grep -q "onclick=" "$vi_conf"; then
        sed -i '/^onclick=/s/=.*/=urxvt -e alsamixer/' "$vi_conf" \
            && print_success "  volumeicon → alsamixer onclick"
        if [[ -x /usr/bin/volumeicon ]]; then
            pkill volumeicon 2>/dev/null || true
            sleep 1
            volumeicon & disown
            print_success "  volumeicon → restarted"
        fi
    fi

    # 6. Write ~/.asoundrc for the best card (full alsa-set-default-card logic)
    _alsa_setup

    echo >&2
}

# ---------------------------------------------------------------------------
# Service management
# ---------------------------------------------------------------------------

stop_turnstile() {
    print_status "$(gettext "Stopping turnstile...")"
    for cmd in dinitctl sv s6-svc 66-stop systemctl; do
        command -v "$cmd" >/dev/null || continue
        case $cmd in
            dinitctl)
                sudo dinitctl stop    turnstiled >/dev/null 2>&1 || true
                sudo dinitctl disable turnstiled >/dev/null 2>&1 || true ;;
            sv)
                sudo sv stop turnstiled >/dev/null 2>&1 || true
                [[ -h /etc/service/turnstiled ]] && sudo rm /etc/service/turnstiled ;;
            s6-svc)
                sudo s6-svc -d /run/service/turnstiled >/dev/null 2>&1 || true ;;
            66-stop)
                sudo 66-stop    turnstiled >/dev/null 2>&1 || true
                sudo 66-disable turnstiled >/dev/null 2>&1 || true ;;
            systemctl)
                sudo systemctl stop    turnstiled >/dev/null 2>&1 || true
                sudo systemctl disable turnstiled >/dev/null 2>&1 || true ;;
        esac
    done
    print_success "$(gettext "Turnstile stopped.")"
    echo >&2
}

# ---------------------------------------------------------------------------
# Plan display, backup, removal
# ---------------------------------------------------------------------------

display_plan() {
    local total=$(( ${#REMOVE_LIST[@]} + ${#extra_kernel_pkgs[@]} ))
    echo >&2; sep
    echo -e "  ${BOLD}$(gettext "REMOVAL PLAN")${NC}" >&2
    sep

    section "$(gettext "KERNEL CONFIGURATION")"
    echo -e "  ${GREEN}$(gettext "Current kernel:") ${BOLD}$(current_kernel)${NC}" >&2
    if [[ "$KEEP_BOTH_KERNELS" = true ]]; then
        print_info "$(gettext "All installed kernels will be kept.")"
    else
        print_info "$(gettext "Only the current kernel will be kept.")"
        if [[ ${#extra_kernel_pkgs[@]} -gt 0 ]]; then
            print_info "$(gettext "Old kernel packages queued for removal:")"
            fmt_cols "${extra_kernel_pkgs[@]}"
        fi
    fi

    _pipewire_preview

    echo >&2; sep
    printf "  ${BOLD}$(gettext "Packages queued")${NC}  :  %d\n" "$total" >&2
    printf "  ${DIM}$(gettext "(absent packages are silently skipped by apt)")${NC}\n" >&2
    sep; echo >&2
}

save_backup() {
    local file="$1"
    print_status "$(gettext "Saving package list to backup...")"
    {
        echo "# antiX Full-to-Base Removal Backup"
        echo "# Script version: $VERSION"
        echo "# Date: $(date '+%Y-%m-%d %H:%M:%S %Z')"
        echo "# Host: $(hostname)"
        echo "# Kernel: $(current_kernel)"
        echo "#"
        echo "# To restore: sudo apt-get install \$(grep -v '^#' \"$file\" | xargs)"
        echo ""
        printf '%s\n' "${REMOVE_LIST[@]}" "${extra_kernel_pkgs[@]}" \
            | grep -v '^$' | sort -u
    } > "$file"
    success_v 'Backup saved to: $BFILE' BFILE "$file"
    echo >&2
}

handle_orphans() {
    echo >&2
    print_status "$(gettext "Checking for orphaned packages...")"
    local orphans=()
    while IFS= read -r line; do
        [[ "$line" =~ ^[[:space:]]+([a-z0-9][a-z0-9.+\-]+) ]] \
            && orphans+=("${BASH_REMATCH[1]}")
    done < <(sudo apt-get autoremove --purge --simulate 2>/dev/null)

    if [[ ${#orphans[@]} -eq 0 ]]; then
        print_info "$(gettext "No orphaned packages found.")"
        return
    fi

    status_v 'Orphaned packages found: $COUNT' COUNT "${#orphans[@]}"
    echo >&2; fmt_cols "${orphans[@]}"; echo >&2

    local do_autoremove=true
    if [[ "$INTERACTIVE" = true ]]; then
        read -rp "$(gettext "Remove orphaned packages?") [y/N] " r
        [[ ! "$r" =~ ^[Yy]$ ]] && do_autoremove=false
    fi

    if [[ "$do_autoremove" = true ]]; then
        sudo apt-get autoremove --purge -y
        sudo apt-get autoclean -y
        print_success "$(gettext "Orphaned packages removed.")"
    else
        print_warn "$(gettext "Orphans kept. Run: sudo apt autoremove --purge")"
    fi
}

show_summary() {
    echo >&2; sep
    echo -e "  ${BOLD}${GREEN}$(gettext "CONVERSION COMPLETE")${NC}" >&2
    sep; echo >&2
    print_success "$(gettext "System is now antiX Base edition.")"
    print_success "$(gettext "Current kernel is protected.")"
    echo >&2
    print_info "$(gettext "Recommendation: Reboot or Remaster the system.")"
    echo >&2
}

do_remove() {
    display_plan
    [[ -n "$BACKUP_FILE" ]] && save_backup "$BACKUP_FILE"

    if [[ "$DRY_RUN" = true ]]; then
        echo -e "  ${BOLD}$(gettext "DRY RUN — no changes will be made.")${NC}" >&2
        echo >&2
        print_info "$(gettext "Packages queued (apt skips absent ones automatically):")"
        fmt_cols "${REMOVE_LIST[@]}" "${extra_kernel_pkgs[@]}"
        return 0
    fi

    if [[ "$INTERACTIVE" = true ]]; then
        local total=$(( ${#REMOVE_LIST[@]} + ${#extra_kernel_pkgs[@]} ))
        echo -e "  ${RED}${BOLD}$(msgv 'About to permanently purge up to $COUNT packages.' COUNT "$total")${NC}" >&2
        echo -e "  $(gettext "Packages not installed will be silently skipped by apt.")" >&2
        echo >&2
        read -rp "  $(gettext "Proceed with removal?") [y/N] " r
        echo >&2
        [[ ! "$r" =~ ^[Yy]$ ]] && {
            print_warn "$(gettext "Operation cancelled by user.")"
            exit 0
        }
    fi

    stop_turnstile
    _pipewire_execute

    export NO_AT_BRIDGE=1
    local all_pkgs=( "${REMOVE_LIST[@]}" "${extra_kernel_pkgs[@]}" )

    print_status "$(gettext "Resolving package list...")"
    local to_purge=()
    mapfile -t to_purge < <(
        dpkg-query --showformat='${db:Status-Abbrev} ${Package}\n' \
            -W "${all_pkgs[@]}" 2>/dev/null \
        | awk '/^[iurhp]/{print $2}'
    )

    if [[ ${#to_purge[@]} -eq 0 ]]; then
        print_info "$(gettext "No matching installed packages found. Nothing to purge.")"
        return 0
    fi
    print_success "$(gettext "Packages to purge: ${#to_purge[@]} of ${#all_pkgs[@]} queued")"
    echo >&2

    local total=${#to_purge[@]}
    print_status "$(gettext "Purging packages ...")"
    echo >&2

    sudo apt-get purge -y "${to_purge[@]}" 2>&1 \
        | apt_progress "$total"
    local apt_rc=${PIPESTATUS[0]}

    if [[ $apt_rc -ne 0 ]]; then
        print_warn "$(gettext "apt returned non-zero ($apt_rc) — force-purging any remaining...")"
        for pkg in "${to_purge[@]}"; do
            dpkg -l "$pkg" 2>/dev/null | grep -q "^ii" \
                && sudo dpkg --purge --force-depends "$pkg" >/dev/null 2>&1 \
                && warn_v 'Force-purged: $PKG' PKG "$pkg"
        done
    fi

    print_success "$(gettext "Package removal complete.")"
    handle_orphans
    show_summary
}

main() {
    echo -e "${BOLD}antix-full-to-base v${VERSION}${NC}" >&2
    echo >&2
    check_root
    check_antix
    check_full_edition
    _collect_old_kernels

    if [[ ${#REMOVE_LIST[@]} -eq 0 && ${#extra_kernel_pkgs[@]} -eq 0 ]]; then
        print_info "$(gettext "REMOVE_LIST is empty and no old kernels found. Nothing to do.")"
        exit 0
    fi

    do_remove
}

main
