#!/bin/bash
# tui-install.sh — TUI installer for the_miro's Arch dotfiles

set -uo pipefail

# ── Paths ─────────────────────────────────────────────────────────────────────
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
DOTFILES_DIR="$(cd "$SCRIPT_DIR/.." && pwd)"
MODULES="$DOTFILES_DIR/setup/modules"
APPS="$MODULES/optional-Modules/apps"
LOG="$HOME/dotfiles-install.log"
TMP_D="$(mktemp -d)"
trap 'rm -rf "$TMP_D"; tput reset 2>/dev/null || stty sane 2>/dev/null || true' EXIT INT TERM HUP

ANSWERFILE="${ANSWERFILE:-/answerfile.json}"
ANSWERFILE_MODE=false
[[ -f "$ANSWERFILE" ]] && ANSWERFILE_MODE=true

BACKTITLE="the_miro's Arch Dotfiles"

# ── Terminal dimensions (bare console safe) ───────────────────────────────────
TERM_H=$(tput lines 2>/dev/null || echo 24)
TERM_W=$(tput cols  2>/dev/null || echo 80)

# ── Cyberqueer dialog theme ───────────────────────────────────────────────────
export DIALOGRC="$TMP_D/dialogrc"
cat > "$DIALOGRC" <<'EOF'
use_shadow = ON
use_colors = ON
screen_color              = (BLACK,BLACK,ON)
shadow_color              = (BLACK,BLACK,ON)
title_color               = (MAGENTA,BLACK,ON)
border_color              = (MAGENTA,BLACK,ON)
button_active_color       = (BLACK,MAGENTA,ON)
button_inactive_color     = (WHITE,BLACK,OFF)
button_key_active_color   = (BLACK,CYAN,ON)
button_key_inactive_color = (CYAN,BLACK,ON)
button_label_active_color   = (BLACK,MAGENTA,ON)
button_label_inactive_color = (WHITE,BLACK,OFF)
inputbox_color        = (WHITE,BLACK,OFF)
inputbox_border_color = (MAGENTA,BLACK,ON)
menubox_color         = (WHITE,BLACK,OFF)
menubox_border_color  = (MAGENTA,BLACK,ON)
item_color            = (WHITE,BLACK,OFF)
item_selected_color   = (BLACK,MAGENTA,ON)
tag_color             = (CYAN,BLACK,ON)
tag_selected_color    = (BLACK,CYAN,ON)
tag_key_color         = (CYAN,BLACK,ON)
tag_key_selected_color = (BLACK,CYAN,ON)
check_color           = (WHITE,BLACK,OFF)
check_selected_color  = (BLACK,MAGENTA,ON)
uarrow_color          = (MAGENTA,BLACK,ON)
darrow_color          = (MAGENTA,BLACK,ON)
EOF

# ── State ─────────────────────────────────────────────────────────────────────
STEP=0
TOTAL=0

# ── Helpers ───────────────────────────────────────────────────────────────────
require_dialog() {
    command -v dialog &>/dev/null && return
    echo "dialog not found — installing..."
    sudo pacman -S --noconfirm dialog || { echo "Failed to install dialog."; exit 1; }
}

require_jq() {
    command -v jq &>/dev/null && return
    echo "jq not found — installing..."
    sudo pacman -S --noconfirm jq || { echo "Failed to install jq."; exit 1; }
}

die() {
    clear
    printf "\n  Error: %s\n\n" "$1" >&2
    exit 1
}

log_sep() {
    printf "\n══════════════════════════════════\n  %s\n  %s\n" "$1" "$(date)" >> "$LOG"
}

run_module() {
    local label="$1" script="$2"
    STEP=$(( STEP + 1 ))
    log_sep "[$STEP/$TOTAL] $label"

    clear
    printf "\n\033[1;35m  [$STEP/$TOTAL] %s\033[0m\n" "$label"
    printf "\033[35m  ─────────────────────────────────────────────\033[0m\n\n"

    local rc=0
    bash "$script" 2>&1 | tee -a "$LOG" || rc=${PIPESTATUS[0]}

    if [[ $rc -ne 0 ]]; then
        if [[ $ANSWERFILE_MODE == true ]]; then
            printf "\n  Warning: %s exited with code %d — continuing.\n" "$label" "$rc" | tee -a "$LOG"
        else
            dialog --backtitle "$BACKTITLE" \
                   --title " Module Failed " \
                   --yesno "$label exited with code $rc.\n\nContinue anyway?" 8 54 \
                || { clear; exit 1; }
        fi
    fi
}

count_steps() {
    local c="$1" de="$2" a="${3:-}"
    TOTAL=0
    [[ "$c" == *"pkg"*   ]] && TOTAL=$(( TOTAL + 1 ))
    [[ "$c" == *"core"*  ]] && TOTAL=$(( TOTAL + 1 ))
    [[ "$c" == *"svc"*   ]] && TOTAL=$(( TOTAL + 1 ))
    [[ "$c" == *"shell"* ]] && TOTAL=$(( TOTAL + 1 ))
    [[ "$de" != "none" ]] && TOTAL=$(( TOTAL + 1 ))
    [[ "$a" == *"ollama"*         ]] && TOTAL=$(( TOTAL + 1 ))
    [[ "$a" == *"llama-cpp"*      ]] && TOTAL=$(( TOTAL + 1 ))
    [[ "$a" == *"open-webui"*     ]] && TOTAL=$(( TOTAL + 1 ))
    [[ "$a" == *"claude"*         ]] && TOTAL=$(( TOTAL + 1 ))
    [[ "$a" == *"networking-cli"* ]] && TOTAL=$(( TOTAL + 1 ))
    [[ "$a" == *"disk-recovery"*  ]] && TOTAL=$(( TOTAL + 1 ))
    [[ "$a" == *"himalaya"*       ]] && TOTAL=$(( TOTAL + 1 ))
    [[ "$a" == *"gnuplot"*        ]] && TOTAL=$(( TOTAL + 1 ))
    [[ "$a" == *"povray"*         ]] && TOTAL=$(( TOTAL + 1 ))
    [[ "$a" == *"blender"*        ]] && TOTAL=$(( TOTAL + 1 ))
    [[ "$a" == *"toot"*           ]] && TOTAL=$(( TOTAL + 1 ))
    [[ "$a" == *"db-clients"*     ]] && TOTAL=$(( TOTAL + 1 ))
    [[ "$a" == *"mysql"*          ]] && TOTAL=$(( TOTAL + 1 ))
    [[ "$a" == *"productivity"*   ]] && TOTAL=$(( TOTAL + 1 ))
    [[ "$a" == *"yt-dlp"*         ]] && TOTAL=$(( TOTAL + 1 ))
    [[ "$a" == *"sox"*            ]] && TOTAL=$(( TOTAL + 1 ))
    [[ "$a" == *"imagemagick"*    ]] && TOTAL=$(( TOTAL + 1 ))
    [[ "$a" == *"ffmpeg"*         ]] && TOTAL=$(( TOTAL + 1 ))
    [[ "$a" == *"localtunnel"*    ]] && TOTAL=$(( TOTAL + 1 ))
    [[ "$a" == *"butter"*         ]] && TOTAL=$(( TOTAL + 1 ))
    [[ "$a" == *"tlp"*            ]] && TOTAL=$(( TOTAL + 1 ))
    [[ "$a" == *"steam"*          ]] && TOTAL=$(( TOTAL + 1 ))
    [[ "$a" == *"vesktop"*        ]] && TOTAL=$(( TOTAL + 1 ))
    [[ "$a" == *"spotify"*        ]] && TOTAL=$(( TOTAL + 1 ))
    [[ "$a" == *"prism"*          ]] && TOTAL=$(( TOTAL + 1 ))
    [[ "$a" == *"vintagestory"*   ]] && TOTAL=$(( TOTAL + 1 ))
    [[ "$a" == *"localsend"*      ]] && TOTAL=$(( TOTAL + 1 ))
    [[ "$a" == *"croc"*           ]] && TOTAL=$(( TOTAL + 1 ))
    [[ "$a" == *"onlyoffice"*     ]] && TOTAL=$(( TOTAL + 1 ))
    [[ "$a" == *"wireshark"*      ]] && TOTAL=$(( TOTAL + 1 ))
    [[ "$a" == *"k8s"*            ]] && TOTAL=$(( TOTAL + 1 ))
    [[ "$a" == *"docker"*         ]] && TOTAL=$(( TOTAL + 1 ))
    [[ "$a" == *"podman"*         ]] && TOTAL=$(( TOTAL + 1 ))
    [[ "$a" == *"cockpit"*        ]] && TOTAL=$(( TOTAL + 1 ))
    [[ "$a" == *"ssh-server"*     ]] && TOTAL=$(( TOTAL + 1 ))
    [[ "$a" == *"freeipa-client"*  ]] && TOTAL=$(( TOTAL + 1 ))
    [[ "$a" == *"freeipa-server"*  ]] && TOTAL=$(( TOTAL + 1 ))
    [[ "$a" == *"freeipa-image"*   ]] && TOTAL=$(( TOTAL + 1 ))
    [[ "$a" == *"python"*         ]] && TOTAL=$(( TOTAL + 1 ))
    [[ "$a" == *"zfs"*            ]] && TOTAL=$(( TOTAL + 1 ))
    [[ "$a" == *"wprs"*           ]] && TOTAL=$(( TOTAL + 1 ))
    [[ "$a" == *"chromium"*       ]] && TOTAL=$(( TOTAL + 1 ))
    [[ "$a" == *"firefox-browser"* ]] && TOTAL=$(( TOTAL + 1 ))
    [[ "$a" == *"zen-browser"*    ]] && TOTAL=$(( TOTAL + 1 ))
    [[ "$a" == *"nyxt"*           ]] && TOTAL=$(( TOTAL + 1 ))
    [[ "$a" == *"librewolf"*      ]] && TOTAL=$(( TOTAL + 1 ))
    [[ "$a" == *"min-browser"*    ]] && TOTAL=$(( TOTAL + 1 ))
    [[ "$a" == *"vscodium"*       ]] && TOTAL=$(( TOTAL + 1 ))
    [[ "$a" == *"zed-ide"*        ]] && TOTAL=$(( TOTAL + 1 ))
    [[ "$a" == *"geany"*          ]] && TOTAL=$(( TOTAL + 1 ))
    [[ "$a" == *"codeblocks"*     ]] && TOTAL=$(( TOTAL + 1 ))
    [[ "$a" == *"kate"*           ]] && TOTAL=$(( TOTAL + 1 ))
    [[ "$a" == *"rdp-client"*       ]] && TOTAL=$(( TOTAL + 1 ))
    [[ "$a" == *"lamco-rdp-server"* ]] && TOTAL=$(( TOTAL + 1 ))
    [[ "$a" == *"qemu"*             ]] && TOTAL=$(( TOTAL + 1 ))
}

# ── Answerfile ────────────────────────────────────────────────────────────────
AF_HOSTNAME=""
AF_COMPONENTS=""
AF_DE="none"
AF_APPS=""
AF_COLOR_TEXT=""
AF_COLOR_BG=""
AF_COLOR_HIGHLIGHT=""
AF_COLOR_DARK=""
AF_COLOR_RED=""

load_answerfile() {
    require_jq
    AF_HOSTNAME=$(jq -r '.hostname // ""' "$ANSWERFILE")
    AF_COMPONENTS=$(jq -r '(.components // []) | join(" ")' "$ANSWERFILE")
    AF_DE=$(jq -r '.desktop_environment // "none"' "$ANSWERFILE")
    AF_APPS=$(jq -r '(.apps // []) | join(" ")' "$ANSWERFILE")
    AF_COLOR_TEXT=$(jq -r '.colors.COLOR_TEXT // ""' "$ANSWERFILE")
    AF_COLOR_BG=$(jq -r '.colors.COLOR_BG // ""' "$ANSWERFILE")
    AF_COLOR_HIGHLIGHT=$(jq -r '.colors.COLOR_HIGHLIGHT // ""' "$ANSWERFILE")
    AF_COLOR_DARK=$(jq -r '.colors.COLOR_DARK // ""' "$ANSWERFILE")
    AF_COLOR_RED=$(jq -r '.colors.COLOR_RED // ""' "$ANSWERFILE")
}

# ── MAC address helper ────────────────────────────────────────────────────────
get_mac_suffix() {
    local mac
    mac=$(ip link show 2>/dev/null \
        | awk '/^[0-9]+: [^l][^o]/{iface=1} iface && /link\/ether/{print $2; iface=0; exit}')
    printf '%s' "${mac//:/}"
}

# ── Preflight ─────────────────────────────────────────────────────────────────
[[ $EUID -eq 0 ]] && die "Run as your normal user (not root)."
command -v pacman &>/dev/null || die "pacman not found — Arch Linux required."

require_dialog

if $ANSWERFILE_MODE; then
    load_answerfile
    printf "Answerfile mode: %s\n" "$ANSWERFILE" | tee -a "$LOG"
fi

if ! ping -c1 -W3 archlinux.org &>/dev/null; then
    if $ANSWERFILE_MODE; then
        printf "Warning: no internet connection detected.\n" | tee -a "$LOG"
    else
        dialog --backtitle "$BACKTITLE" \
               --title " No Network Detected " \
               --msgbox "\n  No internet connection found.\n\n  nmtui will open so you can configure networking.\n  Close nmtui when done to continue the installer.\n" 11 58
        nmtui
        if ! ping -c1 -W3 archlinux.org &>/dev/null; then
            dialog --backtitle "$BACKTITLE" \
                   --title " Still Offline " \
                   --yesno "\n  Still no internet connection.\n\n  Packages cannot be downloaded without network access.\n\n  Continue anyway?" 11 58 \
                || { clear; echo "Aborted — no network."; exit 1; }
        fi
    fi
fi

> "$LOG"
printf "Dotfiles install: %s\nDotfiles dir:    %s\n" "$(date)" "$DOTFILES_DIR" >> "$LOG"

# ── Welcome ───────────────────────────────────────────────────────────────────
if ! $ANSWERFILE_MODE; then
    dialog --backtitle "$BACKTITLE" \
        --title " Welcome " \
        --msgbox "\n\
  the_miro's Arch dotfiles installer\n\
  Cyberqueer  ·  Wayland  ·  Hyprland\n\
  ─────────────────────────────────────────\n\
\n\
  Arch Linux — network admin, development & gaming\n\
\n\
  Source:  $DOTFILES_DIR\n\
  Log:     $LOG\n" 14 62
fi

# ── Hostname ──────────────────────────────────────────────────────────────────
HOSTNAME_SET=""
if $ANSWERFILE_MODE; then
    if [[ -n "$AF_HOSTNAME" ]]; then
        MAC=$(get_mac_suffix)
        HOSTNAME_SET="${AF_HOSTNAME}-${MAC}"
        printf "Hostname (from answerfile + MAC): %s\n" "$HOSTNAME_SET" | tee -a "$LOG"
    fi
else
    HOSTNAME_INPUT=$(dialog --backtitle "$BACKTITLE" \
        --title " Hostname " \
        --inputbox "\n  Hostname for this machine (leave blank to keep default).\n" 9 54 "" \
        3>&1 1>&2 2>&3) || HOSTNAME_INPUT=""
    HOSTNAME_SET="$HOSTNAME_INPUT"
fi

if [[ -n "$HOSTNAME_SET" ]]; then
    sudo hostnamectl set-hostname "$HOSTNAME_SET" 2>/dev/null \
        || echo "$HOSTNAME_SET" | sudo tee /etc/hostname > /dev/null
    printf "Hostname set: %s\n" "$HOSTNAME_SET" >> "$LOG"
fi

# ── Component selection ───────────────────────────────────────────────────────
if $ANSWERFILE_MODE; then
    COMPONENTS="$AF_COMPONENTS"
else
    COMPONENTS=$(dialog --backtitle "$BACKTITLE" \
        --title " Select Components " \
        --checklist "Space toggles  ·  Enter confirms  ·  Esc quits" 15 68 4 \
        "pkg"   "Package managers    yay · nvm · rust"                     on  \
        "core"  "Core packages       100+ base system packages"            on  \
        "svc"   "Core services       NetworkManager · cronie · fail2ban"   on  \
        "shell" "Shell setup         zsh · nvim · yazi · micro · starship" on  \
        3>&1 1>&2 2>&3) || { clear; echo "Aborted."; exit 0; }
fi

# ── DE selection ──────────────────────────────────────────────────────────────
if $ANSWERFILE_MODE; then
    DE="$AF_DE"
else
    DE=$(dialog --backtitle "$BACKTITLE" \
        --title " Desktop Environment " \
        --menu "Select a desktop environment  ·  Esc / none to skip:" 22 72 10 \
        "hyprlua"    "HyprLua    — Hyprland with Lua config (recommended)" \
        "hyprland"   "Hyprland   — Wayland WM, hyprlang config (legacy)" \
        "sway"       "Sway       — Wayland tiling WM" \
        "kde-plasma" "KDE Plasma — feature-rich Wayland/X11 DE" \
        "gnome"      "GNOME      — modern Wayland DE" \
        "cosmic"     "COSMIC     — Rust-built Wayland DE (System76)" \
        "xfce"       "XFCE       — lightweight X11 DE" \
        "lxqt"       "LXQt       — lightweight Qt X11 DE" \
        "none"       "Skip DE installation" \
        3>&1 1>&2 2>&3) || DE="none"
fi

# ── Apps selection ────────────────────────────────────────────────────────────
if $ANSWERFILE_MODE; then
    SELECTED_APPS="$AF_APPS"
else
    _APP_H=$(( TERM_H - 2 < 40 ? TERM_H - 2 : 40 ))
    _APP_LIST_H=$(( _APP_H - 8 < 4 ? 4 : _APP_H - 8 ))
    SELECTED_APPS=$(dialog --backtitle "$BACKTITLE" \
        --title " Applications " \
        --checklist "Optional applications — installed after base components:" "$_APP_H" 76 "$_APP_LIST_H" \
        "ollama"          "Ollama            local LLM runner + API server"          off \
        "llama-cpp"       "llama.cpp         standalone inference CLI + server"      off \
        "open-webui"      "Open WebUI        browser UI for Ollama / LLM backends"  off \
        "claude"          "Claude Code       Anthropic CLI via npm"                 off \
        "networking-cli"  "Networking CLI    nmap · nethogs · mitmproxy · httpie"  off \
        "disk-recovery"   "Disk Recovery     ddrescue · f3"                         off \
        "himalaya"        "Himalaya          terminal email client (AUR)"           off \
        "gnuplot"         "Gnuplot           scientific plotting"                   off \
        "povray"          "POV-Ray           ray-tracing renderer"                  off \
        "blender"         "Blender           3D creation suite"                     off \
        "toot"            "toot              Mastodon CLI client (AUR)"             off \
        "db-clients"      "DB Clients        pgcli · mycli"                         off \
        "mysql"           "MySQL / MariaDB   mariadb server + setup"                off \
        "productivity"    "Productivity      taskwarrior · watson · jrnl"           off \
        "yt-dlp"          "yt-dlp            YouTube / media downloader"            off \
        "sox"             "SoX               audio processing toolkit"              off \
        "imagemagick"     "ImageMagick       image manipulation"                    off \
        "ffmpeg"          "FFmpeg extras     thumbnailer · GStreamer codecs"        off \
        "localtunnel"     "LocalTunnel       expose localhost via tunnel"           off \
        "butter"          "butter            btrfs snapshot backup (AUR)"           off \
        "tlp"             "TLP               laptop power management"               off \
        "steam"           "Steam             gaming platform"                       off \
        "vesktop"         "Vesktop           Discord + Vencord theme"               off \
        "spotify"         "Spotify           launcher + Spicetify theming"          off \
        "prism"           "PrismLauncher     Minecraft launcher (Flatpak)"          off \
        "vintagestory"    "Vintage Story     survival game (AUR)"                   off \
        "localsend"       "LocalSend         LAN file transfer (AUR)"               off \
        "croc"            "croc              cross-platform file transfer"          off \
        "onlyoffice"      "OnlyOffice        office suite (AUR)"                   off \
        "wireshark"       "Wireshark         network packet analyser (GUI)"         off \
        "k8s"             "Kubernetes tools  kubectl · podman-desktop"              off \
        "docker"          "Docker            docker · docker-compose"               off \
        "podman"          "Podman            rootless containers · buildah"         off \
        "cockpit"         "Cockpit           web UI · machines · podman"            off \
        "ssh-server"      "SSH server        openssh · key-auth · enabled"          off \
        "freeipa-client"  "FreeIPA Client    sssd + ipa-client-install + enrollment"  off \
        "freeipa-server"  "FreeIPA Server    interactive server setup + client gen"  off \
        "freeipa-image"   "FreeIPA Image     OCI/LXC/Proxmox LXC builder + Keycloak" off \
        "python"          "Python tools      pyright · pipx · pynvim"              off \
        "zfs"             "ZFS               zfs-dkms kernel module"                off \
        "wprs"            "WPRS              wprs-git (AUR)"                        off \
        \
        "chromium"        "Chromium          open-source browser (official)"        off \
        "firefox-browser" "Firefox           Mozilla browser (official)"            off \
        "zen-browser"     "Zen Browser       Firefox-based privacy browser (AUR)"   off \
        "nyxt"            "Nyxt              keyboard-driven browser (AUR)"         off \
        "librewolf"       "LibreWolf         hardened Firefox fork (AUR)"           off \
        "min-browser"     "Min               minimal Electron browser (AUR)"        off \
        \
        "vscodium"        "VSCodium          telemetry-free VS Code (AUR)"          off \
        "zed-ide"         "Zed               high-performance Rust IDE (official)"  off \
        "geany"           "Geany             lightweight IDE + plugins (official)"  off \
        "codeblocks"      "Code::Blocks      C/C++ IDE (official)"                  off \
        "kate"            "Kate              KDE advanced text editor (official)"   off \
        \
        "rdp-client"       "RDP Client        Remmina + FreeRDP + VNC plugins"           off \
        "lamco-rdp-server" "Lamco RDP Server  native Wayland RDP server (AUR, Rust)"   off \
        "qemu"             "QEMU/KVM          full virt stack + virt-manager GUI"       off \
        3>&1 1>&2 2>&3) || SELECTED_APPS=""
fi

# ── Confirmation (interactive mode only) ──────────────────────────────────────
if ! $ANSWERFILE_MODE; then
    SUMMARY=""
    [[ -n "$HOSTNAME_SET" ]] && SUMMARY+="  ✦ Hostname: $HOSTNAME_SET\n"
    [[ "$COMPONENTS" == *"pkg"*  ]] && SUMMARY+="  ✦ Package managers (yay, nvm, rust)\n"
    [[ "$COMPONENTS" == *"core"* ]] && SUMMARY+="  ✦ Core packages\n"
    [[ "$COMPONENTS" == *"svc"*  ]] && SUMMARY+="  ✦ Core services\n"
    [[ "$COMPONENTS" == *"shell"* ]] && SUMMARY+="  ✦ Shell setup\n"
    [[ "$DE" != "none" ]] && SUMMARY+="  ✦ Desktop environment: $DE\n"

    if [[ -n "$SELECTED_APPS" ]]; then
        SUMMARY+="\n  Applications:\n"
        [[ "$SELECTED_APPS" == *"ollama"*         ]] && SUMMARY+="  ✦ Ollama\n"
        [[ "$SELECTED_APPS" == *"llama-cpp"*      ]] && SUMMARY+="  ✦ llama.cpp\n"
        [[ "$SELECTED_APPS" == *"open-webui"*     ]] && SUMMARY+="  ✦ Open WebUI\n"
        [[ "$SELECTED_APPS" == *"claude"*         ]] && SUMMARY+="  ✦ Claude Code\n"
        [[ "$SELECTED_APPS" == *"networking-cli"* ]] && SUMMARY+="  ✦ Networking CLI (nmap, nethogs, mitmproxy, httpie)\n"
        [[ "$SELECTED_APPS" == *"disk-recovery"*  ]] && SUMMARY+="  ✦ Disk Recovery (ddrescue, f3)\n"
        [[ "$SELECTED_APPS" == *"himalaya"*       ]] && SUMMARY+="  ✦ Himalaya\n"
        [[ "$SELECTED_APPS" == *"gnuplot"*        ]] && SUMMARY+="  ✦ Gnuplot\n"
        [[ "$SELECTED_APPS" == *"povray"*         ]] && SUMMARY+="  ✦ POV-Ray\n"
        [[ "$SELECTED_APPS" == *"blender"*        ]] && SUMMARY+="  ✦ Blender\n"
        [[ "$SELECTED_APPS" == *"toot"*           ]] && SUMMARY+="  ✦ toot\n"
        [[ "$SELECTED_APPS" == *"db-clients"*     ]] && SUMMARY+="  ✦ DB Clients (pgcli, mycli)\n"
        [[ "$SELECTED_APPS" == *"mysql"*          ]] && SUMMARY+="  ✦ MySQL / MariaDB\n"
        [[ "$SELECTED_APPS" == *"productivity"*   ]] && SUMMARY+="  ✦ Productivity (taskwarrior, watson, jrnl)\n"
        [[ "$SELECTED_APPS" == *"yt-dlp"*         ]] && SUMMARY+="  ✦ yt-dlp\n"
        [[ "$SELECTED_APPS" == *"sox"*            ]] && SUMMARY+="  ✦ SoX\n"
        [[ "$SELECTED_APPS" == *"imagemagick"*    ]] && SUMMARY+="  ✦ ImageMagick\n"
        [[ "$SELECTED_APPS" == *"ffmpeg"*         ]] && SUMMARY+="  ✦ FFmpeg extras\n"
        [[ "$SELECTED_APPS" == *"localtunnel"*    ]] && SUMMARY+="  ✦ LocalTunnel\n"
        [[ "$SELECTED_APPS" == *"butter"*         ]] && SUMMARY+="  ✦ butter (btrfs backup)\n"
        [[ "$SELECTED_APPS" == *"tlp"*            ]] && SUMMARY+="  ✦ TLP\n"
        [[ "$SELECTED_APPS" == *"steam"*          ]] && SUMMARY+="  ✦ Steam\n"
        [[ "$SELECTED_APPS" == *"vesktop"*        ]] && SUMMARY+="  ✦ Vesktop + Vencord theme\n"
        [[ "$SELECTED_APPS" == *"spotify"*        ]] && SUMMARY+="  ✦ Spotify + Spicetify\n"
        [[ "$SELECTED_APPS" == *"prism"*          ]] && SUMMARY+="  ✦ PrismLauncher\n"
        [[ "$SELECTED_APPS" == *"vintagestory"*   ]] && SUMMARY+="  ✦ Vintage Story\n"
        [[ "$SELECTED_APPS" == *"localsend"*      ]] && SUMMARY+="  ✦ LocalSend\n"
        [[ "$SELECTED_APPS" == *"croc"*           ]] && SUMMARY+="  ✦ croc\n"
        [[ "$SELECTED_APPS" == *"onlyoffice"*     ]] && SUMMARY+="  ✦ OnlyOffice\n"
        [[ "$SELECTED_APPS" == *"wireshark"*      ]] && SUMMARY+="  ✦ Wireshark\n"
        [[ "$SELECTED_APPS" == *"k8s"*            ]] && SUMMARY+="  ✦ Kubernetes tools\n"
        [[ "$SELECTED_APPS" == *"docker"*         ]] && SUMMARY+="  ✦ Docker + Compose\n"
        [[ "$SELECTED_APPS" == *"podman"*         ]] && SUMMARY+="  ✦ Podman (rootless) + Buildah\n"
        [[ "$SELECTED_APPS" == *"cockpit"*        ]] && SUMMARY+="  ✦ Cockpit web UI\n"
        [[ "$SELECTED_APPS" == *"ssh-server"*     ]] && SUMMARY+="  ✦ SSH server (openssh, key auth)\n"
        [[ "$SELECTED_APPS" == *"freeipa-client"* ]] && SUMMARY+="  ✦ FreeIPA Client\n"
        [[ "$SELECTED_APPS" == *"freeipa-server"* ]] && SUMMARY+="  ✦ FreeIPA Server\n"
        [[ "$SELECTED_APPS" == *"freeipa-image"*  ]] && SUMMARY+="  ✦ FreeIPA Image Builder\n"
        [[ "$SELECTED_APPS" == *"python"*         ]] && SUMMARY+="  ✦ Python tools\n"
        [[ "$SELECTED_APPS" == *"zfs"*            ]] && SUMMARY+="  ✦ ZFS\n"
        [[ "$SELECTED_APPS" == *"wprs"*           ]] && SUMMARY+="  ✦ WPRS\n"
        [[ "$SELECTED_APPS" == *"chromium"*       ]] && SUMMARY+="  ✦ Chromium\n"
        [[ "$SELECTED_APPS" == *"firefox-browser"* ]] && SUMMARY+="  ✦ Firefox\n"
        [[ "$SELECTED_APPS" == *"zen-browser"*    ]] && SUMMARY+="  ✦ Zen Browser\n"
        [[ "$SELECTED_APPS" == *"nyxt"*           ]] && SUMMARY+="  ✦ Nyxt\n"
        [[ "$SELECTED_APPS" == *"librewolf"*      ]] && SUMMARY+="  ✦ LibreWolf\n"
        [[ "$SELECTED_APPS" == *"min-browser"*    ]] && SUMMARY+="  ✦ Min Browser\n"
        [[ "$SELECTED_APPS" == *"vscodium"*       ]] && SUMMARY+="  ✦ VSCodium\n"
        [[ "$SELECTED_APPS" == *"zed-ide"*        ]] && SUMMARY+="  ✦ Zed IDE\n"
        [[ "$SELECTED_APPS" == *"geany"*          ]] && SUMMARY+="  ✦ Geany\n"
        [[ "$SELECTED_APPS" == *"codeblocks"*     ]] && SUMMARY+="  ✦ Code::Blocks\n"
        [[ "$SELECTED_APPS" == *"kate"*           ]] && SUMMARY+="  ✦ Kate\n"
        [[ "$SELECTED_APPS" == *"rdp-client"*       ]] && SUMMARY+="  ✦ RDP Client (Remmina + FreeRDP)\n"
        [[ "$SELECTED_APPS" == *"lamco-rdp-server"* ]] && SUMMARY+="  ✦ Lamco RDP Server (native Wayland)\n"
        [[ "$SELECTED_APPS" == *"qemu"*             ]] && SUMMARY+="  ✦ QEMU/KVM + virt-manager\n"
    fi

    _CONF_H=$(( TERM_H - 2 < 24 ? TERM_H - 2 : 24 ))
    dialog --backtitle "$BACKTITLE" \
        --title " Confirm Installation " \
        --yesno "\n Components to install:\n\n${SUMMARY}\n Log: $LOG\n\n Proceed?" \
        "$_CONF_H" 62 || { clear; echo "Aborted."; exit 0; }
fi

count_steps "$COMPONENTS" "$DE" "$SELECTED_APPS"

# ── Installation: base components ─────────────────────────────────────────────
[[ "$COMPONENTS" == *"pkg"*   ]] && run_module "Package Managers" "$MODULES/package-managers.sh"
[[ "$COMPONENTS" == *"core"*  ]] && run_module "Core Packages"    "$MODULES/core-packages.sh"
[[ "$COMPONENTS" == *"svc"*   ]] && run_module "Core Services"    "$MODULES/core.sh"
[[ "$COMPONENTS" == *"shell"* ]] && run_module "Shell Setup"      "$MODULES/shell-setup.sh"

if [[ "$DE" != "none" ]]; then
    case "$DE" in
        hyprlua)    run_module "HyprLua"     "$MODULES/Desktop-Environments/hyprlua.sh" ;;
        hyprland)   run_module "Hyprland"    "$MODULES/Desktop-Environments/hyprland.sh" ;;
        sway)       run_module "Sway"        "$MODULES/Desktop-Environments/sway.sh" ;;
        kde-plasma) run_module "KDE Plasma"  "$MODULES/Desktop-Environments/kde-plasma.sh" ;;
        gnome)      run_module "GNOME"       "$MODULES/Desktop-Environments/gnome.sh" ;;
        cosmic)     run_module "COSMIC"      "$MODULES/Desktop-Environments/cosmic.sh" ;;
        xfce)       run_module "XFCE"        "$MODULES/Desktop-Environments/xfce.sh" ;;
        lxqt)       run_module "LXQt"        "$MODULES/Desktop-Environments/lxqt.sh" ;;
    esac
fi

# ── Installation: applications ────────────────────────────────────────────────
[[ "$SELECTED_APPS" == *"ollama"*         ]] && run_module "Ollama"            "$APPS/ollama.sh"
[[ "$SELECTED_APPS" == *"llama-cpp"*      ]] && run_module "llama.cpp"         "$APPS/llama-cpp.sh"
[[ "$SELECTED_APPS" == *"open-webui"*     ]] && run_module "Open WebUI"        "$APPS/open-webui.sh"
[[ "$SELECTED_APPS" == *"claude"*         ]] && run_module "Claude Code"       "$APPS/claude.sh"
[[ "$SELECTED_APPS" == *"networking-cli"* ]] && run_module "Networking CLI"    "$APPS/networking-cli.sh"
[[ "$SELECTED_APPS" == *"disk-recovery"*  ]] && run_module "Disk Recovery"     "$APPS/disk-recovery.sh"
[[ "$SELECTED_APPS" == *"himalaya"*       ]] && run_module "Himalaya"          "$APPS/himalaya.sh"
[[ "$SELECTED_APPS" == *"gnuplot"*        ]] && run_module "Gnuplot"           "$APPS/gnuplot.sh"
[[ "$SELECTED_APPS" == *"povray"*         ]] && run_module "POV-Ray"           "$APPS/povray.sh"
[[ "$SELECTED_APPS" == *"blender"*        ]] && run_module "Blender"           "$APPS/blender.sh"
[[ "$SELECTED_APPS" == *"toot"*           ]] && run_module "toot"              "$APPS/toot.sh"
[[ "$SELECTED_APPS" == *"db-clients"*     ]] && run_module "DB Clients"        "$APPS/db-clients.sh"
[[ "$SELECTED_APPS" == *"mysql"*          ]] && run_module "MySQL / MariaDB"   "$APPS/mysql.sh"
[[ "$SELECTED_APPS" == *"productivity"*   ]] && run_module "Productivity"      "$APPS/productivity.sh"
[[ "$SELECTED_APPS" == *"yt-dlp"*         ]] && run_module "yt-dlp"           "$APPS/yt-dlp.sh"
[[ "$SELECTED_APPS" == *"sox"*            ]] && run_module "SoX"              "$APPS/sox.sh"
[[ "$SELECTED_APPS" == *"imagemagick"*    ]] && run_module "ImageMagick"       "$APPS/imagemagick.sh"
[[ "$SELECTED_APPS" == *"ffmpeg"*         ]] && run_module "FFmpeg extras"     "$APPS/ffmpeg.sh"
[[ "$SELECTED_APPS" == *"localtunnel"*    ]] && run_module "LocalTunnel"       "$APPS/localtunnel.sh"
[[ "$SELECTED_APPS" == *"butter"*         ]] && run_module "butter"            "$APPS/butter.sh"
[[ "$SELECTED_APPS" == *"tlp"*            ]] && run_module "TLP"              "$APPS/tlp.sh"
[[ "$SELECTED_APPS" == *"steam"*          ]] && run_module "Steam"             "$APPS/steam.sh"
[[ "$SELECTED_APPS" == *"vesktop"*        ]] && run_module "Vesktop"           "$APPS/vesktop.sh"
[[ "$SELECTED_APPS" == *"spotify"*        ]] && run_module "Spotify"           "$APPS/spotify.sh"
[[ "$SELECTED_APPS" == *"prism"*          ]] && run_module "PrismLauncher"     "$APPS/prismlauncher.sh"
[[ "$SELECTED_APPS" == *"vintagestory"*   ]] && run_module "Vintage Story"     "$APPS/vintagestory.sh"
[[ "$SELECTED_APPS" == *"localsend"*      ]] && run_module "LocalSend"         "$APPS/localsend.sh"
[[ "$SELECTED_APPS" == *"croc"*           ]] && run_module "croc"              "$APPS/croc.sh"
[[ "$SELECTED_APPS" == *"onlyoffice"*     ]] && run_module "OnlyOffice"        "$APPS/onlyoffice.sh"
[[ "$SELECTED_APPS" == *"wireshark"*      ]] && run_module "Wireshark"         "$APPS/wireshark.sh"
[[ "$SELECTED_APPS" == *"k8s"*            ]] && run_module "Kubernetes Tools"  "$APPS/k8s.sh"
[[ "$SELECTED_APPS" == *"docker"*         ]] && run_module "Docker"            "$APPS/docker.sh"
[[ "$SELECTED_APPS" == *"podman"*         ]] && run_module "Podman"            "$APPS/podman.sh"
[[ "$SELECTED_APPS" == *"cockpit"*        ]] && run_module "Cockpit"           "$APPS/cockpit.sh"
[[ "$SELECTED_APPS" == *"ssh-server"*     ]] && run_module "SSH Server"        "$APPS/ssh-server.sh"
[[ "$SELECTED_APPS" == *"freeipa-client"* ]] && run_module "FreeIPA Client"    "$APPS/freeipa-client.sh"
[[ "$SELECTED_APPS" == *"freeipa-server"* ]] && run_module "FreeIPA Server"    "$APPS/freeipa-server.sh"
[[ "$SELECTED_APPS" == *"freeipa-image"*  ]] && run_module "FreeIPA Image"     "$APPS/freeipa-image-builder.sh"
[[ "$SELECTED_APPS" == *"python"*         ]] && run_module "Python Tools"      "$MODULES/optional-Modules/python.sh"
[[ "$SELECTED_APPS" == *"zfs"*            ]] && run_module "ZFS"              "$MODULES/optional-Modules/zfs.sh"
[[ "$SELECTED_APPS" == *"wprs"*           ]] && run_module "WPRS"             "$MODULES/optional-Modules/wprs.sh"
[[ "$SELECTED_APPS" == *"chromium"*       ]] && run_module "Chromium"         "$APPS/chromium.sh"
[[ "$SELECTED_APPS" == *"firefox-browser"* ]] && run_module "Firefox"         "$APPS/firefox.sh"
[[ "$SELECTED_APPS" == *"zen-browser"*    ]] && run_module "Zen Browser"      "$APPS/zen-browser.sh"
[[ "$SELECTED_APPS" == *"nyxt"*           ]] && run_module "Nyxt"             "$APPS/nyxt.sh"
[[ "$SELECTED_APPS" == *"librewolf"*      ]] && run_module "LibreWolf"        "$APPS/librewolf.sh"
[[ "$SELECTED_APPS" == *"min-browser"*    ]] && run_module "Min Browser"      "$APPS/min-browser.sh"
[[ "$SELECTED_APPS" == *"vscodium"*       ]] && run_module "VSCodium"         "$APPS/vscodium.sh"
[[ "$SELECTED_APPS" == *"zed-ide"*        ]] && run_module "Zed IDE"          "$APPS/zed.sh"
[[ "$SELECTED_APPS" == *"geany"*          ]] && run_module "Geany"            "$APPS/geany.sh"
[[ "$SELECTED_APPS" == *"codeblocks"*     ]] && run_module "Code::Blocks"     "$APPS/codeblocks.sh"
[[ "$SELECTED_APPS" == *"kate"*           ]] && run_module "Kate"             "$APPS/kate.sh"
[[ "$SELECTED_APPS" == *"rdp-client"*       ]] && run_module "RDP Client"        "$APPS/rdp-client.sh"
[[ "$SELECTED_APPS" == *"lamco-rdp-server"* ]] && run_module "Lamco RDP Server" "$APPS/lamco-rdp-server.sh"
[[ "$SELECTED_APPS" == *"qemu"*             ]] && run_module "QEMU/KVM"          "$APPS/qemu.sh"

# ── Colorway (final step) ─────────────────────────────────────────────────────
# Read defaults from repo colors.conf for pre-population
declare -A _cdef
if [[ -f "$DOTFILES_DIR/colors.conf" ]]; then
    while IFS='=' read -r k v; do
        k="${k%%[[:space:]]*}"
        [[ "$k" =~ ^[[:space:]]*# || -z "$k" ]] && continue
        v="${v%%#*}"; v="${v//[[:space:]]/}"; v="${v^^}"
        _cdef[$k]="$v"
    done < "$DOTFILES_DIR/colors.conf"
fi
DEF_TEXT="${_cdef[COLOR_TEXT]:-D6ABAB}"
DEF_BG="${_cdef[COLOR_BG]:-1A1A1A}"
DEF_HIGHLIGHT="${_cdef[COLOR_HIGHLIGHT]:-E40046}"
DEF_DARK="${_cdef[COLOR_DARK]:-5018DD}"
DEF_RED="${_cdef[COLOR_RED]:-F50505}"

_write_colors_conf() {
    local out="$1" t="$2" b="$3" h="$4" d="$5" r="$6"
    printf 'COLOR_TEXT=%s\nCOLOR_BG=%s\nCOLOR_HIGHLIGHT=%s\nCOLOR_DARK=%s\nCOLOR_RED=%s\n' \
        "${t^^}" "${b^^}" "${h^^}" "${d^^}" "${r^^}" > "$out"
}

if $ANSWERFILE_MODE; then
    # Apply colors from answerfile if any are set
    if [[ -n "$AF_COLOR_TEXT$AF_COLOR_BG$AF_COLOR_HIGHLIGHT$AF_COLOR_DARK$AF_COLOR_RED" ]]; then
        TMP_COLORS="$TMP_D/colors.conf"
        _write_colors_conf "$TMP_COLORS" \
            "${AF_COLOR_TEXT:-$DEF_TEXT}" \
            "${AF_COLOR_BG:-$DEF_BG}" \
            "${AF_COLOR_HIGHLIGHT:-$DEF_HIGHLIGHT}" \
            "${AF_COLOR_DARK:-$DEF_DARK}" \
            "${AF_COLOR_RED:-$DEF_RED}"
        printf "Applying colorway from answerfile...\n" | tee -a "$LOG"
        bash "$DOTFILES_DIR/apply-theme.sh" "$TMP_COLORS" 2>&1 | tee -a "$LOG" || true
    fi
else
    # Interactive: show color form dialog
    COLORWAY_RAW=$(dialog --backtitle "$BACKTITLE" \
        --title " Colorway (optional) " \
        --form "\n  Customize theme colors — bare 6-digit hex, no #.\n  Leave unchanged to skip colorway setup.\n" \
        16 62 5 \
        "COLOR_TEXT      " 1 1 "$DEF_TEXT"      1 18 10 7 \
        "COLOR_BG        " 2 1 "$DEF_BG"        2 18 10 7 \
        "COLOR_HIGHLIGHT " 3 1 "$DEF_HIGHLIGHT"  3 18 10 7 \
        "COLOR_DARK      " 4 1 "$DEF_DARK"       4 18 10 7 \
        "COLOR_RED       " 5 1 "$DEF_RED"        5 18 10 7 \
        3>&1 1>&2 2>&3) || COLORWAY_RAW=""

    if [[ -n "$COLORWAY_RAW" ]]; then
        mapfile -t _cv <<< "$COLORWAY_RAW"
        N_TEXT="${_cv[0]:-$DEF_TEXT}"
        N_BG="${_cv[1]:-$DEF_BG}"
        N_HIGHLIGHT="${_cv[2]:-$DEF_HIGHLIGHT}"
        N_DARK="${_cv[3]:-$DEF_DARK}"
        N_RED="${_cv[4]:-$DEF_RED}"

        if [[ "${N_TEXT^^}"      != "$DEF_TEXT"      || \
              "${N_BG^^}"        != "$DEF_BG"         || \
              "${N_HIGHLIGHT^^}"  != "$DEF_HIGHLIGHT"  || \
              "${N_DARK^^}"      != "$DEF_DARK"        || \
              "${N_RED^^}"       != "$DEF_RED" ]]; then
            TMP_COLORS="$TMP_D/colors.conf"
            _write_colors_conf "$TMP_COLORS" "$N_TEXT" "$N_BG" "$N_HIGHLIGHT" "$N_DARK" "$N_RED"
            clear
            printf "\n\033[1;35m  Applying colorway...\033[0m\n\n"
            bash "$DOTFILES_DIR/apply-theme.sh" "$TMP_COLORS" 2>&1 | tee -a "$LOG" || true
        fi
    fi
fi

# ── Done ──────────────────────────────────────────────────────────────────────
if $ANSWERFILE_MODE; then
    printf "\nDone. Log: %s\n" "$LOG"
else
    dialog --backtitle "$BACKTITLE" \
        --title " Done " \
        --msgbox "\n  All selected components installed.\n\n  Log: $LOG\n\n  A reboot may be required for all changes to take effect.\n" 12 58

    clear
    printf "\n  Done. Log: %s\n\n" "$LOG"
fi
