#!/bin/bash
# generate-answerfile.sh — dry-run the M-Archy installer dialogs and write
# all selections to an answerfile.json. Nothing is installed.
#
# Usage:
#   bash generate-answerfile.sh [OUTPUT_PATH]
#   OUTPUT_PATH defaults to ~/answerfile.json
#
# The generated file can be placed at /answerfile.json on the installer USB
# (use  build.sh --preconf  to embed it automatically) so that the full
# install — base OS + dotfiles — runs without any manual input.

set -uo pipefail

SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
# This script lives in setup/tools/, so the repo root is two levels up.
DOTFILES_DIR="$(cd "$SCRIPT_DIR/../.." && pwd)"
OUTPUT="${1:-$HOME/answerfile.json}"
TMP_D="$(mktemp -d)"
trap 'rm -rf "$TMP_D"' EXIT

BACKTITLE="M-Archy Answerfile Generator"

# ── 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

# ── 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; }

# json_str: emit a properly-quoted JSON string value (handles empty → null)
json_str() {
    local v="$1"
    if [[ -z "$v" ]]; then printf 'null'; else printf '"%s"' "$v"; fi
}

# json_bool: emit true/false from YES/NO or true/false input
json_bool() {
    local v="${1,,}"
    [[ "$v" == "yes" || "$v" == "true" ]] && echo "true" || echo "false"
}

# ── Preflight ─────────────────────────────────────────────────────────────────
require_dialog
require_jq

# ── Welcome ───────────────────────────────────────────────────────────────────
dialog --backtitle "$BACKTITLE" \
    --title " Answerfile Generator " \
    --msgbox "\n\
  This tool walks you through all installer dialogs\n\
  and saves your choices to an answerfile.\n\
  ─────────────────────────────────────────────────\n\
\n\
  NO software will be installed — this is a dry run.\n\
\n\
  Output: $OUTPUT\n\
\n\
  To use on a USB: build.sh --preconf $OUTPUT\n" 16 62

# ═══════════════════════════════════════════════════════════════
# PART 1 — Base OS install options
# ═══════════════════════════════════════════════════════════════

# ── Drive ─────────────────────────────────────────────────────────────────────
# The available-drives list is rendered inline in the inputbox prompt below via
# lsblk, so no separate variable is needed here.
AF_DRIVE=$(dialog --backtitle "$BACKTITLE" \
    --title " Target Drive " \
    --inputbox "\n  Enter the install drive (e.g. /dev/sda, /dev/nvme0n1).\n\n  Available drives:\n$(lsblk -dn -o NAME,SIZE,MODEL 2>/dev/null | sed 's/^/    /')\n" \
    16 64 "/dev/sda" \
    3>&1 1>&2 2>&3) || AF_DRIVE=""

# ── Kernel ────────────────────────────────────────────────────────────────────
AF_KERNEL=$(dialog --backtitle "$BACKTITLE" \
    --title " Kernel " \
    --menu "Select kernel package:" 13 54 4 \
    "linux"          "Stable kernel" \
    "linux-lts"      "Long-term support kernel" \
    "linux-zen"      "Zen performance kernel" \
    "linux-hardened" "Security-hardened kernel" \
    3>&1 1>&2 2>&3) || AF_KERNEL="linux"

# ── Keymap ────────────────────────────────────────────────────────────────────
AF_KEYMAP=$(dialog --backtitle "$BACKTITLE" \
    --title " Keyboard Layout " \
    --menu "Select keyboard layout (live environment + installed system):" 10 60 2 \
    "us" "English US" \
    "de" "German" \
    3>&1 1>&2 2>&3) || AF_KEYMAP="us"

# ── Hostname ──────────────────────────────────────────────────────────────────
AF_HOSTNAME=$(dialog --backtitle "$BACKTITLE" \
    --title " Hostname " \
    --inputbox "\n  Hostname for the new system.\n\n  Leave blank to keep default.\n\n  Note: a MAC address suffix will be appended\n  automatically when the answerfile is applied,\n  preventing hostname conflicts across machines.\n" \
    14 62 "" \
    3>&1 1>&2 2>&3) || AF_HOSTNAME=""

# ── Username ──────────────────────────────────────────────────────────────────
AF_USERNAME=$(dialog --backtitle "$BACKTITLE" \
    --title " Username " \
    --inputbox "\n  Name for the primary user account.\n" \
    9 54 "" \
    3>&1 1>&2 2>&3) || AF_USERNAME=""

# ── User password (optional) ──────────────────────────────────────────────────
# Storing the password makes the install fully unattended, but it is saved as
# plain text in the answerfile (and world-readable once embedded in an ISO). If
# the user declines, the field is left empty and the installer prompts at runtime.
AF_PASSWORD=""
# ask_pw <username> → echoes a confirmed password, or empty if cancelled/declined.
ask_pw() {
    local who="$1" p1 p2
    while true; do
        p1=$(dialog --backtitle "$BACKTITLE" --title " Password " --insecure \
            --passwordbox "\n  Enter password for ${who}\n  (leave blank to be prompted at install time):" 11 60 \
            3>&1 1>&2 2>&3) || return 1
        [[ -z "$p1" ]] && return 1   # blank = decline, prompt at install
        p2=$(dialog --backtitle "$BACKTITLE" --title " Confirm Password " --insecure \
            --passwordbox "\n  Re-enter the password to confirm:" 10 60 \
            3>&1 1>&2 2>&3) || return 1
        if [[ "$p1" == "$p2" ]]; then printf '%s' "$p1"; return 0; fi
        dialog --backtitle "$BACKTITLE" --title " Mismatch " \
            --msgbox "\n  Passwords did not match. Try again.\n" 8 50
    done
}
AF_PASSWORD=$(ask_pw "the user account") || AF_PASSWORD=""

# ── Disk encryption ───────────────────────────────────────────────────────────
dialog --backtitle "$BACKTITLE" \
    --title " Disk Encryption " \
    --yesno "\n  Enable LUKS2 disk encryption on the root partition?\n\n  If yes, a backup LUKS key will be auto-generated\n  and placed at /_LUKS_BACKUP_KEY in the new system.\n" \
    11 62 && AF_ENCRYPT="true" || AF_ENCRYPT="false"

AF_FIDO2_ROOT="false"
AF_FIDO2_USER="false"
AF_LUKS_PASSWORD=""
if [[ "$AF_ENCRYPT" == "true" ]]; then
    # Optional LUKS passphrase so an encrypted install can also run unattended.
    AF_LUKS_PASSWORD=$(ask_pw "LUKS disk encryption") || AF_LUKS_PASSWORD=""
    dialog --backtitle "$BACKTITLE" \
        --title " FIDO2 Root Unlock " \
        --yesno "\n  Enable FIDO2 hardware key for LUKS root unlock?\n" \
        8 56 && AF_FIDO2_ROOT="true" || AF_FIDO2_ROOT="false"
fi

dialog --backtitle "$BACKTITLE" \
    --title " FIDO2 User Login " \
    --yesno "\n  Enable FIDO2 hardware key for user login (PAM)?\n" \
    8 56 && AF_FIDO2_USER="true" || AF_FIDO2_USER="false"

# ── Run TUI installer after base install ──────────────────────────────────────
dialog --backtitle "$BACKTITLE" \
    --title " Dotfiles Setup " \
    --yesno "\n  Automatically run the dotfiles TUI installer\n  inside the chroot after base install completes?\n" \
    9 58 && AF_RUN_TUI="true" || AF_RUN_TUI="false"

# ═══════════════════════════════════════════════════════════════
# PART 2 — Dotfiles / TUI installer options
# (only shown if run_tui is true)
# ═══════════════════════════════════════════════════════════════
AF_COMPONENTS=""
AF_DE="none"
AF_APPS=""
AF_COLOR_TEXT=""
AF_COLOR_BG=""
AF_COLOR_HIGHLIGHT=""
AF_COLOR_DARK=""
AF_COLOR_RED=""

if [[ "$AF_RUN_TUI" == "true" ]]; then

    # ── Components ────────────────────────────────────────────────────────────
    AF_COMPONENTS=$(dialog --backtitle "$BACKTITLE" \
        --title " Select Components " \
        --checklist "Space toggles  ·  Enter confirms  ·  Esc quits" 16 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) || AF_COMPONENTS=""

    # ── DE ────────────────────────────────────────────────────────────────────
    AF_DE=$(dialog --backtitle "$BACKTITLE" \
        --title " Desktop Environment " \
        --menu "Select a desktop environment  ·  Esc / none to skip:" 24 72 11 \
        "hyprlua"    "HyprLua    — Hyprland with Lua config (recommended)" \
        "niri"       "Niri       — scrollable-tiling Wayland compositor" \
        "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) || AF_DE="none"

    # ── Apps ──────────────────────────────────────────────────────────────────
# BEGIN GENERATED MODULES: module-checklist
    AF_APPS=$(dialog --backtitle "$BACKTITLE" \
        --title " Applications " \
        --checklist "Optional applications — installed after base components:" 40 76 83 \
        "ollama"  "ollama                local LLM runner and API server"  off \
        "llama-cpp"  "llama-cpp             standalone LLM inference CLI and server"  off \
        "open-webui"  "open-webui            browser UI for Ollama and LLM backends"  off \
        "claude"  "claude                Anthropic Claude Code CLI via npm"  off \
        "networking-cli"  "networking-cli        nmap, nethogs, mitmproxy, httpie"  off \
        "disk-recovery"  "disk-recovery         ddrescue and f3 disk recovery tools"  off \
        "himalaya"  "himalaya              terminal email client (AUR)"  off \
        "mail-notmuch"  "mail-notmuch          isync, msmtp, notmuch, alot mail stack"  off \
        "caldav-sync"  "caldav-sync           vdirsyncer and khal CalDAV calendar sync"  off \
        "ssh-server"  "ssh-server            openssh with key-auth and systemd unit enabled"  off \
        "wireshark"  "wireshark             network packet analyser GUI"  off \
        "anti-malware"  "anti-malware          ClamAV, rkhunter, chkrootkit"  off \
        "gnuplot"  "gnuplot               scientific plotting tool"  off \
        "blender-povray"  "blender-povray        3D modelling and ray-tracing (Blender + POV-Ray)"  off \
        "toot"  "toot                  Mastodon CLI client (AUR)"  off \
        "db-clients"  "db-clients            pgcli and mycli interactive database CLIs"  off \
        "mysql"  "mysql                 MariaDB server with initial setup"  off \
        "productivity"  "productivity          taskwarrior, watson, jrnl — task management and time tracking"  off \
        "python"  "python                pyright, pipx, pynvim Python tooling"  on \
        "k8s"  "k8s                   kubectl and podman-desktop Kubernetes tools"  off \
        "docker"  "docker                docker and docker-compose"  off \
        "podman"  "podman                rootless containers with buildah"  off \
        "cockpit"  "cockpit               web UI for machines and containers"  off \
        "tlp"  "tlp                   laptop battery optimisation"  off \
        "butter"  "butter                btrfs snapshot backup manager (AUR)"  off \
        "localsend"  "localsend             LAN file transfer, AirDrop-like (AUR)"  off \
        "croc"  "croc                  cross-platform encrypted file transfer"  off \
        "opendeck"  "opendeck              Stream Deck controller — ydotool + OpenDeck (Flatpak)"  off \
        "localtunnel"  "localtunnel           expose localhost over a public URL"  off \
        "timeshift"  "timeshift             system snapshot and backup with autosnap"  on \
        "zfs"  "zfs                   zfs-dkms kernel module"  off \
        "wprs"  "wprs                  Wayland proxy for remote sessions (wprs-git, AUR)"  off \
        "plymouth"  "plymouth              boot splash — bundled skull logo + boot-log tail"  on \
        "plymouth-custom"  "plymouth-custom       boot splash with a user-supplied image"  off \
        "greetd-regreet"  "greetd-regreet        greetd + ReGreet greeter, Wayland-native, cyberqueer-themed"  off \
        "lightdm"  "lightdm               LightDM GTK greeter, cyberqueer-themed (Xorg)"  off \
        "ly"  "ly                    ly TUI display manager, cyberqueer-themed"  off \
        "steam"  "steam                 Steam gaming platform"  off \
        "vesktop"  "vesktop               Discord client with Vencord theme"  off \
        "spotify"  "spotify               Spotify launcher with Spicetify theming"  off \
        "prism"  "prism                 PrismLauncher Minecraft launcher (Flatpak)"  off \
        "vintagestory"  "vintagestory          Vintage Story survival game (AUR)"  off \
        "openarena"  "openarena             open-source Quake III Arena"  off \
        "tetris"  "tetris                bastet and vitetris terminal Tetris"  off \
        "doom"  "doom                  Chocolate Doom with Freedoom data"  off \
        "sauerbraten"  "sauerbraten           Sauerbraten open-source FPS (Cube 2)"  off \
        "stuntrally"  "stuntrally            Stunt Rally racing game (Flatpak)"  off \
        "onlyoffice"  "onlyoffice            office suite — Docs, Sheets, Slides (AUR)"  on \
        "xournal"  "xournal               note-taking and PDF annotator"  off \
        "rnote"  "rnote                 handwriting and note-taking with stylus support (Flatpak)"  off \
        "obsidian"  "obsidian              knowledge base and Markdown note-taking (Flatpak)"  off \
        "tangent-notes"  "tangent-notes         networked Markdown note-taking (Flatpak)"  off \
        "ffmpeg"  "ffmpeg                GStreamer codecs and ffmpegthumbnailer"  on \
        "sox"  "sox                   command-line audio processing toolkit"  on \
        "imagemagick"  "imagemagick           image manipulation suite"  on \
        "yt-dlp"  "yt-dlp                YouTube and media downloader"  on \
        "gimp"  "gimp                  GNU Image Manipulation Program"  off \
        "inkscape"  "inkscape              vector graphics editor"  on \
        "krita"  "krita                 digital painting and illustration"  off \
        "kdenlive"  "kdenlive              KDE non-linear video editor"  off \
        "openshot"  "openshot              cross-platform video editor"  off \
        "shotcut"  "shotcut               cross-platform video editor"  off \
        "ardour"  "ardour                professional DAW"  off \
        "audacity"  "audacity              multi-track audio editor"  off \
        "lmms"  "lmms                  Linux MultiMedia Studio music production"  off \
        "mixxx"  "mixxx                 DJ mixing software"  off \
        "cecilia"  "cecilia               audio synthesis and signal processing (AUR)"  off \
        "chromium"  "chromium              open-source Chromium browser (official)"  off \
        "firefox-browser"  "firefox-browser       Mozilla Firefox (official)"  on \
        "zen-browser"  "zen-browser           privacy-focused Firefox fork (AUR)"  off \
        "nyxt"  "nyxt                  keyboard-driven hackable browser (AUR)"  off \
        "librewolf"  "librewolf             hardened Firefox fork (AUR)"  off \
        "min-browser"  "min-browser           minimal Electron browser (AUR)"  off \
        "vscodium"  "vscodium              telemetry-free VS Code build (AUR)"  off \
        "zed-ide"  "zed-ide               high-performance Rust IDE (official)"  off \
        "geany"  "geany                 lightweight IDE with plugins (official)"  off \
        "codeblocks"  "codeblocks            C/C++ IDE (official)"  off \
        "kate"  "kate                  KDE advanced text editor (official)"  off \
        "rdp-client"  "rdp-client            Remmina with FreeRDP and VNC plugins"  off \
        "lamco-rdp-server"  "lamco-rdp-server      native Wayland RDP server (AUR, Rust)"  off \
        "qemu"  "qemu                  full QEMU/KVM stack with virt-manager"  off \
        "freeipa-client"  "freeipa-client        sssd and ipa-client-install with auto-enrollment"  off \
        "freeipa-server"  "freeipa-server        interactive FreeIPA server setup with client generator"  off \
        3>&1 1>&2 2>&3) || AF_APPS=""
# END GENERATED MODULES: module-checklist

    # ── Colorway ──────────────────────────────────────────────────────────────
    # Read defaults from repo colors.conf
    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}"

    COLORWAY_RAW=$(dialog --backtitle "$BACKTITLE" \
        --title " Colorway (optional) " \
        --form "\n  Customize theme colors — bare 6-digit hex, no #.\n  Leave unchanged to omit colors from answerfile.\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}"
        # Only save colors if any differ from defaults
        if [[ "${N_TEXT^^}"      != "$DEF_TEXT"      || \
              "${N_BG^^}"        != "$DEF_BG"         || \
              "${N_HIGHLIGHT^^}"  != "$DEF_HIGHLIGHT"  || \
              "${N_DARK^^}"      != "$DEF_DARK"        || \
              "${N_RED^^}"       != "$DEF_RED" ]]; then
            AF_COLOR_TEXT="${N_TEXT^^}"
            AF_COLOR_BG="${N_BG^^}"
            AF_COLOR_HIGHLIGHT="${N_HIGHLIGHT^^}"
            AF_COLOR_DARK="${N_DARK^^}"
            AF_COLOR_RED="${N_RED^^}"
        fi
    fi
fi

# ── Confirmation ──────────────────────────────────────────────────────────────
SUMMARY=""
[[ -n "$AF_DRIVE"    ]] && SUMMARY+="  Drive:      $AF_DRIVE\n"
[[ -n "$AF_KERNEL"   ]] && SUMMARY+="  Kernel:     $AF_KERNEL\n"
[[ -n "$AF_KEYMAP"   ]] && SUMMARY+="  Keymap:     $AF_KEYMAP\n"
[[ -n "$AF_HOSTNAME" ]] && SUMMARY+="  Hostname:   $AF_HOSTNAME (+ MAC suffix at deploy)\n"
[[ -n "$AF_USERNAME" ]] && SUMMARY+="  Username:   $AF_USERNAME\n"
SUMMARY+="  Encrypt:    $AF_ENCRYPT\n"
SUMMARY+="  FIDO2 root: $AF_FIDO2_ROOT  /  FIDO2 user: $AF_FIDO2_USER\n"
SUMMARY+="  Run TUI:    $AF_RUN_TUI\n"
[[ -n "$AF_DE" && "$AF_DE" != "none" ]] && SUMMARY+="  DE:         $AF_DE\n"
[[ -n "$AF_COLOR_TEXT" ]] && SUMMARY+="  Colors:     custom\n"

dialog --backtitle "$BACKTITLE" \
    --title " Confirm " \
    --yesno "\n  Save answerfile with these settings:\n\n${SUMMARY}\n  Output: $OUTPUT\n\n  Proceed?" \
    22 66 || { clear; echo "Aborted."; exit 0; }

# ── Build JSON arrays from space-separated dialog output ──────────────────────
_words_to_json_array() {
    local input="$1"
    local first=1
    printf '['
    for w in $input; do
        [[ $first -eq 0 ]] && printf ','
        printf '"%s"' "$w"
        first=0
    done
    printf ']'
}

# ── Write answerfile ──────────────────────────────────────────────────────────
mkdir -p "$(dirname "$OUTPUT")"

{
    printf '{\n'
    printf '  "_generated": "%s",\n' "$(date -Iseconds)"
    printf '  "drive": %s,\n'        "$(json_str "$AF_DRIVE")"
    printf '  "kernel": %s,\n'       "$(json_str "$AF_KERNEL")"
    printf '  "keymap": %s,\n'       "$(json_str "$AF_KEYMAP")"
    printf '  "hostname": %s,\n'     "$(json_str "$AF_HOSTNAME")"
    printf '  "username": %s,\n'     "$(json_str "$AF_USERNAME")"
    # Passwords are emitted as JSON null when not supplied; the installer treats
    # null/absent as "prompt interactively".
    printf '  "password": %s,\n'      "$(json_str "$AF_PASSWORD")"
    printf '  "luks_password": %s,\n' "$(json_str "$AF_LUKS_PASSWORD")"
    printf '  "encrypt": %s,\n'      "$AF_ENCRYPT"
    printf '  "fido2_root": %s,\n'   "$AF_FIDO2_ROOT"
    printf '  "fido2_user": %s,\n'   "$AF_FIDO2_USER"
    printf '  "run_tui": %s,\n'      "$AF_RUN_TUI"
    printf '  "components": %s,\n'   "$(_words_to_json_array "$AF_COMPONENTS")"
    printf '  "desktop_environment": %s,\n' "$(json_str "$AF_DE")"
    printf '  "apps": %s'             "$(_words_to_json_array "$AF_APPS")"

    if [[ -n "$AF_COLOR_TEXT" ]]; then
        printf ',\n  "colors": {\n'
        printf '    "COLOR_TEXT": "%s",\n'      "$AF_COLOR_TEXT"
        printf '    "COLOR_BG": "%s",\n'        "$AF_COLOR_BG"
        printf '    "COLOR_HIGHLIGHT": "%s",\n' "$AF_COLOR_HIGHLIGHT"
        printf '    "COLOR_DARK": "%s",\n'      "$AF_COLOR_DARK"
        printf '    "COLOR_RED": "%s"\n'        "$AF_COLOR_RED"
        printf '  }'
    fi

    printf '\n}\n'
} > "$OUTPUT"

clear
printf "\n  Answerfile saved to: %s\n\n" "$OUTPUT"
printf "  To embed in ISO:  bash setup/tools/build.sh --preconf %s\n\n" "$OUTPUT"
