#!/usr/bin/env bash
# Copyright (c) 2021-2026 community-scripts ORG
# Author: tteck (tteckster) | MickLesk | michelroegl-brunner
# License: MIT | https://github.com/community-scripts/ProxmoxVED/raw/branch/main/LICENSE
# Revision: 1

# ==============================================================================
# BUILD.FUNC - LXC CONTAINER BUILD & CONFIGURATION
# ==============================================================================
#
# This file provides the main build functions for creating and configuring
# LXC containers in Proxmox VE. It handles:
#
#   - Variable initialization and defaults
#   - Container creation and resource allocation
#   - Storage selection and management
#   - Advanced configuration and customization
#   - User interaction menus and prompts
#
# Usage:
#   - Sourced automatically by CT creation scripts
#   - Requires core.func and error_handler.func to be loaded first
#
# ==============================================================================

# ==============================================================================
# SECTION 1: INITIALIZATION & CORE VARIABLES
# ==============================================================================

# ------------------------------------------------------------------------------
# variables()
#
# - Initializes core variables for container creation
# - Normalizes application name (NSAPP = lowercase, no spaces)
# - Builds installer filename (var_install)
# - Defines regex patterns for validation
# - Fetches Proxmox hostname and version
# - Generates unique session ID for tracking and logging
# - Captures app-declared resource defaults (CPU, RAM, Disk)
# ------------------------------------------------------------------------------
variables() {
    NSAPP=$(echo "${APP,,}" | tr -d ' ')              # This function sets the NSAPP variable by converting the value of the APP variable to lowercase and removing any spaces.
    var_install="${NSAPP}-install"                    # sets the var_install variable by appending "-install" to the value of NSAPP.
    INTEGER='^[0-9]+([.][0-9]+)?$'                    # it defines the INTEGER regular expression pattern.
    PVEHOST_NAME=$(hostname)                          # gets the Proxmox Hostname and sets it to Uppercase
    DIAGNOSTICS="yes"                                 # sets the DIAGNOSTICS variable to "yes", used for the API call.
    METHOD="default"                                  # sets the METHOD variable to "default", used for the API call.
    RANDOM_UUID="$(cat /proc/sys/kernel/random/uuid)" # generates a random UUID and sets it to the RANDOM_UUID variable.
    SESSION_ID="${RANDOM_UUID:0:8}"                   # Short session ID (first 8 chars of UUID) for log files
    BUILD_LOG="/tmp/create-lxc-${SESSION_ID}.log"     # Host-side container creation log
    CTTYPE="${CTTYPE:-${CT_TYPE:-1}}"

    # Parse dev_mode early
    parse_dev_mode

    # Setup persistent log directory if logs mode active
    if [[ "${DEV_MODE_LOGS:-false}" == "true" ]]; then
        mkdir -p /var/log/community-scripts
        BUILD_LOG="/var/log/community-scripts/create-lxc-${SESSION_ID}-$(date +%Y%m%d_%H%M%S).log"
    fi

    # Get Proxmox VE version and kernel version
    if command -v pveversion >/dev/null 2>&1; then
        PVEVERSION="$(pveversion | awk -F'/' '{print $2}' | awk -F'-' '{print $1}')"
    else
        PVEVERSION="N/A"
    fi
    KERNEL_VERSION=$(uname -r)

    # Capture app-declared defaults (for precedence logic)
    # These values are set by the app script BEFORE default.vars is loaded
    # If app declares higher values than default.vars, app values take precedence
    if [[ -n "${var_cpu:-}" && "${var_cpu}" =~ ^[0-9]+$ ]]; then
        export APP_DEFAULT_CPU="${var_cpu}"
    fi
    if [[ -n "${var_ram:-}" && "${var_ram}" =~ ^[0-9]+$ ]]; then
        export APP_DEFAULT_RAM="${var_ram}"
    fi
    if [[ -n "${var_disk:-}" && "${var_disk}" =~ ^[0-9]+$ ]]; then
        export APP_DEFAULT_DISK="${var_disk}"
    fi
}

# -----------------------------------------------------------------------------
# Community-Scripts bootstrap loader
# - Always sources build.func from remote
# - Updates local core files only if build.func changed
# - Local cache: /usr/local/community-scripts/core
# -----------------------------------------------------------------------------

# FUNC_DIR="/usr/local/community-scripts/core"
# mkdir -p "$FUNC_DIR"

# BUILD_URL="https://raw.githubusercontent.com/community-scripts/ProxmoxVED/main/misc/build.func"
# BUILD_REV="$FUNC_DIR/build.rev"
# DEVMODE="${DEVMODE:-no}"

# # --- Step 1: fetch build.func content once, compute hash ---
# build_content="$(curl -fsSL "$BUILD_URL")" || {
#   echo "❌ Failed to fetch build.func"
#   exit 1
# }

# newhash=$(printf "%s" "$build_content" | sha256sum | awk '{print $1}')
# oldhash=$(cat "$BUILD_REV" 2>/dev/null || echo "")

# # --- Step 2: if build.func changed, offer update for core files ---
# if [ "$newhash" != "$oldhash" ]; then
#   echo "⚠️  build.func changed!"

#   while true; do
#     read -rp "Refresh local core files? [y/N/diff]: " ans
#     case "$ans" in
#     [Yy]*)
#       echo "$newhash" >"$BUILD_REV"

#       update_func_file() {
#         local file="$1"
#         local url="https://raw.githubusercontent.com/community-scripts/ProxmoxVED/main/misc/$file"
#         local local_path="$FUNC_DIR/$file"

#         echo "⬇️  Downloading $file ..."
#         curl -fsSL "$url" -o "$local_path" || {
#           echo "❌ Failed to fetch $file"
#           exit 1
#         }
#         echo "✔️  Updated $file"
#       }

#       update_func_file core.func
#       update_func_file error_handler.func
#       update_func_file tools.func
#       break
#       ;;
#     [Dd]*)
#       for file in core.func error_handler.func tools.func; do
#         local_path="$FUNC_DIR/$file"
#         url="https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/$file"
#         remote_tmp="$(mktemp)"

#         curl -fsSL "$url" -o "$remote_tmp" || continue

#         if [ -f "$local_path" ]; then
#           echo "🔍 Diff for $file:"
#           diff -u "$local_path" "$remote_tmp" || echo "(no differences)"
#         else
#           echo "📦 New file $file will be installed"
#         fi

#         rm -f "$remote_tmp"
#       done
#       ;;
#     *)
#       echo "❌ Skipped updating local core files"
#       break
#       ;;
#     esac
#   done
# else
#   if [ "$DEVMODE" != "yes" ]; then
#     echo "✔️  build.func unchanged → using existing local core files"
#   fi
# fi

# if [ -n "${_COMMUNITY_SCRIPTS_LOADER:-}" ]; then
#   return 0 2>/dev/null || exit 0
# fi
# _COMMUNITY_SCRIPTS_LOADER=1

# # --- Step 3: always source local versions of the core files ---
# source "$FUNC_DIR/core.func"
# source "$FUNC_DIR/error_handler.func"
# source "$FUNC_DIR/tools.func"

# # --- Step 4: finally, source build.func directly from memory ---
# # (no tmp file needed)
# source <(printf "%s" "$build_content")

# ------------------------------------------------------------------------------
# Load core + error handler functions from community-scripts repo
#
# - Prefer curl if available, fallback to wget
# - Load: core.func, error_handler.func, api.func
# - Initialize error traps after loading
# ------------------------------------------------------------------------------

source <(curl -fsSL https://git.community-scripts.org/community-scripts/ProxmoxVED/raw/branch/main/misc/api.func)

if command -v curl >/dev/null 2>&1; then
    source <(curl -fsSL https://git.community-scripts.org/community-scripts/ProxmoxVED/raw/branch/main/misc/core.func)
    source <(curl -fsSL https://git.community-scripts.org/community-scripts/ProxmoxVED/raw/branch/main/misc/error_handler.func)
    load_functions
    catch_errors
    #echo "(build.func) Loaded core.func via curl"
elif command -v wget >/dev/null 2>&1; then
    source <(wget -qO- https://git.community-scripts.org/community-scripts/ProxmoxVED/raw/branch/main/misc/core.func)
    source <(wget -qO- https://git.community-scripts.org/community-scripts/ProxmoxVED/raw/branch/main/misc/error_handler.func)
    load_functions
    catch_errors
    #echo "(build.func) Loaded core.func via wget"
fi

# ==============================================================================
# SECTION 2: PRE-FLIGHT CHECKS & SYSTEM VALIDATION
# ==============================================================================

# ------------------------------------------------------------------------------
# maxkeys_check()
#
# - Reads kernel keyring limits (maxkeys, maxbytes)
# - Checks current usage for LXC user (UID 100000)
# - Warns if usage is close to limits and suggests sysctl tuning
# - Exits if thresholds are exceeded
# - https://cleveruptime.com/docs/files/proc-key-users | https://docs.kernel.org/security/keys/core.html
# ------------------------------------------------------------------------------

maxkeys_check() {
    # Read kernel parameters
    per_user_maxkeys=$(cat /proc/sys/kernel/keys/maxkeys 2>/dev/null || echo 0)
    per_user_maxbytes=$(cat /proc/sys/kernel/keys/maxbytes 2>/dev/null || echo 0)

    # Exit if kernel parameters are unavailable
    if [[ "$per_user_maxkeys" -eq 0 || "$per_user_maxbytes" -eq 0 ]]; then
        echo -e "${CROSS}${RD} Error: Unable to read kernel parameters. Ensure proper permissions.${CL}"
        exit 1
    fi

    # Fetch key usage for user ID 100000 (typical for containers)
    used_lxc_keys=$(awk '/100000:/ {print $2}' /proc/key-users 2>/dev/null || echo 0)
    used_lxc_bytes=$(awk '/100000:/ {split($5, a, "/"); print a[1]}' /proc/key-users 2>/dev/null || echo 0)

    # Calculate thresholds and suggested new limits
    threshold_keys=$((per_user_maxkeys - 100))
    threshold_bytes=$((per_user_maxbytes - 1000))
    new_limit_keys=$((per_user_maxkeys * 2))
    new_limit_bytes=$((per_user_maxbytes * 2))

    # Check if key or byte usage is near limits
    failure=0
    if [[ "$used_lxc_keys" -gt "$threshold_keys" ]]; then
        echo -e "${CROSS}${RD} Warning: Key usage is near the limit (${used_lxc_keys}/${per_user_maxkeys}).${CL}"
        echo -e "${INFO} Suggested action: Set ${GN}kernel.keys.maxkeys=${new_limit_keys}${CL} in ${BOLD}/etc/sysctl.d/98-community-scripts.conf${CL}."
        failure=1
    fi
    if [[ "$used_lxc_bytes" -gt "$threshold_bytes" ]]; then
        echo -e "${CROSS}${RD} Warning: Key byte usage is near the limit (${used_lxc_bytes}/${per_user_maxbytes}).${CL}"
        echo -e "${INFO} Suggested action: Set ${GN}kernel.keys.maxbytes=${new_limit_bytes}${CL} in ${BOLD}/etc/sysctl.d/98-community-scripts.conf${CL}."
        failure=1
    fi

    # Provide next steps if issues are detected
    if [[ "$failure" -eq 1 ]]; then
        echo -e "${INFO} To apply changes, run: ${BOLD}service procps force-reload${CL}"
        exit 1
    fi

    # Silent success - only show errors if they exist
}

# ==============================================================================
# SECTION 3: CONTAINER SETUP UTILITIES
# ==============================================================================

# ------------------------------------------------------------------------------
# get_current_ip()
#
# - Returns current container IP depending on OS type
# - Debian/Ubuntu: uses `hostname -I`
# - Alpine: parses eth0 via `ip -4 addr`
# - Returns "Unknown" if OS type cannot be determined
# ------------------------------------------------------------------------------
get_current_ip() {
    if [ -f /etc/os-release ]; then
        # Check for Debian/Ubuntu (uses hostname -I)
        if grep -qE 'ID=debian|ID=ubuntu' /etc/os-release; then
            CURRENT_IP=$(hostname -I | awk '{print $1}')
        # Check for Alpine (uses ip command)
        elif grep -q 'ID=alpine' /etc/os-release; then
            CURRENT_IP=$(ip -4 addr show eth0 | awk '/inet / {print $2}' | cut -d/ -f1 | head -n 1)
        else
            CURRENT_IP="Unknown"
        fi
    fi
    echo "$CURRENT_IP"
}

# ------------------------------------------------------------------------------
# update_motd_ip()
#
# - Updates /etc/motd with current container IP
# - Removes old IP entries to avoid duplicates
# ------------------------------------------------------------------------------
update_motd_ip() {
    MOTD_FILE="/etc/motd"

    if [ -f "$MOTD_FILE" ]; then
        # Remove existing IP Address lines to prevent duplication
        sed -i '/IP Address:/d' "$MOTD_FILE"

        IP=$(get_current_ip)
        # Add the new IP address
        echo -e "${TAB}${NETWORK}${YW} IP Address: ${GN}${IP}${CL}" >>"$MOTD_FILE"
    fi
}

# ------------------------------------------------------------------------------
# install_ssh_keys_into_ct()
#
# - Installs SSH keys into container root account if SSH is enabled
# - Uses pct push or direct input to authorized_keys
# - Falls back to warning if no keys provided
# ------------------------------------------------------------------------------
install_ssh_keys_into_ct() {
    [[ "$SSH" != "yes" ]] && return 0

    if [[ -n "$SSH_KEYS_FILE" && -s "$SSH_KEYS_FILE" ]]; then
        msg_info "Installing selected SSH keys into CT ${CTID}"
        pct exec "$CTID" -- sh -c 'mkdir -p /root/.ssh && chmod 700 /root/.ssh' || {
            msg_error "prepare /root/.ssh failed"
            return 1
        }
        pct push "$CTID" "$SSH_KEYS_FILE" /root/.ssh/authorized_keys >/dev/null 2>&1 ||
            pct exec "$CTID" -- sh -c "cat > /root/.ssh/authorized_keys" <"$SSH_KEYS_FILE" || {
            msg_error "write authorized_keys failed"
            return 1
        }
        pct exec "$CTID" -- sh -c 'chmod 600 /root/.ssh/authorized_keys' || true
        msg_ok "Installed SSH keys into CT ${CTID}"
        return 0
    fi

    # Fallback: nichts ausgewählt
    msg_warn "No SSH keys to install (skipping)."
    return 0
}

# ------------------------------------------------------------------------------
# find_host_ssh_keys()
#
# - Scans system for available SSH keys
# - Supports defaults (~/.ssh, /etc/ssh/authorized_keys)
# - Returns list of files containing valid SSH public keys
# - Sets FOUND_HOST_KEY_COUNT to number of keys found
# ------------------------------------------------------------------------------
find_host_ssh_keys() {
    local re='(ssh-(rsa|ed25519)|ecdsa-sha2-nistp256|sk-(ssh-ed25519|ecdsa-sha2-nistp256))'
    local -a files=() cand=()
    local g="${var_ssh_import_glob:-}"
    local total=0 f base c

    shopt -s nullglob
    if [[ -n "$g" ]]; then
        for pat in $g; do cand+=($pat); done
    else
        cand+=(/root/.ssh/authorized_keys /root/.ssh/authorized_keys2)
        cand+=(/root/.ssh/*.pub)
        cand+=(/etc/ssh/authorized_keys /etc/ssh/authorized_keys.d/*)
    fi
    shopt -u nullglob

    for f in "${cand[@]}"; do
        [[ -f "$f" && -r "$f" ]] || continue
        base="$(basename -- "$f")"
        case "$base" in
        known_hosts | known_hosts.* | config) continue ;;
        id_*) [[ "$f" != *.pub ]] && continue ;;
        esac

        # CRLF safe check for host keys
        c=$(tr -d '\r' <"$f" | awk '
      /^[[:space:]]*#/ {next}
      /^[[:space:]]*$/ {next}
      {print}
    ' | grep -E -c '"$re"' || true)

        if ((c > 0)); then
            files+=("$f")
            total=$((total + c))
        fi
    done

    # Fallback to /root/.ssh/authorized_keys
    if ((${#files[@]} == 0)) && [[ -r /root/.ssh/authorized_keys ]]; then
        if grep -E -q "$re" /root/.ssh/authorized_keys; then
            files+=(/root/.ssh/authorized_keys)
            total=$((total + $(grep -E -c "$re" /root/.ssh/authorized_keys || echo 0)))
        fi
    fi

    FOUND_HOST_KEY_COUNT="$total"
    (
        IFS=:
        echo "${files[*]}"
    )
}

# ==============================================================================
# SECTION 4: STORAGE & RESOURCE MANAGEMENT
# ==============================================================================

# ------------------------------------------------------------------------------
# _write_storage_to_vars()
#
# - Writes storage selection to vars file
# - Removes old entries (commented and uncommented) to avoid duplicates
# - Arguments: vars_file, key (var_container_storage/var_template_storage), value
# ------------------------------------------------------------------------------
_write_storage_to_vars() {
    # $1 = vars_file, $2 = key (var_container_storage / var_template_storage), $3 = value
    local vf="$1" key="$2" val="$3"
    # remove uncommented and commented versions to avoid duplicates
    sed -i "/^[#[:space:]]*${key}=/d" "$vf"
    echo "${key}=${val}" >>"$vf"
}

choose_and_set_storage_for_file() {
    # $1 = vars_file, $2 = class ('container'|'template')
    local vf="$1" class="$2" key="" current=""
    case "$class" in
    container) key="var_container_storage" ;;
    template) key="var_template_storage" ;;
    *)
        msg_error "Unknown storage class: $class"
        return 1
        ;;
    esac

    current=$(awk -F= -v k="^${key}=" '$0 ~ k {print $2; exit}' "$vf")

    # If only one storage exists for the content type, auto-pick. Else always ask (your wish #4).
    local content="rootdir"
    [[ "$class" == "template" ]] && content="vztmpl"
    local count
    count=$(pvesm status -content "$content" | awk 'NR>1{print $1}' | wc -l)

    if [[ "$count" -eq 1 ]]; then
        STORAGE_RESULT=$(pvesm status -content "$content" | awk 'NR>1{print $1; exit}')
        STORAGE_INFO=""
    else
        # If the current value is preselectable, we could show it, but per your requirement we always offer selection
        select_storage "$class" || return 1
    fi

    _write_storage_to_vars "$vf" "$key" "$STORAGE_RESULT"

    # Keep environment in sync for later steps (e.g. app-default save)
    if [[ "$class" == "container" ]]; then
        export var_container_storage="$STORAGE_RESULT"
        export CONTAINER_STORAGE="$STORAGE_RESULT"
    else
        export var_template_storage="$STORAGE_RESULT"
        export TEMPLATE_STORAGE="$STORAGE_RESULT"
    fi

    # Silent operation - no output message
}

# ==============================================================================
# SECTION 5: CONFIGURATION & DEFAULTS MANAGEMENT
# ==============================================================================

# ------------------------------------------------------------------------------
# base_settings()
#
# - Defines all base/default variables for container creation
# - Reads from environment variables (var_*)
# - Provides fallback defaults for OS type/version
# - App-specific values take precedence when they are HIGHER (for CPU, RAM, DISK)
# - Sets up container type, resources, network, SSH, features, and tags
# ------------------------------------------------------------------------------
base_settings() {
    # Default Settings
    CT_TYPE=${var_unprivileged:-"1"}

    # Resource allocation: App defaults take precedence if HIGHER
    # Compare app-declared values (saved in APP_DEFAULT_*) with current var_* values
    local final_disk="${var_disk:-4}"
    local final_cpu="${var_cpu:-1}"
    local final_ram="${var_ram:-1024}"

    # If app declared higher values, use those instead
    if [[ -n "${APP_DEFAULT_DISK:-}" && "${APP_DEFAULT_DISK}" =~ ^[0-9]+$ ]]; then
        if [[ "${APP_DEFAULT_DISK}" -gt "${final_disk}" ]]; then
            final_disk="${APP_DEFAULT_DISK}"
        fi
    fi

    if [[ -n "${APP_DEFAULT_CPU:-}" && "${APP_DEFAULT_CPU}" =~ ^[0-9]+$ ]]; then
        if [[ "${APP_DEFAULT_CPU}" -gt "${final_cpu}" ]]; then
            final_cpu="${APP_DEFAULT_CPU}"
        fi
    fi

    if [[ -n "${APP_DEFAULT_RAM:-}" && "${APP_DEFAULT_RAM}" =~ ^[0-9]+$ ]]; then
        if [[ "${APP_DEFAULT_RAM}" -gt "${final_ram}" ]]; then
            final_ram="${APP_DEFAULT_RAM}"
        fi
    fi

    DISK_SIZE="${final_disk}"
    CORE_COUNT="${final_cpu}"
    RAM_SIZE="${final_ram}"
    VERBOSE=${var_verbose:-"${1:-no}"}
    PW=${var_pw:-""}
    CT_ID=${var_ctid:-$NEXTID}
    HN=${var_hostname:-$NSAPP}
    BRG=${var_brg:-"vmbr0"}
    NET=${var_net:-"dhcp"}
    IPV6_METHOD=${var_ipv6_method:-"none"}
    IPV6_STATIC=${var_ipv6_static:-""}
    GATE=${var_gateway:-""}
    APT_CACHER=${var_apt_cacher:-""}
    APT_CACHER_IP=${var_apt_cacher_ip:-""}

    # Runtime check: Verify APT cacher is reachable if configured
    if [[ -n "$APT_CACHER_IP" && "$APT_CACHER" == "yes" ]]; then
        if ! curl -s --connect-timeout 2 "http://${APT_CACHER_IP}:3142" >/dev/null 2>&1; then
            msg_warn "APT Cacher configured but not reachable at ${APT_CACHER_IP}:3142"
            msg_custom "⚠️" "${YW}" "Disabling APT Cacher for this installation"
            APT_CACHER=""
            APT_CACHER_IP=""
        else
            msg_ok "APT Cacher verified at ${APT_CACHER_IP}:3142"
        fi
    fi

    MTU=${var_mtu:-""}
    SD=${var_storage:-""}
    NS=${var_ns:-""}
    MAC=${var_mac:-""}
    VLAN=${var_vlan:-""}
    SSH=${var_ssh:-"no"}
    SSH_AUTHORIZED_KEY=${var_ssh_authorized_key:-""}
    UDHCPC_FIX=${var_udhcpc_fix:-""}
    TAGS="community-script,${var_tags:-}"
    ENABLE_FUSE=${var_fuse:-"${1:-no}"}
    ENABLE_TUN=${var_tun:-"${1:-no}"}

    # Since these 2 are only defined outside of default_settings function, we add a temporary fallback. TODO: To align everything, we should add these as constant variables (e.g. OSTYPE and OSVERSION), but that would currently require updating the default_settings function for all existing scripts
    if [ -z "$var_os" ]; then
        var_os="debian"
    fi
    if [ -z "$var_version" ]; then
        var_version="12"
    fi
}

# ------------------------------------------------------------------------------
# load_vars_file()
#
# - Safe parser for KEY=VALUE lines from vars files
# - Used by default_var_settings and app defaults loading
# - Only loads whitelisted var_* keys
# ------------------------------------------------------------------------------
load_vars_file() {
    local file="$1"
    [ -f "$file" ] || return 0
    msg_info "Loading defaults from ${file}"

    # Allowed var_* keys
    local VAR_WHITELIST=(
        var_apt_cacher var_apt_cacher_ip var_brg var_cpu var_disk var_fuse var_keyctl
        var_gateway var_hostname var_ipv6_method var_mac var_mknod var_mount_fs var_mtu
        var_net var_nesting var_ns var_protection var_pw var_ram var_tags var_timezone var_tun var_unprivileged
        var_verbose var_vlan var_ssh var_ssh_authorized_key var_container_storage var_template_storage
    )

    # Whitelist check helper
    _is_whitelisted() {
        local k="$1" w
        for w in "${VAR_WHITELIST[@]}"; do [ "$k" = "$w" ] && return 0; done
        return 1
    }

    local line key val
    while IFS= read -r line || [ -n "$line" ]; do
        line="${line#"${line%%[![:space:]]*}"}"
        line="${line%"${line##*[![:space:]]}"}"
        [[ -z "$line" || "$line" == \#* ]] && continue
        if [[ "$line" =~ ^([A-Za-z_][A-Za-z0-9_]*)=(.*)$ ]]; then
            local var_key="${BASH_REMATCH[1]}"
            local var_val="${BASH_REMATCH[2]}"

            [[ "$var_key" != var_* ]] && continue
            _is_whitelisted "$var_key" || continue

            # Strip quotes
            if [[ "$var_val" =~ ^\"(.*)\"$ ]]; then
                var_val="${BASH_REMATCH[1]}"
            elif [[ "$var_val" =~ ^\'(.*)\'$ ]]; then
                var_val="${BASH_REMATCH[1]}"
            fi

            # Set only if not already exported
            [[ -z "${!var_key+x}" ]] && export "${var_key}=${var_val}"
        fi
    done <"$file"
    msg_ok "Loaded ${file}"
}

# ------------------------------------------------------------------------------
# default_var_settings
#
# - Ensures /usr/local/community-scripts/default.vars exists (creates if missing)
# - Loads var_* values from default.vars (safe parser, no source/eval)
# - Precedence: ENV var_* > default.vars > built-in defaults
# - Maps var_verbose → VERBOSE
# - Calls base_settings "$VERBOSE" and echo_default
# ------------------------------------------------------------------------------
default_var_settings() {
    # Allowed var_* keys (alphabetically sorted)
    # Note: Removed var_ctid (can only exist once), var_ipv6_static (static IPs are unique)
    local VAR_WHITELIST=(
        var_apt_cacher var_apt_cacher_ip var_brg var_cpu var_disk var_fuse var_keyctl
        var_gateway var_hostname var_ipv6_method var_mac var_mknod var_mount_fs var_mtu
        var_net var_nesting var_ns var_protection var_pw var_ram var_tags var_timezone var_tun var_unprivileged
        var_verbose var_vlan var_ssh var_ssh_authorized_key var_container_storage var_template_storage
    )

    # Snapshot: environment variables (highest precedence)
    declare -A _HARD_ENV=()
    local _k
    for _k in "${VAR_WHITELIST[@]}"; do
        if printenv "$_k" >/dev/null 2>&1; then _HARD_ENV["$_k"]=1; fi
    done

    # Find default.vars location
    local _find_default_vars
    _find_default_vars() {
        local f
        for f in \
            /usr/local/community-scripts/default.vars \
            "$HOME/.config/community-scripts/default.vars" \
            "./default.vars"; do
            [ -f "$f" ] && {
                echo "$f"
                return 0
            }
        done
        return 1
    }
    # Allow override of storages via env (for non-interactive use cases)
    [ -n "${var_template_storage:-}" ] && TEMPLATE_STORAGE="$var_template_storage"
    [ -n "${var_container_storage:-}" ] && CONTAINER_STORAGE="$var_container_storage"

    # Create once, with storages already selected, no var_ctid/var_hostname lines
    local _ensure_default_vars
    _ensure_default_vars() {
        _find_default_vars >/dev/null 2>&1 && return 0

        local canonical="/usr/local/community-scripts/default.vars"
        # Silent creation - no msg_info output
        mkdir -p /usr/local/community-scripts

        # Pick storages before writing the file (always ask unless only one)
        # Create a minimal temp file to write into
        : >"$canonical"

        # Base content (no var_ctid / var_hostname here)
        cat >"$canonical" <<'EOF'
# Community-Scripts defaults (var_* only). Lines starting with # are comments.
# Precedence: ENV var_* > default.vars > built-ins.
# Keep keys alphabetically sorted.
#
# Supported OS types (PVE 9.x):
#   alpine, archlinux, centos, debian, devuan, fedora, gentoo,
#   nixos, opensuse, ubuntu, unmanaged
#   Also: almalinux, rocky (RHEL derivatives)
#
# Supported architectures (PVE 9.x):
#   amd64, arm64, armhf, i386, riscv32, riscv64

# Container type
var_unprivileged=1

# Resources
var_cpu=1
var_disk=4
var_ram=1024

# Network
var_brg=vmbr0
var_net=dhcp
var_ipv6_method=none
# var_gateway=
# var_vlan=
# var_mtu=
# var_mac=
# var_ns=

# SSH
var_ssh=no
# var_ssh_authorized_key=

# APT cacher (optional - with example)
# var_apt_cacher=yes
# var_apt_cacher_ip=192.168.1.10

# Features/Tags/verbosity
var_fuse=no
var_tun=no

# Advanced Settings (Proxmox-official features)
var_nesting=1          # Allow nesting (required for Docker/LXC in CT)
var_keyctl=0           # Allow keyctl() - needed for Docker (systemd-networkd workaround)
var_mknod=0            # Allow device node creation (requires kernel 5.3+, experimental)
var_mount_fs=          # Allow specific filesystems: nfs,fuse,ext4,etc (leave empty for defaults)
var_protection=no      # Prevent accidental deletion of container
var_timezone=          # Container timezone (e.g. Europe/Berlin, leave empty for host timezone)
var_tags=community-script
var_verbose=no

# Security (root PW) – empty => autologin
# var_pw=
EOF

        # Now choose storages (always prompt unless just one exists)
        choose_and_set_storage_for_file "$canonical" template
        choose_and_set_storage_for_file "$canonical" container

        chmod 0644 "$canonical"
        # Silent creation - no output message
    }

    # Whitelist check
    local _is_whitelisted_key
    _is_whitelisted_key() {
        local k="$1"
        local w
        for w in "${VAR_WHITELIST[@]}"; do [ "$k" = "$w" ] && return 0; done
        return 1
    }

    # 1) Ensure file exists
    _ensure_default_vars

    # 2) Load file
    local dv
    dv="$(_find_default_vars)" || {
        msg_error "default.vars not found after ensure step"
        return 1
    }
    load_vars_file "$dv"

    # 3) Map var_verbose → VERBOSE
    if [[ -n "${var_verbose:-}" ]]; then
        case "${var_verbose,,}" in 1 | yes | true | on) VERBOSE="yes" ;; 0 | no | false | off) VERBOSE="no" ;; *) VERBOSE="${var_verbose}" ;; esac
    else
        VERBOSE="no"
    fi

    # 4) Apply base settings and show summary
    METHOD="mydefaults-global"
    base_settings "$VERBOSE"
    header_info
    echo -e "${DEFAULT}${BOLD}${BL}Using User Defaults (default.vars) on node $PVEHOST_NAME${CL}"
    echo_default
}

# ------------------------------------------------------------------------------
# get_app_defaults_path()
#
# - Returns full path for app-specific defaults file
# - Example: /usr/local/community-scripts/defaults/<app>.vars
# ------------------------------------------------------------------------------

get_app_defaults_path() {
    local n="${NSAPP:-${APP,,}}"
    echo "/usr/local/community-scripts/defaults/${n}.vars"
}

# ------------------------------------------------------------------------------
# maybe_offer_save_app_defaults
#
# - Called after advanced_settings returned with fully chosen values.
# - If no <nsapp>.vars exists, offers to persist current advanced settings
#   into /usr/local/community-scripts/defaults/<nsapp>.vars
# - Only writes whitelisted var_* keys.
# - Extracts raw values from flags like ",gw=..." ",mtu=..." etc.
# ------------------------------------------------------------------------------
if ! declare -p VAR_WHITELIST >/dev/null 2>&1; then
    # Note: Removed var_ctid (can only exist once), var_ipv6_static (static IPs are unique)
    declare -ag VAR_WHITELIST=(
        var_apt_cacher var_apt_cacher_ip var_brg var_cpu var_disk var_fuse
        var_gateway var_hostname var_ipv6_method var_mac var_mtu
        var_net var_ns var_pw var_ram var_tags var_tun var_unprivileged
        var_verbose var_vlan var_ssh var_ssh_authorized_key var_container_storage var_template_storage
    )
fi

# Global whitelist check function (used by _load_vars_file_to_map and others)
_is_whitelisted_key() {
    local k="$1"
    local w
    for w in "${VAR_WHITELIST[@]}"; do [ "$k" = "$w" ] && return 0; done
    return 1
}

_sanitize_value() {
    # Disallow Command-Substitution / Shell-Meta
    case "$1" in
    *'$('* | *'`'* | *';'* | *'&'* | *'<('*)
        echo ""
        return 0
        ;;
    esac
    echo "$1"
}

# Map-Parser: read var_* from file into _VARS_IN associative array
# Note: Main _load_vars_file() with full validation is defined in default_var_settings section
# This simplified version is used specifically for diff operations via _VARS_IN array
declare -A _VARS_IN
_load_vars_file_to_map() {
    local file="$1"
    [ -f "$file" ] || return 0
    _VARS_IN=() # Clear array
    local line key val
    while IFS= read -r line || [ -n "$line" ]; do
        line="${line#"${line%%[![:space:]]*}"}"
        line="${line%"${line##*[![:space:]]}"}"
        [ -z "$line" ] && continue
        case "$line" in
        \#*) continue ;;
        esac
        key=$(printf "%s" "$line" | cut -d= -f1)
        val=$(printf "%s" "$line" | cut -d= -f2-)
        case "$key" in
        var_*)
            if _is_whitelisted_key "$key"; then
                _VARS_IN["$key"]="$val"
            fi
            ;;
        esac
    done <"$file"
}

# Diff function for two var_* files -> produces human-readable diff list for $1 (old) vs $2 (new)
_build_vars_diff() {
    local oldf="$1" newf="$2"
    local k
    local -A OLD=() NEW=()
    _load_vars_file_to_map "$oldf"
    for k in "${!_VARS_IN[@]}"; do OLD["$k"]="${_VARS_IN[$k]}"; done
    _load_vars_file_to_map "$newf"
    for k in "${!_VARS_IN[@]}"; do NEW["$k"]="${_VARS_IN[$k]}"; done

    local out
    out+="# Diff for ${APP} (${NSAPP})\n"
    out+="# Old: ${oldf}\n# New: ${newf}\n\n"

    local found_change=0

    # Changed & Removed
    for k in "${!OLD[@]}"; do
        if [[ -v NEW["$k"] ]]; then
            if [[ "${OLD[$k]}" != "${NEW[$k]}" ]]; then
                out+="~ ${k}\n    - old: ${OLD[$k]}\n    + new: ${NEW[$k]}\n"
                found_change=1
            fi
        else
            out+="- ${k}\n    - old: ${OLD[$k]}\n"
            found_change=1
        fi
    done

    # Added
    for k in "${!NEW[@]}"; do
        if [[ ! -v OLD["$k"] ]]; then
            out+="+ ${k}\n    + new: ${NEW[$k]}\n"
            found_change=1
        fi
    done

    if [[ $found_change -eq 0 ]]; then
        out+="(No differences)\n"
    fi

    printf "%b" "$out"
}

# Build a temporary <app>.vars file from current advanced settings
_build_current_app_vars_tmp() {
    tmpf="$(mktemp /tmp/${NSAPP:-app}.vars.new.XXXXXX)"

    # NET/GW
    _net="${NET:-}"
    _gate=""
    case "${GATE:-}" in
    ,gw=*) _gate=$(echo "$GATE" | sed 's/^,gw=//') ;;
    esac

    # IPv6
    _ipv6_method="${IPV6_METHOD:-auto}"
    _ipv6_static=""
    _ipv6_gateway=""
    if [ "$_ipv6_method" = "static" ]; then
        _ipv6_static="${IPV6_ADDR:-}"
        _ipv6_gateway="${IPV6_GATE:-}"
    fi

    # MTU/VLAN/MAC
    _mtu=""
    _vlan=""
    _mac=""
    case "${MTU:-}" in
    ,mtu=*) _mtu=$(echo "$MTU" | sed 's/^,mtu=//') ;;
    esac
    case "${VLAN:-}" in
    ,tag=*) _vlan=$(echo "$VLAN" | sed 's/^,tag=//') ;;
    esac
    case "${MAC:-}" in
    ,hwaddr=*) _mac=$(echo "$MAC" | sed 's/^,hwaddr=//') ;;
    esac

    # DNS / Searchdomain
    _ns=""
    _searchdomain=""
    case "${NS:-}" in
    -nameserver=*) _ns=$(echo "$NS" | sed 's/^-nameserver=//') ;;
    esac
    case "${SD:-}" in
    -searchdomain=*) _searchdomain=$(echo "$SD" | sed 's/^-searchdomain=//') ;;
    esac

    # SSH / APT / Features
    _ssh="${SSH:-no}"
    _ssh_auth="${SSH_AUTHORIZED_KEY:-}"
    _apt_cacher="${APT_CACHER:-}"
    _apt_cacher_ip="${APT_CACHER_IP:-}"
    _fuse="${ENABLE_FUSE:-no}"
    _tun="${ENABLE_TUN:-no}"
    _nesting="${ENABLE_NESTING:-1}"
    _keyctl="${ENABLE_KEYCTL:-0}"
    _mknod="${ENABLE_MKNOD:-0}"
    _mount_fs="${ALLOW_MOUNT_FS:-}"
    _protect="${PROTECT_CT:-no}"
    _timezone="${CT_TIMEZONE:-}"
    _tags="${TAGS:-}"
    _verbose="${VERBOSE:-no}"

    # Type / Resources / Identity
    _unpriv="${CT_TYPE:-1}"
    _cpu="${CORE_COUNT:-1}"
    _ram="${RAM_SIZE:-1024}"
    _disk="${DISK_SIZE:-4}"
    _hostname="${HN:-$NSAPP}"

    # Storage
    _tpl_storage="${TEMPLATE_STORAGE:-${var_template_storage:-}}"
    _ct_storage="${CONTAINER_STORAGE:-${var_container_storage:-}}"

    {
        echo "# App-specific defaults for ${APP} (${NSAPP})"
        echo "# Generated on $(date -u '+%Y-%m-%dT%H:%M:%SZ')"
        echo

        echo "var_unprivileged=$(_sanitize_value "$_unpriv")"
        echo "var_cpu=$(_sanitize_value "$_cpu")"
        echo "var_ram=$(_sanitize_value "$_ram")"
        echo "var_disk=$(_sanitize_value "$_disk")"

        [ -n "${BRG:-}" ] && echo "var_brg=$(_sanitize_value "$BRG")"
        [ -n "$_net" ] && echo "var_net=$(_sanitize_value "$_net")"
        [ -n "$_gate" ] && echo "var_gateway=$(_sanitize_value "$_gate")"
        [ -n "$_mtu" ] && echo "var_mtu=$(_sanitize_value "$_mtu")"
        [ -n "$_vlan" ] && echo "var_vlan=$(_sanitize_value "$_vlan")"
        [ -n "$_mac" ] && echo "var_mac=$(_sanitize_value "$_mac")"
        [ -n "$_ns" ] && echo "var_ns=$(_sanitize_value "$_ns")"

        [ -n "$_ipv6_method" ] && echo "var_ipv6_method=$(_sanitize_value "$_ipv6_method")"
        # var_ipv6_static removed - static IPs are unique, can't be default

        [ -n "$_ssh" ] && echo "var_ssh=$(_sanitize_value "$_ssh")"
        [ -n "$_ssh_auth" ] && echo "var_ssh_authorized_key=$(_sanitize_value "$_ssh_auth")"

        [ -n "$_apt_cacher" ] && echo "var_apt_cacher=$(_sanitize_value "$_apt_cacher")"
        [ -n "$_apt_cacher_ip" ] && echo "var_apt_cacher_ip=$(_sanitize_value "$_apt_cacher_ip")"

        [ -n "$_fuse" ] && echo "var_fuse=$(_sanitize_value "$_fuse")"
        [ -n "$_tun" ] && echo "var_tun=$(_sanitize_value "$_tun")"
        [ -n "$_nesting" ] && echo "var_nesting=$(_sanitize_value "$_nesting")"
        [ -n "$_keyctl" ] && echo "var_keyctl=$(_sanitize_value "$_keyctl")"
        [ -n "$_mknod" ] && echo "var_mknod=$(_sanitize_value "$_mknod")"
        [ -n "$_mount_fs" ] && echo "var_mount_fs=$(_sanitize_value "$_mount_fs")"
        [ -n "$_protect" ] && echo "var_protection=$(_sanitize_value "$_protect")"
        [ -n "$_timezone" ] && echo "var_timezone=$(_sanitize_value "$_timezone")"
        [ -n "$_tags" ] && echo "var_tags=$(_sanitize_value "$_tags")"
        [ -n "$_verbose" ] && echo "var_verbose=$(_sanitize_value "$_verbose")"

        [ -n "$_hostname" ] && echo "var_hostname=$(_sanitize_value "$_hostname")"
        [ -n "$_searchdomain" ] && echo "var_searchdomain=$(_sanitize_value "$_searchdomain")"

        [ -n "$_tpl_storage" ] && echo "var_template_storage=$(_sanitize_value "$_tpl_storage")"
        [ -n "$_ct_storage" ] && echo "var_container_storage=$(_sanitize_value "$_ct_storage")"
    } >"$tmpf"

    echo "$tmpf"
}

# ------------------------------------------------------------------------------
# maybe_offer_save_app_defaults()
#
# - Called after advanced_settings()
# - Offers to save current values as app defaults if not existing
# - If file exists: shows diff and allows Update, Keep, View Diff, or Cancel
# ------------------------------------------------------------------------------
maybe_offer_save_app_defaults() {
    local app_vars_path
    app_vars_path="$(get_app_defaults_path)"

    # always build from current settings
    local new_tmp diff_tmp
    new_tmp="$(_build_current_app_vars_tmp)"
    diff_tmp="$(mktemp -p /tmp "${NSAPP:-app}.vars.diff.XXXXXX")"

    # 1) if no file → offer to create
    if [[ ! -f "$app_vars_path" ]]; then
        if whiptail --backtitle "[dev] Proxmox VE Helper Scripts" \
            --yesno "Save these advanced settings as defaults for ${APP}?\n\nThis will create:\n${app_vars_path}" 12 72; then
            mkdir -p "$(dirname "$app_vars_path")"
            install -m 0644 "$new_tmp" "$app_vars_path"
            msg_ok "Saved app defaults: ${app_vars_path}"
        fi
        rm -f "$new_tmp" "$diff_tmp"
        return 0
    fi

    # 2) if file exists → build diff
    _build_vars_diff "$app_vars_path" "$new_tmp" >"$diff_tmp"

    # if no differences → do nothing
    if grep -q "^(No differences)$" "$diff_tmp"; then
        rm -f "$new_tmp" "$diff_tmp"
        return 0
    fi

    # 3) if file exists → show menu with default selection "Update Defaults"
    local app_vars_file
    app_vars_file="$(basename "$app_vars_path")"

    while true; do
        local sel
        sel="$(whiptail --backtitle "[dev] Proxmox VE Helper Scripts" \
            --title "APP DEFAULTS – ${APP}" \
            --menu "Differences detected. What do you want to do?" 20 78 10 \
            "Update Defaults" "Write new values to ${app_vars_file}" \
            "Keep Current" "Keep existing defaults (no changes)" \
            "View Diff" "Show a detailed diff" \
            "Cancel" "Abort without changes" \
            --default-item "Update Defaults" \
            3>&1 1>&2 2>&3)" || { sel="Cancel"; }

        case "$sel" in
        "Update Defaults")
            install -m 0644 "$new_tmp" "$app_vars_path"
            msg_ok "Updated app defaults: ${app_vars_path}"
            break
            ;;
        "Keep Current")
            msg_custom "ℹ️" "${BL}" "Keeping current app defaults: ${app_vars_path}"
            break
            ;;
        "View Diff")
            whiptail --backtitle "[dev] Proxmox VE Helper Scripts" \
                --title "Diff – ${APP}" \
                --scrolltext --textbox "$diff_tmp" 25 100
            ;;
        "Cancel" | *)
            msg_custom "🚫" "${YW}" "Canceled. No changes to app defaults."
            break
            ;;
        esac
    done

    rm -f "$new_tmp" "$diff_tmp"
}

ensure_storage_selection_for_vars_file() {
    local vf="$1"

    # Read stored values (if any)
    local tpl ct
    tpl=$(grep -E '^var_template_storage=' "$vf" | cut -d= -f2-)
    ct=$(grep -E '^var_container_storage=' "$vf" | cut -d= -f2-)

    if [[ -n "$tpl" && -n "$ct" ]]; then
        TEMPLATE_STORAGE="$tpl"
        CONTAINER_STORAGE="$ct"
        return 0
    fi

    choose_and_set_storage_for_file "$vf" template
    choose_and_set_storage_for_file "$vf" container

    # Silent operation - no output message
}

ensure_global_default_vars_file() {
    local vars_path="/usr/local/community-scripts/default.vars"
    if [[ ! -f "$vars_path" ]]; then
        mkdir -p "$(dirname "$vars_path")"
        touch "$vars_path"
    fi
    echo "$vars_path"
}

# ==============================================================================
# SECTION 6: ADVANCED INTERACTIVE CONFIGURATION
# ==============================================================================

# ------------------------------------------------------------------------------
# advanced_settings()
#
# - Interactive wizard-style configuration with BACK navigation
# - State-machine approach: each step can go forward or backward
# - Cancel at Step 1 = Exit Script, Cancel at other steps = Go Back
# - Allows user to customize all container settings
# ------------------------------------------------------------------------------
advanced_settings() {
    # Enter alternate screen buffer to prevent flicker between dialogs
    tput smcup 2>/dev/null || true
    trap 'tput rmcup 2>/dev/null || true' RETURN

    # Initialize defaults
    TAGS="community-script;${var_tags:-}"
    local STEP=1
    local MAX_STEP=19

    # Store values for back navigation
    local _ct_type="${CT_TYPE:-1}"
    local _pw=""
    local _pw_display="Automatic Login"
    local _ct_id="$NEXTID"
    local _hostname="$NSAPP"
    local _disk_size="$var_disk"
    local _core_count="$var_cpu"
    local _ram_size="$var_ram"
    local _bridge="vmbr0"
    local _net="dhcp"
    local _gate=""
    local _ipv6_method="auto"
    local _ipv6_addr=""
    local _ipv6_gate=""
    local _apt_cacher_ip=""
    local _mtu=""
    local _sd=""
    local _ns=""
    local _mac=""
    local _vlan=""
    local _tags="$TAGS"
    local _enable_fuse="no"
    local _verbose="no"
    local _enable_keyctl="0"
    local _enable_mknod="0"
    local _mount_fs=""
    local _protect_ct="no"
    local _ct_timezone=""

    # Helper to show current progress
    show_progress() {
        local current=$1
        local total=$MAX_STEP
        echo -e "\n${INFO}${BOLD}${DGN}Step $current of $total${CL}"
    }

    # Detect available bridges (do this once)
    local BRIDGES=""
    local BRIDGE_MENU_OPTIONS=()
    _detect_bridges() {
        IFACE_FILEPATH_LIST="/etc/network/interfaces"$'\n'$(find "/etc/network/interfaces.d/" -type f 2>/dev/null)
        BRIDGES=""
        local OLD_IFS=$IFS
        IFS=$'\n'
        for iface_filepath in ${IFACE_FILEPATH_LIST}; do
            local iface_indexes_tmpfile=$(mktemp -q -u '.iface-XXXX')
            (grep -Pn '^\s*iface' "${iface_filepath}" 2>/dev/null | cut -d':' -f1 && wc -l "${iface_filepath}" 2>/dev/null | cut -d' ' -f1) | awk 'FNR==1 {line=$0; next} {print line":"$0-1; line=$0}' >"${iface_indexes_tmpfile}" 2>/dev/null || true
            if [ -f "${iface_indexes_tmpfile}" ]; then
                while read -r pair; do
                    local start=$(echo "${pair}" | cut -d':' -f1)
                    local end=$(echo "${pair}" | cut -d':' -f2)
                    if awk "NR >= ${start} && NR <= ${end}" "${iface_filepath}" 2>/dev/null | grep -qP '^\s*(bridge[-_](ports|stp|fd|vlan-aware|vids)|ovs_type\s+OVSBridge)\b'; then
                        local iface_name=$(sed "${start}q;d" "${iface_filepath}" | awk '{print $2}')
                        BRIDGES="${iface_name}"$'\n'"${BRIDGES}"
                    fi
                done <"${iface_indexes_tmpfile}"
                rm -f "${iface_indexes_tmpfile}"
            fi
        done
        IFS=$OLD_IFS
        BRIDGES=$(echo "$BRIDGES" | grep -v '^\s*$' | sort | uniq)

        # Build bridge menu
        BRIDGE_MENU_OPTIONS=()
        if [[ -n "$BRIDGES" ]]; then
            while IFS= read -r bridge; do
                if [[ -n "$bridge" ]]; then
                    local description=$(grep -A 10 "iface $bridge" /etc/network/interfaces 2>/dev/null | grep '^#' | head -n1 | sed 's/^#\s*//')
                    BRIDGE_MENU_OPTIONS+=("$bridge" "${description:- }")
                fi
            done <<<"$BRIDGES"
        fi
    }
    _detect_bridges

    # Main wizard loop
    while [ $STEP -le $MAX_STEP ]; do
        case $STEP in

        # ═══════════════════════════════════════════════════════════════════════════
        # STEP 1: Container Type
        # ═══════════════════════════════════════════════════════════════════════════
        1)
            local default_on="ON"
            local default_off="OFF"
            [[ "$_ct_type" == "0" ]] && {
                default_on="OFF"
                default_off="ON"
            }

            if result=$(whiptail --backtitle "Proxmox VE Helper Scripts [Step $STEP/$MAX_STEP]" \
                --title "CONTAINER TYPE" \
                --ok-button "Next" --cancel-button "Exit" \
                --radiolist "\nChoose container type:\n\nUse SPACE to select, ENTER to confirm." 14 58 2 \
                "1" "Unprivileged (recommended)" $default_on \
                "0" "Privileged" $default_off \
                3>&1 1>&2 2>&3); then
                [[ -n "$result" ]] && _ct_type="$result"
                ((STEP++))
            else
                exit_script
            fi
            ;;

        # ═══════════════════════════════════════════════════════════════════════════
        # STEP 2: Root Password
        # ═══════════════════════════════════════════════════════════════════════════
        2)
            if PW1=$(whiptail --backtitle "Proxmox VE Helper Scripts [Step $STEP/$MAX_STEP]" \
                --title "ROOT PASSWORD" \
                --ok-button "Next" --cancel-button "Back" \
                --passwordbox "\nSet Root Password (needed for root ssh access)\n\nLeave blank for automatic login (no password)" 12 58 \
                3>&1 1>&2 2>&3); then

                if [[ -z "$PW1" ]]; then
                    _pw=""
                    _pw_display="Automatic Login"
                    ((STEP++))
                elif [[ "$PW1" == *" "* ]]; then
                    whiptail --msgbox "Password cannot contain spaces." 8 58
                elif ((${#PW1} < 5)); then
                    whiptail --msgbox "Password must be at least 5 characters." 8 58
                else
                    # Verify password
                    if PW2=$(whiptail --backtitle "Proxmox VE Helper Scripts [Step $STEP/$MAX_STEP]" \
                        --title "PASSWORD VERIFICATION" \
                        --ok-button "Confirm" --cancel-button "Back" \
                        --passwordbox "\nVerify Root Password" 10 58 \
                        3>&1 1>&2 2>&3); then
                        if [[ "$PW1" == "$PW2" ]]; then
                            _pw="-password $PW1"
                            _pw_display="********"
                            ((STEP++))
                        else
                            whiptail --msgbox "Passwords do not match. Please try again." 8 58
                        fi
                    else
                        ((STEP--))
                    fi
                fi
            else
                ((STEP--))
            fi
            ;;

        # ═══════════════════════════════════════════════════════════════════════════
        # STEP 3: Container ID
        # ═══════════════════════════════════════════════════════════════════════════
        3)
            if result=$(whiptail --backtitle "Proxmox VE Helper Scripts [Step $STEP/$MAX_STEP]" \
                --title "CONTAINER ID" \
                --ok-button "Next" --cancel-button "Back" \
                --inputbox "\nSet Container ID" 10 58 "$_ct_id" \
                3>&1 1>&2 2>&3); then
                _ct_id="${result:-$NEXTID}"
                ((STEP++))
            else
                ((STEP--))
            fi
            ;;

        # ═══════════════════════════════════════════════════════════════════════════
        # STEP 4: Hostname
        # ═══════════════════════════════════════════════════════════════════════════
        4)
            if result=$(whiptail --backtitle "Proxmox VE Helper Scripts [Step $STEP/$MAX_STEP]" \
                --title "HOSTNAME" \
                --ok-button "Next" --cancel-button "Back" \
                --inputbox "\nSet Hostname (lowercase, alphanumeric, hyphens only)" 10 58 "$_hostname" \
                3>&1 1>&2 2>&3); then
                local hn_test="${result:-$NSAPP}"
                hn_test=$(echo "${hn_test,,}" | tr -d ' ')
                if [[ "$hn_test" =~ ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$ ]]; then
                    _hostname="$hn_test"
                    ((STEP++))
                else
                    whiptail --msgbox "Invalid hostname: '$hn_test'\n\nOnly lowercase letters, digits and hyphens are allowed." 10 58
                fi
            else
                ((STEP--))
            fi
            ;;

        # ═══════════════════════════════════════════════════════════════════════════
        # STEP 5: Disk Size
        # ═══════════════════════════════════════════════════════════════════════════
        5)
            if result=$(whiptail --backtitle "Proxmox VE Helper Scripts [Step $STEP/$MAX_STEP]" \
                --title "DISK SIZE" \
                --ok-button "Next" --cancel-button "Back" \
                --inputbox "\nSet Disk Size in GB" 10 58 "$_disk_size" \
                3>&1 1>&2 2>&3); then
                local disk_test="${result:-$var_disk}"
                if [[ "$disk_test" =~ ^[1-9][0-9]*$ ]]; then
                    _disk_size="$disk_test"
                    ((STEP++))
                else
                    whiptail --msgbox "Disk size must be a positive integer!" 8 58
                fi
            else
                ((STEP--))
            fi
            ;;

        # ═══════════════════════════════════════════════════════════════════════════
        # STEP 6: CPU Cores
        # ═══════════════════════════════════════════════════════════════════════════
        6)
            if result=$(whiptail --backtitle "Proxmox VE Helper Scripts [Step $STEP/$MAX_STEP]" \
                --title "CPU CORES" \
                --ok-button "Next" --cancel-button "Back" \
                --inputbox "\nAllocate CPU Cores" 10 58 "$_core_count" \
                3>&1 1>&2 2>&3); then
                local cpu_test="${result:-$var_cpu}"
                if [[ "$cpu_test" =~ ^[1-9][0-9]*$ ]]; then
                    _core_count="$cpu_test"
                    ((STEP++))
                else
                    whiptail --msgbox "CPU core count must be a positive integer!" 8 58
                fi
            else
                ((STEP--))
            fi
            ;;

        # ═══════════════════════════════════════════════════════════════════════════
        # STEP 7: RAM Size
        # ═══════════════════════════════════════════════════════════════════════════
        7)
            if result=$(whiptail --backtitle "Proxmox VE Helper Scripts [Step $STEP/$MAX_STEP]" \
                --title "RAM SIZE" \
                --ok-button "Next" --cancel-button "Back" \
                --inputbox "\nAllocate RAM in MiB" 10 58 "$_ram_size" \
                3>&1 1>&2 2>&3); then
                local ram_test="${result:-$var_ram}"
                if [[ "$ram_test" =~ ^[1-9][0-9]*$ ]]; then
                    _ram_size="$ram_test"
                    ((STEP++))
                else
                    whiptail --msgbox "RAM size must be a positive integer!" 8 58
                fi
            else
                ((STEP--))
            fi
            ;;

        # ═══════════════════════════════════════════════════════════════════════════
        # STEP 8: Network Bridge
        # ═══════════════════════════════════════════════════════════════════════════
        8)
            if [[ ${#BRIDGE_MENU_OPTIONS[@]} -eq 0 ]]; then
                _bridge="vmbr0"
                ((STEP++))
            else
                if result=$(whiptail --backtitle "Proxmox VE Helper Scripts [Step $STEP/$MAX_STEP]" \
                    --title "NETWORK BRIDGE" \
                    --ok-button "Next" --cancel-button "Back" \
                    --menu "\nSelect network bridge:" 16 58 6 \
                    "${BRIDGE_MENU_OPTIONS[@]}" \
                    3>&1 1>&2 2>&3); then
                    _bridge="${result:-vmbr0}"
                    ((STEP++))
                else
                    ((STEP--))
                fi
            fi
            ;;

        # ═══════════════════════════════════════════════════════════════════════════
        # STEP 9: IPv4 Configuration
        # ═══════════════════════════════════════════════════════════════════════════
        9)
            if result=$(whiptail --backtitle "Proxmox VE Helper Scripts [Step $STEP/$MAX_STEP]" \
                --title "IPv4 CONFIGURATION" \
                --ok-button "Next" --cancel-button "Back" \
                --menu "\nSelect IPv4 Address Assignment:" 14 60 2 \
                "dhcp" "Automatic (DHCP, recommended)" \
                "static" "Static (manual entry)" \
                3>&1 1>&2 2>&3); then

                if [[ "$result" == "static" ]]; then
                    # Get static IP
                    local static_ip
                    if static_ip=$(whiptail --backtitle "Proxmox VE Helper Scripts [Step $STEP/$MAX_STEP]" \
                        --title "STATIC IPv4 ADDRESS" \
                        --ok-button "Next" --cancel-button "Back" \
                        --inputbox "\nEnter Static IPv4 CIDR Address\n(e.g. 192.168.1.100/24)" 12 58 "" \
                        3>&1 1>&2 2>&3); then
                        if [[ "$static_ip" =~ ^([0-9]{1,3}\.){3}[0-9]{1,3}/([0-9]|[1-2][0-9]|3[0-2])$ ]]; then
                            # Get gateway
                            local gateway_ip
                            if gateway_ip=$(whiptail --backtitle "Proxmox VE Helper Scripts [Step $STEP/$MAX_STEP]" \
                                --title "GATEWAY IP" \
                                --ok-button "Next" --cancel-button "Back" \
                                --inputbox "\nEnter Gateway IP address" 10 58 "" \
                                3>&1 1>&2 2>&3); then
                                if [[ "$gateway_ip" =~ ^([0-9]{1,3}\.){3}[0-9]{1,3}$ ]]; then
                                    _net="$static_ip"
                                    _gate=",gw=$gateway_ip"
                                    ((STEP++))
                                else
                                    whiptail --msgbox "Invalid Gateway IP format." 8 58
                                fi
                            fi
                        else
                            whiptail --msgbox "Invalid IPv4 CIDR format.\nExample: 192.168.1.100/24" 8 58
                        fi
                    fi
                else
                    _net="dhcp"
                    _gate=""
                    ((STEP++))
                fi
            else
                ((STEP--))
            fi
            ;;

        # ═══════════════════════════════════════════════════════════════════════════
        # STEP 10: IPv6 Configuration
        # ═══════════════════════════════════════════════════════════════════════════
        10)
            if result=$(whiptail --backtitle "Proxmox VE Helper Scripts [Step $STEP/$MAX_STEP]" \
                --title "IPv6 CONFIGURATION" \
                --ok-button "Next" --cancel-button "Back" \
                --menu "\nSelect IPv6 Address Management:" 16 70 5 \
                "auto" "SLAAC/AUTO (recommended) - Dynamic IPv6 from network" \
                "dhcp" "DHCPv6 - DHCP-assigned IPv6 address" \
                "static" "Static - Manual IPv6 address configuration" \
                "none" "None - No IPv6 assignment (most containers)" \
                "disable" "Fully Disabled - (breaks some services)" \
                3>&1 1>&2 2>&3); then

                _ipv6_method="$result"
                case "$result" in
                static)
                    local ipv6_addr
                    if ipv6_addr=$(whiptail --backtitle "Proxmox VE Helper Scripts" \
                        --title "STATIC IPv6 ADDRESS" \
                        --inputbox "\nEnter IPv6 CIDR address\n(e.g. 2001:db8::1/64)" 12 58 "" \
                        3>&1 1>&2 2>&3); then
                        if [[ "$ipv6_addr" =~ ^([0-9a-fA-F:]+:+)+[0-9a-fA-F]+(/[0-9]{1,3})$ ]]; then
                            _ipv6_addr="$ipv6_addr"
                            # Optional gateway
                            _ipv6_gate=$(whiptail --backtitle "Proxmox VE Helper Scripts" \
                                --title "IPv6 GATEWAY" \
                                --inputbox "\nEnter IPv6 gateway (optional, leave blank for none)" 10 58 "" \
                                3>&1 1>&2 2>&3) || true
                            ((STEP++))
                        else
                            whiptail --msgbox "Invalid IPv6 CIDR format." 8 58
                        fi
                    fi
                    ;;
                dhcp)
                    _ipv6_addr="dhcp"
                    _ipv6_gate=""
                    ((STEP++))
                    ;;
                disable)
                    _ipv6_addr=""
                    _ipv6_gate=""
                    ((STEP++))
                    ;;
                none)
                    _ipv6_addr="none"
                    _ipv6_gate=""
                    ((STEP++))
                    ;;
                *)
                    _ipv6_addr=""
                    _ipv6_gate=""
                    ((STEP++))
                    ;;
                esac
            else
                ((STEP--))
            fi
            ;;

        # ═══════════════════════════════════════════════════════════════════════════
        # STEP 11: MTU Size
        # ═══════════════════════════════════════════════════════════════════════════
        11)
            if result=$(whiptail --backtitle "Proxmox VE Helper Scripts [Step $STEP/$MAX_STEP]" \
                --title "MTU SIZE" \
                --ok-button "Next" --cancel-button "Back" \
                --inputbox "\nSet Interface MTU Size\n(leave blank for default 1500)" 12 58 "" \
                3>&1 1>&2 2>&3); then
                _mtu="$result"
                ((STEP++))
            else
                ((STEP--))
            fi
            ;;

        # ═══════════════════════════════════════════════════════════════════════════
        # STEP 12: DNS Search Domain
        # ═══════════════════════════════════════════════════════════════════════════
        12)
            if result=$(whiptail --backtitle "Proxmox VE Helper Scripts [Step $STEP/$MAX_STEP]" \
                --title "DNS SEARCH DOMAIN" \
                --ok-button "Next" --cancel-button "Back" \
                --inputbox "\nSet DNS Search Domain\n(leave blank to use host setting)" 12 58 "" \
                3>&1 1>&2 2>&3); then
                _sd="$result"
                ((STEP++))
            else
                ((STEP--))
            fi
            ;;

        # ═══════════════════════════════════════════════════════════════════════════
        # STEP 13: DNS Server
        # ═══════════════════════════════════════════════════════════════════════════
        13)
            if result=$(whiptail --backtitle "Proxmox VE Helper Scripts [Step $STEP/$MAX_STEP]" \
                --title "DNS SERVER" \
                --ok-button "Next" --cancel-button "Back" \
                --inputbox "\nSet DNS Server IP\n(leave blank to use host setting)" 12 58 "" \
                3>&1 1>&2 2>&3); then
                _ns="$result"
                ((STEP++))
            else
                ((STEP--))
            fi
            ;;

        # ═══════════════════════════════════════════════════════════════════════════
        # STEP 14: MAC Address
        # ═══════════════════════════════════════════════════════════════════════════
        14)
            if result=$(whiptail --backtitle "Proxmox VE Helper Scripts [Step $STEP/$MAX_STEP]" \
                --title "MAC ADDRESS" \
                --ok-button "Next" --cancel-button "Back" \
                --inputbox "\nSet MAC Address\n(leave blank for auto-generated)" 12 58 "" \
                3>&1 1>&2 2>&3); then
                _mac="$result"
                ((STEP++))
            else
                ((STEP--))
            fi
            ;;

        # ═══════════════════════════════════════════════════════════════════════════
        # STEP 15: VLAN Tag
        # ═══════════════════════════════════════════════════════════════════════════
        15)
            if result=$(whiptail --backtitle "Proxmox VE Helper Scripts [Step $STEP/$MAX_STEP]" \
                --title "VLAN TAG" \
                --ok-button "Next" --cancel-button "Back" \
                --inputbox "\nSet VLAN Tag\n(leave blank for no VLAN)" 12 58 "" \
                3>&1 1>&2 2>&3); then
                _vlan="$result"
                ((STEP++))
            else
                ((STEP--))
            fi
            ;;

        # ═══════════════════════════════════════════════════════════════════════════
        # STEP 16: Tags
        # ═══════════════════════════════════════════════════════════════════════════
        16)
            if result=$(whiptail --backtitle "Proxmox VE Helper Scripts [Step $STEP/$MAX_STEP]" \
                --title "CONTAINER TAGS" \
                --ok-button "Next" --cancel-button "Back" \
                --inputbox "\nSet Custom Tags (semicolon-separated)\n(remove all for no tags)" 12 58 "$_tags" \
                3>&1 1>&2 2>&3); then
                _tags="${result:-;}"
                _tags=$(echo "$_tags" | tr -d '[:space:]')
                ((STEP++))
            else
                ((STEP--))
            fi
            ;;

        # ═══════════════════════════════════════════════════════════════════════════
        # STEP 17: SSH Settings
        # ═══════════════════════════════════════════════════════════════════════════
        17)
            configure_ssh_settings
            # configure_ssh_settings handles its own flow, always advance
            ((STEP++))
            ;;

        # ═══════════════════════════════════════════════════════════════════════════
        # STEP 18: FUSE & Verbose Mode
        # ═══════════════════════════════════════════════════════════════════════════
        18)
            if whiptail --backtitle "Proxmox VE Helper Scripts [Step $STEP/$MAX_STEP]" \
                --title "FUSE SUPPORT" \
                --ok-button "Next" --cancel-button "Back" \
                --defaultno \
                --yesno "\nEnable FUSE support?\n\nRequired for: rclone, mergerfs, AppImage, etc." 12 58; then
                _enable_fuse="yes"
            else
                if [ $? -eq 1 ]; then
                    _enable_fuse="no"
                else
                    ((STEP--))
                    continue
                fi
            fi

            if whiptail --backtitle "Proxmox VE Helper Scripts [Step $STEP/$MAX_STEP]" \
                --title "VERBOSE MODE" \
                --defaultno \
                --yesno "\nEnable Verbose Mode?\n\nShows detailed output during installation." 12 58; then
                _verbose="yes"
            else
                _verbose="no"
            fi
            ((STEP++))
            ;;

        # ═══════════════════════════════════════════════════════════════════════════
        # STEP 19: Confirmation
        # ═══════════════════════════════════════════════════════════════════════════
        19)
            # Build summary
            local ct_type_desc="Unprivileged"
            [[ "$_ct_type" == "0" ]] && ct_type_desc="Privileged"

            local summary="Container Type: $ct_type_desc
Container ID: $_ct_id
Hostname: $_hostname

Resources:
  Disk: ${_disk_size} GB
  CPU: $_core_count cores
  RAM: $_ram_size MiB

Network:
  Bridge: $_bridge
  IPv4: $_net
  IPv6: $_ipv6_method

Options:
  FUSE: $_enable_fuse
  Verbose: $_verbose"

            if whiptail --backtitle "Proxmox VE Helper Scripts [Step $STEP/$MAX_STEP]" \
                --title "CONFIRM SETTINGS" \
                --ok-button "Create LXC" --cancel-button "Back" \
                --yesno "$summary\n\nCreate ${APP} LXC with these settings?" 26 58; then
                ((STEP++))
            else
                ((STEP--))
            fi
            ;;
        esac
    done

    # ═══════════════════════════════════════════════════════════════════════════
    # Apply all collected values to global variables
    # ═══════════════════════════════════════════════════════════════════════════
    CT_TYPE="$_ct_type"
    PW="$_pw"
    CT_ID="$_ct_id"
    HN="$_hostname"
    DISK_SIZE="$_disk_size"
    CORE_COUNT="$_core_count"
    RAM_SIZE="$_ram_size"
    BRG="$_bridge"
    NET="$_net"
    GATE="$_gate"
    IPV6_METHOD="$_ipv6_method"
    IPV6_ADDR="$_ipv6_addr"
    IPV6_GATE="$_ipv6_gate"
    TAGS="$_tags"
    ENABLE_FUSE="$_enable_fuse"
    VERBOSE="$_verbose"

    # Format optional values
    [[ -n "$_mtu" ]] && MTU=",mtu=$_mtu" || MTU=""
    [[ -n "$_sd" ]] && SD="-searchdomain=$_sd" || SD=""
    [[ -n "$_ns" ]] && NS="-nameserver=$_ns" || NS=""
    [[ -n "$_mac" ]] && MAC=",hwaddr=$_mac" || MAC=""
    [[ -n "$_vlan" ]] && VLAN=",tag=$_vlan" || VLAN=""

    # Alpine UDHCPC fix
    if [ "$var_os" == "alpine" ] && [ "$NET" == "dhcp" ] && [ -n "$_ns" ]; then
        UDHCPC_FIX="yes"
    else
        UDHCPC_FIX="no"
    fi
    export UDHCPC_FIX
    export SSH_KEYS_FILE

    # Display final summary
    echo -e "\n${INFO}${BOLD}${DGN}PVE Version ${PVEVERSION} (Kernel: ${KERNEL_VERSION})${CL}"
    echo -e "${OS}${BOLD}${DGN}Operating System: ${BGN}$var_os${CL}"
    echo -e "${OSVERSION}${BOLD}${DGN}Version: ${BGN}$var_version${CL}"
    echo -e "${CONTAINERTYPE}${BOLD}${DGN}Container Type: ${BGN}$([ "$CT_TYPE" == "1" ] && echo "Unprivileged" || echo "Privileged")${CL}"
    echo -e "${CONTAINERID}${BOLD}${DGN}Container ID: ${BGN}$CT_ID${CL}"
    echo -e "${HOSTNAME}${BOLD}${DGN}Hostname: ${BGN}$HN${CL}"
    echo -e "${DISKSIZE}${BOLD}${DGN}Disk Size: ${BGN}${DISK_SIZE} GB${CL}"
    echo -e "${CPUCORE}${BOLD}${DGN}CPU Cores: ${BGN}$CORE_COUNT${CL}"
    echo -e "${RAMSIZE}${BOLD}${DGN}RAM Size: ${BGN}${RAM_SIZE} MiB${CL}"
    echo -e "${BRIDGE}${BOLD}${DGN}Bridge: ${BGN}$BRG${CL}"
    echo -e "${NETWORK}${BOLD}${DGN}IPv4: ${BGN}$NET${CL}"
    echo -e "${NETWORK}${BOLD}${DGN}IPv6: ${BGN}$IPV6_METHOD${CL}"
    echo -e "${FUSE}${BOLD}${DGN}FUSE Support: ${BGN}$ENABLE_FUSE${CL}"
    echo -e "${SEARCH}${BOLD}${DGN}Verbose Mode: ${BGN}$VERBOSE${CL}"
    echo -e "${CREATING}${BOLD}${RD}Creating a ${APP} LXC using the above advanced settings${CL}"
}

# ==============================================================================
# SECTION 7: USER INTERFACE & DIAGNOSTICS
# ==============================================================================

# ------------------------------------------------------------------------------
# diagnostics_check()
#
# - Ensures diagnostics config file exists at /usr/local/community-scripts/diagnostics
# - Asks user whether to send anonymous diagnostic data
# - Saves DIAGNOSTICS=yes/no in the config file
# - Creates file if missing with default DIAGNOSTICS=yes
# - Reads current diagnostics setting from file
# - Sets global DIAGNOSTICS variable for API telemetry opt-in/out
# ------------------------------------------------------------------------------
diagnostics_check() {
    if ! [ -d "/usr/local/community-scripts" ]; then
        mkdir -p /usr/local/community-scripts
    fi

    if ! [ -f "/usr/local/community-scripts/diagnostics" ]; then
        if (whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --title "DIAGNOSTICS" --yesno "Send Diagnostics of LXC Installation?\n\n(This only transmits data without user data, just RAM, CPU, LXC name, ...)" 10 58); then
            cat <<EOF >/usr/local/community-scripts/diagnostics
DIAGNOSTICS=yes

#This file is used to store the diagnostics settings for the Community-Scripts API.
#https://github.com/community-scripts/ProxmoxVED/discussions/1836
#Your diagnostics will be sent to the Community-Scripts API for troubleshooting/statistical purposes.
#You can review the data at https://community-scripts.github.io/ProxmoxVE/data
#If you do not wish to send diagnostics, please set the variable 'DIAGNOSTICS' to "no" in /usr/local/community-scripts/diagnostics, or use the menue.
#This will disable the diagnostics feature.
#To send diagnostics, set the variable 'DIAGNOSTICS' to "yes" in /usr/local/community-scripts/diagnostics, or use the menue.
#This will enable the diagnostics feature.
#The following information will be sent:
#"disk_size"
#"core_count"
#"ram_size"
#"os_type"
#"os_version"
#"nsapp"
#"method"
#"pve_version"
#"status"
#If you have any concerns, please review the source code at /misc/build.func
EOF
            DIAGNOSTICS="yes"
        else
            cat <<EOF >/usr/local/community-scripts/diagnostics
DIAGNOSTICS=no

#This file is used to store the diagnostics settings for the Community-Scripts API.
#https://github.com/community-scripts/ProxmoxVED/discussions/1836
#Your diagnostics will be sent to the Community-Scripts API for troubleshooting/statistical purposes.
#You can review the data at https://community-scripts.github.io/ProxmoxVE/data
#If you do not wish to send diagnostics, please set the variable 'DIAGNOSTICS' to "no" in /usr/local/community-scripts/diagnostics, or use the menue.
#This will disable the diagnostics feature.
#To send diagnostics, set the variable 'DIAGNOSTICS' to "yes" in /usr/local/community-scripts/diagnostics, or use the menue.
#This will enable the diagnostics feature.
#The following information will be sent:
#"disk_size"
#"core_count"
#"ram_size"
#"os_type"
#"os_version"
#"nsapp"
#"method"
#"pve_version"
#"status"
#If you have any concerns, please review the source code at /misc/build.func
EOF
            DIAGNOSTICS="no"
        fi
    else
        DIAGNOSTICS=$(awk -F '=' '/^DIAGNOSTICS/ {print $2}' /usr/local/community-scripts/diagnostics)

    fi
}

diagnostics_menu() {
    if [ "${DIAGNOSTICS:-no}" = "yes" ]; then
        if whiptail --backtitle "[dev] Proxmox VE Helper Scripts" \
            --title "DIAGNOSTIC SETTINGS" \
            --yesno "Send Diagnostics?\n\nCurrent: ${DIAGNOSTICS}" 10 58 \
            --yes-button "No" --no-button "Back"; then
            DIAGNOSTICS="no"
            sed -i 's/^DIAGNOSTICS=.*/DIAGNOSTICS=no/' /usr/local/community-scripts/diagnostics
            whiptail --msgbox "Diagnostics set to ${DIAGNOSTICS}." 8 58
        fi
    else
        if whiptail --backtitle "[dev] Proxmox VE Helper Scripts" \
            --title "DIAGNOSTIC SETTINGS" \
            --yesno "Send Diagnostics?\n\nCurrent: ${DIAGNOSTICS}" 10 58 \
            --yes-button "Yes" --no-button "Back"; then
            DIAGNOSTICS="yes"
            sed -i 's/^DIAGNOSTICS=.*/DIAGNOSTICS=yes/' /usr/local/community-scripts/diagnostics
            whiptail --msgbox "Diagnostics set to ${DIAGNOSTICS}." 8 58
        fi
    fi
}

# ------------------------------------------------------------------------------
# echo_default()
#
# - Prints summary of default values (ID, OS, type, disk, RAM, CPU, etc.)
# - Uses icons and formatting for readability
# - Convert CT_TYPE to description
# ------------------------------------------------------------------------------
echo_default() {
    CT_TYPE_DESC="Unprivileged"
    if [ "$CT_TYPE" -eq 0 ]; then
        CT_TYPE_DESC="Privileged"
    fi
    echo -e "${INFO}${BOLD}${DGN}PVE Version ${PVEVERSION} (Kernel: ${KERNEL_VERSION})${CL}"
    echo -e "${CONTAINERID}${BOLD}${DGN}Container ID: ${BGN}${CT_ID}${CL}"
    echo -e "${OS}${BOLD}${DGN}Operating System: ${BGN}$var_os ($var_version)${CL}"
    echo -e "${CONTAINERTYPE}${BOLD}${DGN}Container Type: ${BGN}$CT_TYPE_DESC${CL}"
    echo -e "${DISKSIZE}${BOLD}${DGN}Disk Size: ${BGN}${DISK_SIZE} GB${CL}"
    echo -e "${CPUCORE}${BOLD}${DGN}CPU Cores: ${BGN}${CORE_COUNT}${CL}"
    echo -e "${RAMSIZE}${BOLD}${DGN}RAM Size: ${BGN}${RAM_SIZE} MiB${CL}"
    if [ "$VERBOSE" == "yes" ]; then
        echo -e "${SEARCH}${BOLD}${DGN}Verbose Mode: ${BGN}Enabled${CL}"
    fi
    echo -e "${CREATING}${BOLD}${BL}Creating a ${APP} LXC using the above default settings${CL}"
    echo -e "  "
}

# ------------------------------------------------------------------------------
# install_script()
#
# - Main entrypoint for installation mode
# - Runs safety checks (pve_check, root_check, maxkeys_check, diagnostics_check)
# - Builds interactive menu (Default, Verbose, Advanced, My Defaults, App Defaults, Diagnostics, Storage, Exit)
# - Applies chosen settings and triggers container build
# ------------------------------------------------------------------------------
install_script() {
    pve_check
    shell_check
    root_check
    arch_check
    ssh_check
    maxkeys_check
    diagnostics_check

    if systemctl is-active -q ping-instances.service; then
        systemctl -q stop ping-instances.service
    fi

    NEXTID=$(pvesh get /cluster/nextid)

    # Get timezone using timedatectl (Debian 13+ compatible)
    # Fallback to /etc/timezone for older systems
    if command -v timedatectl >/dev/null 2>&1; then
        timezone=$(timedatectl show --value --property=Timezone 2>/dev/null || echo "UTC")
    elif [ -f /etc/timezone ]; then
        timezone=$(cat /etc/timezone)
    else
        timezone="UTC"
    fi

    # Show APP Header
    header_info

    # --- Support CLI argument as direct preset (default, advanced, …) ---
    CHOICE="${mode:-${1:-}}"

    # If no CLI argument → show whiptail menu
    # Build menu dynamically based on available options
    local appdefaults_option=""
    local settings_option=""
    local menu_items=(
        "1" "Default Install"
        "2" "Advanced Install"
        "3" "User Defaults"
    )

    if [ -f "$(get_app_defaults_path)" ]; then
        appdefaults_option="4"
        menu_items+=("4" "App Defaults for ${APP}")
        settings_option="5"
        menu_items+=("5" "Settings")
    else
        settings_option="4"
        menu_items+=("4" "Settings")
    fi

    APPDEFAULTS_OPTION="$appdefaults_option"
    SETTINGS_OPTION="$settings_option"

    # Main menu loop - allows returning from Settings
    while true; do
        if [ -z "$CHOICE" ]; then
            TMP_CHOICE=$(whiptail \
                --backtitle "Proxmox VE Helper Scripts" \
                --title "Community-Scripts Options" \
                --ok-button "Select" --cancel-button "Exit Script" \
                --notags \
                --menu "\nChoose an option:\n Use TAB or Arrow keys to navigate, ENTER to select.\n" \
                20 60 9 \
                "${menu_items[@]}" \
                --default-item "1" \
                3>&1 1>&2 2>&3) || exit_script
            CHOICE="$TMP_CHOICE"
        fi

        # --- Main case ---
        local defaults_target=""
        local run_maybe_offer="no"
        case "$CHOICE" in
        1 | default | DEFAULT)
            header_info
            echo -e "${DEFAULT}${BOLD}${BL}Using Default Settings on node $PVEHOST_NAME${CL}"
            VERBOSE="no"
            METHOD="default"
            base_settings "$VERBOSE"
            echo_default
            defaults_target="$(ensure_global_default_vars_file)"
            break
            ;;
        2 | advanced | ADVANCED)
            header_info
            echo -e "${ADVANCED}${BOLD}${RD}Using Advanced Install on node $PVEHOST_NAME${CL}"
            echo -e "${INFO}${BOLD}${DGN}PVE Version ${PVEVERSION} (Kernel: ${KERNEL_VERSION})${CL}"
            METHOD="advanced"
            base_settings
            advanced_settings
            defaults_target="$(ensure_global_default_vars_file)"
            run_maybe_offer="yes"
            break
            ;;
        3 | mydefaults | MYDEFAULTS | userdefaults | USERDEFAULTS)
            default_var_settings || {
                msg_error "Failed to apply default.vars"
                exit 1
            }
            defaults_target="/usr/local/community-scripts/default.vars"
            break
            ;;
        "$APPDEFAULTS_OPTION" | appdefaults | APPDEFAULTS)
            if [ -f "$(get_app_defaults_path)" ]; then
                header_info
                echo -e "${DEFAULT}${BOLD}${BL}Using App Defaults for ${APP} on node $PVEHOST_NAME${CL}"
                METHOD="appdefaults"
                base_settings
                load_vars_file "$(get_app_defaults_path)"
                echo_default
                defaults_target="$(get_app_defaults_path)"
                break
            else
                msg_error "No App Defaults available for ${APP}"
                exit 1
            fi
            ;;
        "$SETTINGS_OPTION" | settings | SETTINGS)
            settings_menu
            # After settings menu, show main menu again
            header_info
            CHOICE=""
            ;;
        *)
            echo -e "${CROSS}${RD}Invalid option: $CHOICE${CL}"
            exit 1
            ;;
        esac
    done

    if [[ -n "$defaults_target" ]]; then
        ensure_storage_selection_for_vars_file "$defaults_target"
    fi

    if [[ "$run_maybe_offer" == "yes" ]]; then
        maybe_offer_save_app_defaults
    fi
}

edit_default_storage() {
    local vf="/usr/local/community-scripts/default.vars"

    # Ensure file exists
    if [[ ! -f "$vf" ]]; then
        mkdir -p "$(dirname "$vf")"
        touch "$vf"
    fi

    # Let ensure_storage_selection_for_vars_file handle everything
    ensure_storage_selection_for_vars_file "$vf"
}

settings_menu() {
    while true; do
        local settings_items=(
            "1" "Manage API-Diagnostic Setting"
            "2" "Edit Default.vars"
        )
        if [ -f "$(get_app_defaults_path)" ]; then
            settings_items+=("3" "Edit App.vars for ${APP}")
            settings_items+=("4" "Back to Main Menu")
        else
            settings_items+=("3" "Back to Main Menu")
        fi

        local choice
        choice=$(whiptail --backtitle "Proxmox VE Helper Scripts" \
            --title "Community-Scripts SETTINGS Menu" \
            --ok-button "Select" --cancel-button "Exit Script" \
            --menu "\n\nChoose a settings option:\n\nUse Arrow keys to navigate, ENTER to select, TAB for buttons." 20 60 9 \
            "${settings_items[@]}" \
            3>&1 1>&2 2>&3) || exit_script

        case "$choice" in
        1) diagnostics_menu ;;
        2) nano /usr/local/community-scripts/default.vars ;;
        3)
            if [ -f "$(get_app_defaults_path)" ]; then
                nano "$(get_app_defaults_path)"
            else
                # Back was selected (no app.vars available)
                return
            fi
            ;;
        4)
            # Back to main menu
            return
            ;;
        esac
    done
}

# ------------------------------------------------------------------------------
# check_container_resources()
#
# - Compares host RAM/CPU with required values
# - Warns if under-provisioned and asks user to continue or abort
# ------------------------------------------------------------------------------
check_container_resources() {
    current_ram=$(free -m | awk 'NR==2{print $2}')
    current_cpu=$(nproc)

    if [[ "$current_ram" -lt "$var_ram" ]] || [[ "$current_cpu" -lt "$var_cpu" ]]; then
        echo -e "\n${INFO}${HOLD} ${GN}Required: ${var_cpu} CPU, ${var_ram}MB RAM ${CL}| ${RD}Current: ${current_cpu} CPU, ${current_ram}MB RAM${CL}"
        echo -e "${YWB}Please ensure that the ${APP} LXC is configured with at least ${var_cpu} vCPU and ${var_ram} MB RAM for the build process.${CL}\n"
        echo -ne "${INFO}${HOLD} May cause data loss! ${INFO} Continue update with under-provisioned LXC? <yes/No>  "
        read -r prompt
        if [[ ! ${prompt,,} =~ ^(yes)$ ]]; then
            echo -e "${CROSS}${HOLD} ${YWB}Exiting based on user input.${CL}"
            exit 1
        fi
    else
        echo -e ""
    fi
}

# ------------------------------------------------------------------------------
# check_container_storage()
#
# - Checks /boot partition usage
# - Warns if usage >80% and asks user confirmation before proceeding
# ------------------------------------------------------------------------------
check_container_storage() {
    total_size=$(df /boot --output=size | tail -n 1)
    local used_size=$(df /boot --output=used | tail -n 1)
    usage=$((100 * used_size / total_size))
    if ((usage > 80)); then
        echo -e "${INFO}${HOLD} ${YWB}Warning: Storage is dangerously low (${usage}%).${CL}"
        echo -ne "Continue anyway? <y/N>  "
        read -r prompt
        if [[ ! ${prompt,,} =~ ^(y|yes)$ ]]; then
            echo -e "${CROSS}${HOLD}${YWB}Exiting based on user input.${CL}"
            exit 1
        fi
    fi
}

# ------------------------------------------------------------------------------
# ssh_extract_keys_from_file()
#
# - Extracts valid SSH public keys from given file
# - Supports RSA, Ed25519, ECDSA and filters out comments/invalid lines
# ------------------------------------------------------------------------------
ssh_extract_keys_from_file() {
    local f="$1"
    [[ -r "$f" ]] || return 0
    tr -d '\r' <"$f" | awk '
    /^[[:space:]]*#/ {next}
    /^[[:space:]]*$/ {next}
    # nackt: typ base64 [comment]
    /^(ssh-(rsa|ed25519)|ecdsa-sha2-nistp256|sk-(ssh-ed25519|ecdsa-sha2-nistp256))[[:space:]]+/ {print; next}
    # mit Optionen: finde ab erstem Key-Typ
    {
      match($0, /(ssh-(rsa|ed25519)|ecdsa-sha2-nistp256|sk-(ssh-ed25519|ecdsa-sha2-nistp256))[[:space:]]+/)
      if (RSTART>0) { print substr($0, RSTART) }
    }
  '
}

# ------------------------------------------------------------------------------
# ssh_build_choices_from_files()
#
# - Builds interactive whiptail checklist of available SSH keys
# - Generates fingerprint, type and comment for each key
# ------------------------------------------------------------------------------
ssh_build_choices_from_files() {
    local -a files=("$@")
    CHOICES=()
    COUNT=0
    MAPFILE="$(mktemp)"
    local id key typ fp cmt base ln=0

    for f in "${files[@]}"; do
        [[ -f "$f" && -r "$f" ]] || continue
        base="$(basename -- "$f")"
        case "$base" in
        known_hosts | known_hosts.* | config) continue ;;
        id_*) [[ "$f" != *.pub ]] && continue ;;
        esac

        # map every key in file
        while IFS= read -r key; do
            [[ -n "$key" ]] || continue

            typ=""
            fp=""
            cmt=""
            # Only the pure key part (without options) is already included in ‘key’.
            read -r _typ _b64 _cmt <<<"$key"
            typ="${_typ:-key}"
            cmt="${_cmt:-}"
            # Fingerprint via ssh-keygen (if available)
            if command -v ssh-keygen >/dev/null 2>&1; then
                fp="$(printf '%s\n' "$key" | ssh-keygen -lf - 2>/dev/null | awk '{print $2}')"
            fi
            # Label shorten
            [[ ${#cmt} -gt 40 ]] && cmt="${cmt:0:37}..."

            ln=$((ln + 1))
            COUNT=$((COUNT + 1))
            id="K${COUNT}"
            echo "${id}|${key}" >>"$MAPFILE"
            CHOICES+=("$id" "[$typ] ${fp:+$fp }${cmt:+$cmt }— ${base}" "OFF")
        done < <(ssh_extract_keys_from_file "$f")
    done
}

# ------------------------------------------------------------------------------
# ssh_discover_default_files()
#
# - Scans standard paths for SSH keys
# - Includes ~/.ssh/*.pub, /etc/ssh/authorized_keys, etc.
# ------------------------------------------------------------------------------
ssh_discover_default_files() {
    local -a cand=()
    shopt -s nullglob
    cand+=(/root/.ssh/authorized_keys /root/.ssh/authorized_keys2)
    cand+=(/root/.ssh/*.pub)
    cand+=(/etc/ssh/authorized_keys /etc/ssh/authorized_keys.d/*)
    shopt -u nullglob
    printf '%s\0' "${cand[@]}"
}

configure_ssh_settings() {
    SSH_KEYS_FILE="$(mktemp)"
    : >"$SSH_KEYS_FILE"

    IFS=$'\0' read -r -d '' -a _def_files < <(ssh_discover_default_files && printf '\0')
    ssh_build_choices_from_files "${_def_files[@]}"
    local default_key_count="$COUNT"

    local ssh_key_mode
    if [[ "$default_key_count" -gt 0 ]]; then
        ssh_key_mode=$(whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --title "SSH KEY SOURCE" --menu \
            "Provision SSH keys for root:" 14 72 4 \
            "found" "Select from detected keys (${default_key_count})" \
            "manual" "Paste a single public key" \
            "folder" "Scan another folder (path or glob)" \
            "none" "No keys" 3>&1 1>&2 2>&3) || exit_script
    else
        ssh_key_mode=$(whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --title "SSH KEY SOURCE" --menu \
            "No host keys detected; choose manual/none:" 12 72 2 \
            "manual" "Paste a single public key" \
            "none" "No keys" 3>&1 1>&2 2>&3) || exit_script
    fi

    case "$ssh_key_mode" in
    found)
        local selection
        selection=$(whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --title "SELECT HOST KEYS" \
            --checklist "Select one or more keys to import:" 20 140 10 "${CHOICES[@]}" 3>&1 1>&2 2>&3) || exit_script
        for tag in $selection; do
            tag="${tag%\"}"
            tag="${tag#\"}"
            local line
            line=$(grep -E "^${tag}\|" "$MAPFILE" | head -n1 | cut -d'|' -f2-)
            [[ -n "$line" ]] && printf '%s\n' "$line" >>"$SSH_KEYS_FILE"
        done
        ;;
    manual)
        SSH_AUTHORIZED_KEY="$(whiptail --backtitle "[dev] Proxmox VE Helper Scripts" \
            --inputbox "Paste one SSH public key line (ssh-ed25519/ssh-rsa/...)" 10 72 --title "SSH Public Key" 3>&1 1>&2 2>&3)"
        [[ -n "$SSH_AUTHORIZED_KEY" ]] && printf '%s\n' "$SSH_AUTHORIZED_KEY" >>"$SSH_KEYS_FILE"
        ;;
    folder)
        local glob_path
        glob_path=$(whiptail --backtitle "[dev] Proxmox VE Helper Scripts" \
            --inputbox "Enter a folder or glob to scan (e.g. /root/.ssh/*.pub)" 10 72 --title "Scan Folder/Glob" 3>&1 1>&2 2>&3)
        if [[ -n "$glob_path" ]]; then
            shopt -s nullglob
            read -r -a _scan_files <<<"$glob_path"
            shopt -u nullglob
            if [[ "${#_scan_files[@]}" -gt 0 ]]; then
                ssh_build_choices_from_files "${_scan_files[@]}"
                if [[ "$COUNT" -gt 0 ]]; then
                    local folder_selection
                    folder_selection=$(whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --title "SELECT FOLDER KEYS" \
                        --checklist "Select key(s) to import:" 20 78 10 "${CHOICES[@]}" 3>&1 1>&2 2>&3) || exit_script
                    for tag in $folder_selection; do
                        tag="${tag%\"}"
                        tag="${tag#\"}"
                        local line
                        line=$(grep -E "^${tag}\|" "$MAPFILE" | head -n1 | cut -d'|' -f2-)
                        [[ -n "$line" ]] && printf '%s\n' "$line" >>"$SSH_KEYS_FILE"
                    done
                else
                    whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --msgbox "No keys found in: $glob_path" 8 60
                fi
            else
                whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --msgbox "Path/glob returned no files." 8 60
            fi
        fi
        ;;
    none)
        :
        ;;
    esac

    if [[ -s "$SSH_KEYS_FILE" ]]; then
        sort -u -o "$SSH_KEYS_FILE" "$SSH_KEYS_FILE"
        printf '\n' >>"$SSH_KEYS_FILE"
    fi

    if [[ -s "$SSH_KEYS_FILE" || "$PW" == -password* ]]; then
        if (whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --defaultno --title "SSH ACCESS" --yesno "Enable root SSH access?" 10 58); then
            SSH="yes"
        else
            SSH="no"
        fi
    else
        SSH="no"
    fi
}

# ------------------------------------------------------------------------------
# start()
#
# - Entry point of script
# - On Proxmox host: calls install_script
# - In silent mode: runs update_script
# - Otherwise: shows update/setting menu
# ------------------------------------------------------------------------------
start() {
    source <(curl -fsSL https://git.community-scripts.org/community-scripts/ProxmoxVED/raw/branch/main/misc/tools.func)
    if command -v pveversion >/dev/null 2>&1; then
        install_script || return 0
        return 0
    elif [ ! -z ${PHS_SILENT+x} ] && [[ "${PHS_SILENT}" == "1" ]]; then
        VERBOSE="no"
        set_std_mode
        update_script
    else
        CHOICE=$(whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --title "${APP} LXC Update/Setting" --menu \
            "Support/Update functions for ${APP} LXC. Choose an option:" \
            12 60 3 \
            "1" "YES (Silent Mode)" \
            "2" "YES (Verbose Mode)" \
            "3" "NO (Cancel Update)" --nocancel --default-item "1" 3>&1 1>&2 2>&3)

        case "$CHOICE" in
        1)
            VERBOSE="no"
            set_std_mode
            ;;
        2)
            VERBOSE="yes"
            set_std_mode
            ;;
        3)
            clear
            exit_script
            exit
            ;;
        esac
        update_script
    fi
}

# ==============================================================================
# SECTION 8: CONTAINER CREATION & DEPLOYMENT
# ==============================================================================

# ------------------------------------------------------------------------------
# build_container()
#
# - Main function for creating and configuring LXC container
# - Builds network configuration string (IP, gateway, VLAN, MTU, MAC, IPv6)
# - Creates container via pct create with all specified settings
# - Applies features: FUSE, TUN, keyctl, VAAPI passthrough
# - Starts container and waits for network connectivity
# - Installs base packages (curl, sudo, etc.)
# - Injects SSH keys if configured
# - Executes <app>-install.sh inside container
# - Posts installation telemetry to API if diagnostics enabled
# ------------------------------------------------------------------------------
build_container() {
    #  if [ "$VERBOSE" == "yes" ]; then set -x; fi

    NET_STRING="-net0 name=eth0,bridge=${BRG:-vmbr0}"

    # MAC
    if [[ -n "$MAC" ]]; then
        case "$MAC" in
        ,hwaddr=*) NET_STRING+="$MAC" ;;
        *) NET_STRING+=",hwaddr=$MAC" ;;
        esac
    fi

    # IP (immer zwingend, Standard dhcp)
    NET_STRING+=",ip=${NET:-dhcp}"

    # Gateway
    if [[ -n "$GATE" ]]; then
        case "$GATE" in
        ,gw=*) NET_STRING+="$GATE" ;;
        *) NET_STRING+=",gw=$GATE" ;;
        esac
    fi

    # VLAN
    if [[ -n "$VLAN" ]]; then
        case "$VLAN" in
        ,tag=*) NET_STRING+="$VLAN" ;;
        *) NET_STRING+=",tag=$VLAN" ;;
        esac
    fi

    # MTU
    if [[ -n "$MTU" ]]; then
        case "$MTU" in
        ,mtu=*) NET_STRING+="$MTU" ;;
        *) NET_STRING+=",mtu=$MTU" ;;
        esac
    fi

    # IPv6 Handling
    case "$IPV6_METHOD" in
    auto) NET_STRING="$NET_STRING,ip6=auto" ;;
    dhcp) NET_STRING="$NET_STRING,ip6=dhcp" ;;
    static)
        NET_STRING="$NET_STRING,ip6=$IPV6_ADDR"
        [ -n "$IPV6_GATE" ] && NET_STRING="$NET_STRING,gw6=$IPV6_GATE"
        ;;
    none) ;;
    esac

    # Build FEATURES string (simple working version)
    if [ "$CT_TYPE" == "1" ]; then
        FEATURES="keyctl=1,nesting=1"
    else
        FEATURES="nesting=1"
    fi

    if [ "$ENABLE_FUSE" == "yes" ]; then
        FEATURES="$FEATURES,fuse=1"
    fi

    # NEW IMPLEMENTATION (Fixed): Build PCT_OPTIONS properly
    # Key insight: Bash cannot export arrays, so we build the options as a string

    TEMP_DIR=$(mktemp -d)
    pushd "$TEMP_DIR" >/dev/null
    if [ "$var_os" == "alpine" ]; then
        export FUNCTIONS_FILE_PATH="$(curl -fsSL https://git.community-scripts.org/community-scripts/ProxmoxVED/raw/branch/main/misc/alpine-install.func)"
    else
        export FUNCTIONS_FILE_PATH="$(curl -fsSL https://git.community-scripts.org/community-scripts/ProxmoxVED/raw/branch/main/misc/install.func)"
    fi

    # Core exports for install.func
    export DIAGNOSTICS="$DIAGNOSTICS"
    export RANDOM_UUID="$RANDOM_UUID"
    export SESSION_ID="$SESSION_ID"
    export CACHER="$APT_CACHER"
    export CACHER_IP="$APT_CACHER_IP"
    export tz="$timezone"
    export APPLICATION="$APP"
    export app="$NSAPP"
    export PASSWORD="$PW"
    export VERBOSE="$VERBOSE"
    export SSH_ROOT="${SSH}"
    export SSH_AUTHORIZED_KEY
    export CTID="$CT_ID"
    export CTTYPE="$CT_TYPE"
    export ENABLE_FUSE="$ENABLE_FUSE"
    export ENABLE_TUN="$ENABLE_TUN"
    export PCT_OSTYPE="$var_os"
    export PCT_OSVERSION="$var_version"
    export PCT_DISK_SIZE="$DISK_SIZE"

    # DEV_MODE exports (optional, for debugging)
    export BUILD_LOG="$BUILD_LOG"
    export INSTALL_LOG="/root/.install-${SESSION_ID}.log"
    export dev_mode="${dev_mode:-}"
    export DEV_MODE_MOTD="${DEV_MODE_MOTD:-false}"
    export DEV_MODE_KEEP="${DEV_MODE_KEEP:-false}"
    export DEV_MODE_TRACE="${DEV_MODE_TRACE:-false}"
    export DEV_MODE_PAUSE="${DEV_MODE_PAUSE:-false}"
    export DEV_MODE_BREAKPOINT="${DEV_MODE_BREAKPOINT:-false}"
    export DEV_MODE_LOGS="${DEV_MODE_LOGS:-false}"
    export DEV_MODE_DRYRUN="${DEV_MODE_DRYRUN:-false}"

    # Build PCT_OPTIONS as multi-line string
    PCT_OPTIONS_STRING="  -features $FEATURES
  -hostname $HN
  -tags $TAGS"

    # Add storage if specified
    if [ -n "$SD" ]; then
        PCT_OPTIONS_STRING="$PCT_OPTIONS_STRING
  $SD"
    fi

    # Add nameserver if specified
    if [ -n "$NS" ]; then
        PCT_OPTIONS_STRING="$PCT_OPTIONS_STRING
  $NS"
    fi

    # Network configuration
    PCT_OPTIONS_STRING="$PCT_OPTIONS_STRING
  $NET_STRING
  -onboot 1
  -cores $CORE_COUNT
  -memory $RAM_SIZE
  -unprivileged $CT_TYPE"

    # Protection flag (if var_protection was set)
    if [ "${PROTECT_CT:-}" == "1" ] || [ "${PROTECT_CT:-}" == "yes" ]; then
        PCT_OPTIONS_STRING="$PCT_OPTIONS_STRING
  -protection 1"
    fi

    # Timezone flag (if var_timezone was set)
    if [ -n "${CT_TIMEZONE:-}" ]; then
        PCT_OPTIONS_STRING="$PCT_OPTIONS_STRING
  -timezone $CT_TIMEZONE"
    fi

    # Password (already formatted)
    if [ -n "$PW" ]; then
        PCT_OPTIONS_STRING="$PCT_OPTIONS_STRING
  $PW"
    fi

    # Export as string (this works, unlike arrays!)
    export PCT_OPTIONS="$PCT_OPTIONS_STRING"
    export TEMPLATE_STORAGE="${var_template_storage:-}"
    export CONTAINER_STORAGE="${var_container_storage:-}"

    # # DEBUG: Show final PCT_OPTIONS being exported
    # echo "[DEBUG] PCT_OPTIONS to be exported:"
    # echo "$PCT_OPTIONS" | sed 's/^/  /'
    # echo "[DEBUG] Calling create_lxc_container..."

    create_lxc_container || exit $?

    LXC_CONFIG="/etc/pve/lxc/${CTID}.conf"

    # ============================================================================
    # GPU/USB PASSTHROUGH CONFIGURATION
    # ============================================================================

    # List of applications that benefit from GPU acceleration
    GPU_APPS=(
        "immich" "channels" "emby" "ersatztv" "frigate"
        "jellyfin" "plex" "scrypted" "tdarr" "unmanic"
        "ollama" "fileflows" "open-webui" "tunarr" "debian"
        "handbrake" "sunshine" "moonlight" "kodi" "stremio"
        "viseron"
    )

    # Check if app needs GPU
    is_gpu_app() {
        local app="${1,,}"
        for gpu_app in "${GPU_APPS[@]}"; do
            [[ "$app" == "${gpu_app,,}" ]] && return 0
        done
        return 1
    }

    # Detect all available GPU devices
    detect_gpu_devices() {
        INTEL_DEVICES=()
        AMD_DEVICES=()
        NVIDIA_DEVICES=()

        # Store PCI info to avoid multiple calls
        local pci_vga_info=$(lspci -nn 2>/dev/null | grep -E "VGA|Display|3D")

        # Check for Intel GPU - look for Intel vendor ID [8086]
        if echo "$pci_vga_info" | grep -q "\[8086:"; then
            msg_custom "🎮" "${BL}" "Detected Intel GPU"
            if [[ -d /dev/dri ]]; then
                for d in /dev/dri/renderD* /dev/dri/card*; do
                    [[ -e "$d" ]] && INTEL_DEVICES+=("$d")
                done
            fi
        fi

        # Check for AMD GPU - look for AMD vendor IDs [1002] (AMD/ATI) or [1022] (AMD)
        if echo "$pci_vga_info" | grep -qE "\[1002:|\[1022:"; then
            msg_custom "🎮" "${RD}" "Detected AMD GPU"
            if [[ -d /dev/dri ]]; then
                # Only add if not already claimed by Intel
                if [[ ${#INTEL_DEVICES[@]} -eq 0 ]]; then
                    for d in /dev/dri/renderD* /dev/dri/card*; do
                        [[ -e "$d" ]] && AMD_DEVICES+=("$d")
                    done
                fi
            fi
        fi

        # Check for NVIDIA GPU - look for NVIDIA vendor ID [10de]
        if echo "$pci_vga_info" | grep -q "\[10de:"; then
            msg_custom "🎮" "${GN}" "Detected NVIDIA GPU"

            # Simple passthrough - just bind /dev/nvidia* devices if they exist
            for d in /dev/nvidia* /dev/nvidiactl /dev/nvidia-modeset /dev/nvidia-uvm /dev/nvidia-uvm-tools; do
                [[ -e "$d" ]] && NVIDIA_DEVICES+=("$d")
            done

            if [[ ${#NVIDIA_DEVICES[@]} -gt 0 ]]; then
                msg_custom "🎮" "${GN}" "Found ${#NVIDIA_DEVICES[@]} NVIDIA device(s) for passthrough"
            else
                msg_warn "NVIDIA GPU detected via PCI but no /dev/nvidia* devices found"
                msg_custom "ℹ️" "${YW}" "Skipping NVIDIA passthrough (host drivers may not be loaded)"
            fi
        fi

        # Debug output
        msg_debug "Intel devices: ${INTEL_DEVICES[*]}"
        msg_debug "AMD devices: ${AMD_DEVICES[*]}"
        msg_debug "NVIDIA devices: ${NVIDIA_DEVICES[*]}"
    }

    # Configure USB passthrough for privileged containers
    configure_usb_passthrough() {
        if [[ "$CT_TYPE" != "0" ]]; then
            return 0
        fi

        msg_info "Configuring automatic USB passthrough (privileged container)"
        cat <<EOF >>"$LXC_CONFIG"
# Automatic USB passthrough (privileged container)
lxc.cgroup2.devices.allow: a
lxc.cap.drop:
lxc.cgroup2.devices.allow: c 188:* rwm
lxc.cgroup2.devices.allow: c 189:* rwm
lxc.mount.entry: /dev/serial/by-id  dev/serial/by-id  none bind,optional,create=dir
lxc.mount.entry: /dev/ttyUSB0       dev/ttyUSB0       none bind,optional,create=file
lxc.mount.entry: /dev/ttyUSB1       dev/ttyUSB1       none bind,optional,create=file
lxc.mount.entry: /dev/ttyACM0       dev/ttyACM0       none bind,optional,create=file
lxc.mount.entry: /dev/ttyACM1       dev/ttyACM1       none bind,optional,create=file
EOF
        msg_ok "USB passthrough configured"
    }

    # Configure GPU passthrough
    configure_gpu_passthrough() {
        # Skip if not a GPU app and not privileged
        if [[ "$CT_TYPE" != "0" ]] && ! is_gpu_app "$APP"; then
            return 0
        fi

        detect_gpu_devices

        # Count available GPU types
        local gpu_count=0
        local available_gpus=()

        if [[ ${#INTEL_DEVICES[@]} -gt 0 ]]; then
            available_gpus+=("INTEL")
            gpu_count=$((gpu_count + 1))
        fi

        if [[ ${#AMD_DEVICES[@]} -gt 0 ]]; then
            available_gpus+=("AMD")
            gpu_count=$((gpu_count + 1))
        fi

        if [[ ${#NVIDIA_DEVICES[@]} -gt 0 ]]; then
            available_gpus+=("NVIDIA")
            gpu_count=$((gpu_count + 1))
        fi

        if [[ $gpu_count -eq 0 ]]; then
            msg_custom "ℹ️" "${YW}" "No GPU devices found for passthrough"
            return 0
        fi

        local selected_gpu=""

        if [[ $gpu_count -eq 1 ]]; then
            # Automatic selection for single GPU
            selected_gpu="${available_gpus[0]}"
            msg_ok "Automatically configuring ${selected_gpu} GPU passthrough"
        else
            # Multiple GPUs - ask user
            echo -e "\n${INFO} Multiple GPU types detected:"
            for gpu in "${available_gpus[@]}"; do
                echo "  - $gpu"
            done
            read -rp "Which GPU type to passthrough? (${available_gpus[*]}): " selected_gpu
            selected_gpu="${selected_gpu^^}"

            # Validate selection
            local valid=0
            for gpu in "${available_gpus[@]}"; do
                [[ "$selected_gpu" == "$gpu" ]] && valid=1
            done

            if [[ $valid -eq 0 ]]; then
                msg_warn "Invalid selection. Skipping GPU passthrough."
                return 0
            fi
        fi

        # Apply passthrough configuration based on selection
        local dev_idx=0

        case "$selected_gpu" in
        INTEL | AMD)
            local devices=()
            [[ "$selected_gpu" == "INTEL" ]] && devices=("${INTEL_DEVICES[@]}")
            [[ "$selected_gpu" == "AMD" ]] && devices=("${AMD_DEVICES[@]}")

            # Use pct set to add devices with proper dev0/dev1 format
            # GIDs will be detected and set after container starts
            local dev_index=0
            for dev in "${devices[@]}"; do
                # Add to config using pct set (will be visible in GUI)
                echo "dev${dev_index}: ${dev},gid=44" >>"$LXC_CONFIG"
                dev_index=$((dev_index + 1))
            done

            export GPU_TYPE="$selected_gpu"
            msg_ok "${selected_gpu} GPU passthrough configured (${#devices[@]} devices)"
            ;;

        NVIDIA)
            if [[ ${#NVIDIA_DEVICES[@]} -eq 0 ]]; then
                msg_warn "No NVIDIA devices available for passthrough"
                return 0
            fi

            # Use pct set for NVIDIA devices
            local dev_index=0
            for dev in "${NVIDIA_DEVICES[@]}"; do
                echo "dev${dev_index}: ${dev},gid=44" >>"$LXC_CONFIG"
                dev_index=$((dev_index + 1))
            done

            export GPU_TYPE="NVIDIA"
            msg_ok "NVIDIA GPU passthrough configured (${#NVIDIA_DEVICES[@]} devices) - install drivers in container if needed"
            ;;
        esac
    }

    # Additional device passthrough
    configure_additional_devices() {
        # TUN device passthrough
        if [ "$ENABLE_TUN" == "yes" ]; then
            cat <<EOF >>"$LXC_CONFIG"
lxc.cgroup2.devices.allow: c 10:200 rwm
lxc.mount.entry: /dev/net/tun dev/net/tun none bind,create=file
EOF
        fi

        # Coral TPU passthrough
        if [[ -e /dev/apex_0 ]]; then
            msg_custom "🔌" "${BL}" "Detected Coral TPU - configuring passthrough"
            echo "lxc.mount.entry: /dev/apex_0 dev/apex_0 none bind,optional,create=file" >>"$LXC_CONFIG"
        fi
    }

    # Execute pre-start configurations
    configure_usb_passthrough
    configure_gpu_passthrough
    configure_additional_devices

    # ============================================================================
    # START CONTAINER AND INSTALL USERLAND
    # ============================================================================

    msg_info "Starting LXC Container"
    pct start "$CTID"

    # Wait for container to be running
    for i in {1..10}; do
        if pct status "$CTID" | grep -q "status: running"; then
            msg_ok "Started LXC Container"
            break
        fi
        sleep 1
        if [ "$i" -eq 10 ]; then
            msg_error "LXC Container did not reach running state"
            exit 1
        fi
    done

    # Wait for network (skip for Alpine initially)
    if [ "$var_os" != "alpine" ]; then
        msg_info "Waiting for network in LXC container"

        # Wait for IP assignment (IPv4 or IPv6)
        local ip_in_lxc=""
        for i in {1..20}; do
            # Try IPv4 first
            ip_in_lxc=$(pct exec "$CTID" -- ip -4 addr show dev eth0 2>/dev/null | awk '/inet / {print $2}' | cut -d/ -f1)
            # Fallback to IPv6 if IPv4 not available
            if [ -z "$ip_in_lxc" ]; then
                ip_in_lxc=$(pct exec "$CTID" -- ip -6 addr show dev eth0 scope global 2>/dev/null | awk '/inet6 / {print $2}' | cut -d/ -f1 | head -n1)
            fi
            [ -n "$ip_in_lxc" ] && break
            sleep 1
        done

        if [ -z "$ip_in_lxc" ]; then
            msg_error "No IP assigned to CT $CTID after 20s"
            echo -e "${YW}Troubleshooting:${CL}"
            echo "  • Verify bridge ${BRG} exists and has connectivity"
            echo "  • Check if DHCP server is reachable (if using DHCP)"
            echo "  • Verify static IP configuration (if using static IP)"
            echo "  • Check Proxmox firewall rules"
            echo "  • If using Tailscale: Disable MagicDNS temporarily"
            exit 1
        fi

        # Verify basic connectivity (ping test)
        local ping_success=false
        for retry in {1..3}; do
            if pct exec "$CTID" -- ping -c 1 -W 2 1.1.1.1 &>/dev/null ||
                pct exec "$CTID" -- ping -c 1 -W 2 8.8.8.8 &>/dev/null ||
                pct exec "$CTID" -- ping6 -c 1 -W 2 2606:4700:4700::1111 &>/dev/null; then
                ping_success=true
                break
            fi
            sleep 2
        done

        if [ "$ping_success" = false ]; then
            msg_warn "Network configured (IP: $ip_in_lxc) but connectivity test failed"
            echo -e "${YW}Container may have limited internet access. Installation will continue...${CL}"
        else
            msg_ok "Network in LXC is reachable (ping)"
        fi
    fi

    # Function to get correct GID inside container
    get_container_gid() {
        local group="$1"
        local gid=$(pct exec "$CTID" -- getent group "$group" 2>/dev/null | cut -d: -f3)
        echo "${gid:-44}" # Default to 44 if not found
    }

    fix_gpu_gids

    # Continue with standard container setup
    msg_info "Customizing LXC Container"

    # # Install GPU userland if configured
    # if [[ "${ENABLE_VAAPI:-0}" == "1" ]]; then
    #   install_gpu_userland "VAAPI"
    # fi

    # if [[ "${ENABLE_NVIDIA:-0}" == "1" ]]; then
    #   install_gpu_userland "NVIDIA"
    # fi

    # Continue with standard container setup
    if [ "$var_os" == "alpine" ]; then
        sleep 3
        pct exec "$CTID" -- /bin/sh -c 'cat <<EOF >/etc/apk/repositories
http://dl-cdn.alpinelinux.org/alpine/latest-stable/main
http://dl-cdn.alpinelinux.org/alpine/latest-stable/community
EOF'
        pct exec "$CTID" -- ash -c "apk add bash newt curl openssh nano mc ncurses jq >/dev/null"
    else
        sleep 3
        pct exec "$CTID" -- bash -c "sed -i '/$LANG/ s/^# //' /etc/locale.gen"
        pct exec "$CTID" -- bash -c "locale_line=\$(grep -v '^#' /etc/locale.gen | grep -E '^[a-zA-Z]' | awk '{print \$1}' | head -n 1) && \
    echo LANG=\$locale_line >/etc/default/locale && \
    locale-gen >/dev/null && \
    export LANG=\$locale_line"

        if [[ -z "${tz:-}" ]]; then
            tz=$(timedatectl show --property=Timezone --value 2>/dev/null || echo "Etc/UTC")
        fi

        if pct exec "$CTID" -- test -e "/usr/share/zoneinfo/$tz"; then
            # Set timezone using symlink (Debian 13+ compatible)
            # Create /etc/timezone for backwards compatibility with older scripts
            pct exec "$CTID" -- bash -c "tz='$tz'; ln -sf \"/usr/share/zoneinfo/\$tz\" /etc/localtime && echo \"\$tz\" >/etc/timezone || true"
        else
            msg_warn "Skipping timezone setup – zone '$tz' not found in container"
        fi

        pct exec "$CTID" -- bash -c "apt-get update >/dev/null && apt-get install -y sudo curl mc gnupg2 jq >/dev/null" || {
            msg_error "apt-get base packages installation failed"
            exit 1
        }
    fi

    msg_ok "Customized LXC Container"

    # Install SSH keys
    install_ssh_keys_into_ct

    # Run application installer
    # NOTE: We disable error handling here because:
    # 1. Container errors are caught by error_handler INSIDE container
    # 2. Container creates flag file with exit code
    # 3. We read flag file and handle cleanup manually below
    # 4. We DON'T want host error_handler to fire for lxc-attach command itself

    set +Eeuo pipefail # Disable ALL error handling temporarily
    trap - ERR         # Remove ERR trap completely

    lxc-attach -n "$CTID" -- bash -c "$(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVED/main/install/${var_install}.sh)"
    local lxc_exit=$?

    set -Eeuo pipefail       # Re-enable error handling
    trap 'error_handler' ERR # Restore ERR trap

    # Check for error flag file in container (more reliable than lxc-attach exit code)
    local install_exit_code=0
    if [[ -n "${SESSION_ID:-}" ]]; then
        local error_flag="/root/.install-${SESSION_ID}.failed"
        if pct exec "$CTID" -- test -f "$error_flag" 2>/dev/null; then
            install_exit_code=$(pct exec "$CTID" -- cat "$error_flag" 2>/dev/null || echo "1")
            pct exec "$CTID" -- rm -f "$error_flag" 2>/dev/null || true
        fi
    fi

    # Fallback to lxc-attach exit code if no flag file
    if [[ $install_exit_code -eq 0 && $lxc_exit -ne 0 ]]; then
        install_exit_code=$lxc_exit
    fi

    # Installation failed?
    if [[ $install_exit_code -ne 0 ]]; then
        msg_error "Installation failed in container ${CTID} (exit code: ${install_exit_code})"

        # Copy both logs from container before potential deletion
        local build_log_copied=false
        local install_log_copied=false

        if [[ -n "$CTID" && -n "${SESSION_ID:-}" ]]; then
            # Copy BUILD_LOG (creation log) if it exists
            if [[ -f "${BUILD_LOG}" ]]; then
                cp "${BUILD_LOG}" "/tmp/create-lxc-${CTID}-${SESSION_ID}.log" 2>/dev/null && build_log_copied=true
            fi

            # Copy INSTALL_LOG from container
            if pct pull "$CTID" "/root/.install-${SESSION_ID}.log" "/tmp/install-lxc-${CTID}-${SESSION_ID}.log" 2>/dev/null; then
                install_log_copied=true
            fi

            # Show available logs
            echo ""
            [[ "$build_log_copied" == true ]] && echo -e "${GN}✔${CL} Container creation log: ${BL}/tmp/create-lxc-${CTID}-${SESSION_ID}.log${CL}"
            [[ "$install_log_copied" == true ]] && echo -e "${GN}✔${CL} Installation log: ${BL}/tmp/install-lxc-${CTID}-${SESSION_ID}.log${CL}"
        fi

        # Dev mode: Keep container or open breakpoint shell
        if [[ "${DEV_MODE_KEEP:-false}" == "true" ]]; then
            msg_dev "Keep mode active - container ${CTID} preserved"
            return 0
        elif [[ "${DEV_MODE_BREAKPOINT:-false}" == "true" ]]; then
            msg_dev "Breakpoint mode - opening shell in container ${CTID}"
            echo -e "${YW}Type 'exit' to return to host${CL}"
            pct enter "$CTID"
            echo ""
            echo -en "${YW}Container ${CTID} still running. Remove now? (y/N): ${CL}"
            if read -r response && [[ "$response" =~ ^[Yy]$ ]]; then
                pct stop "$CTID" &>/dev/null || true
                pct destroy "$CTID" &>/dev/null || true
                msg_ok "Container ${CTID} removed"
            else
                msg_dev "Container ${CTID} kept for debugging"
            fi
            exit $install_exit_code
        fi

        # Prompt user for cleanup with 60s timeout (plain echo - no msg_info to avoid spinner)
        echo ""
        echo -en "${YW}Remove broken container ${CTID}? (Y/n) [auto-remove in 60s]: ${CL}"

        if read -t 60 -r response; then
            if [[ -z "$response" || "$response" =~ ^[Yy]$ ]]; then
                # Remove container
                echo -e "\n${TAB}${HOLD}${YW}Removing container ${CTID}${CL}"
                pct stop "$CTID" &>/dev/null || true
                pct destroy "$CTID" &>/dev/null || true
                echo -e "${BFR}${CM}${GN}Container ${CTID} removed${CL}"
            elif [[ "$response" =~ ^[Nn]$ ]]; then
                echo -e "\n${TAB}${YW}Container ${CTID} kept for debugging${CL}"

                # Dev mode: Setup MOTD/SSH for debugging access to broken container
                if [[ "${DEV_MODE_MOTD:-false}" == "true" ]]; then
                    echo -e "${TAB}${HOLD}${DGN}Setting up MOTD and SSH for debugging...${CL}"
                    if pct exec "$CTID" -- bash -c "
            source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVED/main/misc/install.func)
            declare -f motd_ssh >/dev/null 2>&1 && motd_ssh || true
          " >/dev/null 2>&1; then
                        local ct_ip=$(pct exec "$CTID" ip a s dev eth0 2>/dev/null | awk '/inet / {print $2}' | cut -d/ -f1)
                        echo -e "${BFR}${CM}${GN}MOTD/SSH ready - SSH into container: ssh root@${ct_ip}${CL}"
                    fi
                fi
            fi
        else
            # Timeout - auto-remove
            echo -e "\n${YW}No response - auto-removing container${CL}"
            echo -e "${TAB}${HOLD}${YW}Removing container ${CTID}${CL}"
            pct stop "$CTID" &>/dev/null || true
            pct destroy "$CTID" &>/dev/null || true
            echo -e "${BFR}${CM}${GN}Container ${CTID} removed${CL}"
        fi

        exit $install_exit_code
    fi
}

destroy_lxc() {
    if [[ -z "$CT_ID" ]]; then
        msg_error "No CT_ID found. Nothing to remove."
        return 1
    fi

    # Abbruch bei Ctrl-C / Ctrl-D / ESC
    trap 'echo; msg_error "Aborted by user (SIGINT/SIGQUIT)"; return 130' INT QUIT

    local prompt
    if ! read -rp "Remove this Container? <y/N> " prompt; then
        # read gibt != 0 zurück bei Ctrl-D/ESC
        msg_error "Aborted input (Ctrl-D/ESC)"
        return 130
    fi

    case "${prompt,,}" in
    y | yes)
        if pct stop "$CT_ID" &>/dev/null && pct destroy "$CT_ID" &>/dev/null; then
            msg_ok "Removed Container $CT_ID"
        else
            msg_error "Failed to remove Container $CT_ID"
            return 1
        fi
        ;;
    "" | n | no)
        msg_custom "ℹ️" "${BL}" "Container was not removed."
        ;;
    *)
        msg_warn "Invalid response. Container was not removed."
        ;;
    esac
}

# ------------------------------------------------------------------------------
# Storage discovery / selection helpers
# ------------------------------------------------------------------------------
# ===== Storage discovery / selection helpers (ported from create_lxc.sh) =====
resolve_storage_preselect() {
    local class="$1" preselect="$2" required_content=""
    case "$class" in
    template) required_content="vztmpl" ;;
    container) required_content="rootdir" ;;
    *) return 1 ;;
    esac
    [[ -z "$preselect" ]] && return 1
    if ! pvesm status -content "$required_content" | awk 'NR>1{print $1}' | grep -qx -- "$preselect"; then
        msg_warn "Preselected storage '${preselect}' does not support content '${required_content}' (or not found)"
        return 1
    fi

    local line total used free
    line="$(pvesm status | awk -v s="$preselect" 'NR>1 && $1==s {print $0}')"
    if [[ -z "$line" ]]; then
        STORAGE_INFO="n/a"
    else
        total="$(awk '{print $4}' <<<"$line")"
        used="$(awk '{print $5}' <<<"$line")"
        free="$(awk '{print $6}' <<<"$line")"
        local total_h used_h free_h
        if command -v numfmt >/dev/null 2>&1; then
            total_h="$(numfmt --to=iec --suffix=B --format %.1f "$total" 2>/dev/null || echo "$total")"
            used_h="$(numfmt --to=iec --suffix=B --format %.1f "$used" 2>/dev/null || echo "$used")"
            free_h="$(numfmt --to=iec --suffix=B --format %.1f "$free" 2>/dev/null || echo "$free")"
            STORAGE_INFO="Free: ${free_h}  Used: ${used_h}"
        else
            STORAGE_INFO="Free: ${free}  Used: ${used}"
        fi
    fi
    STORAGE_RESULT="$preselect"
    return 0
}

fix_gpu_gids() {
    if [[ -z "${GPU_TYPE:-}" ]]; then
        return 0
    fi

    msg_info "Detecting and setting correct GPU group IDs"

    # Get actual GIDs from container
    local video_gid=$(pct exec "$CTID" -- sh -c "getent group video 2>/dev/null | cut -d: -f3")
    local render_gid=$(pct exec "$CTID" -- sh -c "getent group render 2>/dev/null | cut -d: -f3")

    # Create groups if they don't exist
    if [[ -z "$video_gid" ]]; then
        pct exec "$CTID" -- sh -c "groupadd -r video 2>/dev/null || true" >/dev/null 2>&1
        video_gid=$(pct exec "$CTID" -- sh -c "getent group video 2>/dev/null | cut -d: -f3")
        [[ -z "$video_gid" ]] && video_gid="44"
    fi

    if [[ -z "$render_gid" ]]; then
        pct exec "$CTID" -- sh -c "groupadd -r render 2>/dev/null || true" >/dev/null 2>&1
        render_gid=$(pct exec "$CTID" -- sh -c "getent group render 2>/dev/null | cut -d: -f3")
        [[ -z "$render_gid" ]] && render_gid="104"
    fi

    # Stop container to update config
    pct stop "$CTID" >/dev/null 2>&1
    sleep 1

    # Update dev entries with correct GIDs
    sed -i.bak -E "s|(dev[0-9]+: /dev/dri/renderD[0-9]+),gid=[0-9]+|\1,gid=${render_gid}|g" "$LXC_CONFIG"
    sed -i -E "s|(dev[0-9]+: /dev/dri/card[0-9]+),gid=[0-9]+|\1,gid=${video_gid}|g" "$LXC_CONFIG"

    # Restart container
    pct start "$CTID" >/dev/null 2>&1
    sleep 2

    msg_ok "GPU passthrough configured (video:${video_gid}, render:${render_gid})"

    # For privileged containers: also fix permissions inside container
    if [[ "$CT_TYPE" == "0" ]]; then
        pct exec "$CTID" -- bash -c "
      if [ -d /dev/dri ]; then
        for dev in /dev/dri/*; do
          if [ -e \"\$dev\" ]; then
            if [[ \"\$dev\" =~ renderD ]]; then
              chgrp ${render_gid} \"\$dev\" 2>/dev/null || true
            else
              chgrp ${video_gid} \"\$dev\" 2>/dev/null || true
            fi
            chmod 660 \"\$dev\" 2>/dev/null || true
          fi
        done
      fi
    " >/dev/null 2>&1
    fi
}

check_storage_support() {
    local CONTENT="$1" VALID=0
    while IFS= read -r line; do
        local STORAGE_NAME
        STORAGE_NAME=$(awk '{print $1}' <<<"$line")
        [[ -n "$STORAGE_NAME" ]] && VALID=1
    done < <(pvesm status -content "$CONTENT" 2>/dev/null | awk 'NR>1')
    [[ $VALID -eq 1 ]]
}

select_storage() {
    local CLASS=$1 CONTENT CONTENT_LABEL
    case $CLASS in
    container)
        CONTENT='rootdir'
        CONTENT_LABEL='Container'
        ;;
    template)
        CONTENT='vztmpl'
        CONTENT_LABEL='Container template'
        ;;
    iso)
        CONTENT='iso'
        CONTENT_LABEL='ISO image'
        ;;
    images)
        CONTENT='images'
        CONTENT_LABEL='VM Disk image'
        ;;
    backup)
        CONTENT='backup'
        CONTENT_LABEL='Backup'
        ;;
    snippets)
        CONTENT='snippets'
        CONTENT_LABEL='Snippets'
        ;;
    *)
        msg_error "Invalid storage class '$CLASS'"
        return 1
        ;;
    esac

    declare -A STORAGE_MAP
    local -a MENU=()
    local COL_WIDTH=0

    while read -r TAG TYPE _ TOTAL USED FREE _; do
        [[ -n "$TAG" && -n "$TYPE" ]] || continue
        local DISPLAY="${TAG} (${TYPE})"
        local USED_FMT=$(numfmt --to=iec --from-unit=K --format %.1f <<<"$USED")
        local FREE_FMT=$(numfmt --to=iec --from-unit=K --format %.1f <<<"$FREE")
        local INFO="Free: ${FREE_FMT}B  Used: ${USED_FMT}B"
        STORAGE_MAP["$DISPLAY"]="$TAG"
        MENU+=("$DISPLAY" "$INFO" "OFF")
        ((${#DISPLAY} > COL_WIDTH)) && COL_WIDTH=${#DISPLAY}
    done < <(pvesm status -content "$CONTENT" | awk 'NR>1')

    if [[ ${#MENU[@]} -eq 0 ]]; then
        msg_error "No storage found for content type '$CONTENT'."
        return 2
    fi

    if [[ $((${#MENU[@]} / 3)) -eq 1 ]]; then
        STORAGE_RESULT="${STORAGE_MAP[${MENU[0]}]}"
        STORAGE_INFO="${MENU[1]}"
        return 0
    fi

    local WIDTH=$((COL_WIDTH + 42))
    while true; do
        local DISPLAY_SELECTED
        DISPLAY_SELECTED=$(whiptail --backtitle "[dev] Proxmox VE Helper Scripts" \
            --title "Storage Pools" \
            --radiolist "Which storage pool for ${CONTENT_LABEL,,}?\n(Spacebar to select)" \
            16 "$WIDTH" 6 "${MENU[@]}" 3>&1 1>&2 2>&3) || { exit_script; }

        DISPLAY_SELECTED=$(sed 's/[[:space:]]*$//' <<<"$DISPLAY_SELECTED")
        if [[ -z "$DISPLAY_SELECTED" || -z "${STORAGE_MAP[$DISPLAY_SELECTED]+_}" ]]; then
            whiptail --msgbox "No valid storage selected. Please try again." 8 58
            continue
        fi
        STORAGE_RESULT="${STORAGE_MAP[$DISPLAY_SELECTED]}"
        for ((i = 0; i < ${#MENU[@]}; i += 3)); do
            if [[ "${MENU[$i]}" == "$DISPLAY_SELECTED" ]]; then
                STORAGE_INFO="${MENU[$i + 1]}"
                break
            fi
        done
        return 0
    done
}

create_lxc_container() {
    # ------------------------------------------------------------------------------
    # Optional verbose mode (debug tracing)
    # ------------------------------------------------------------------------------
    if [[ "${CREATE_LXC_VERBOSE:-no}" == "yes" ]]; then set -x; fi

    # ------------------------------------------------------------------------------
    # Helpers (dynamic versioning / template parsing)
    # ------------------------------------------------------------------------------
    pkg_ver() { dpkg-query -W -f='${Version}\n' "$1" 2>/dev/null || echo ""; }
    pkg_cand() { apt-cache policy "$1" 2>/dev/null | awk '/Candidate:/ {print $2}'; }

    ver_ge() { dpkg --compare-versions "$1" ge "$2"; }
    ver_gt() { dpkg --compare-versions "$1" gt "$2"; }
    ver_lt() { dpkg --compare-versions "$1" lt "$2"; }

    # Extract Debian OS minor from template name: debian-13-standard_13.1-1_amd64.tar.zst => "13.1"
    parse_template_osver() { sed -n 's/.*_\([0-9][0-9]*\(\.[0-9]\+\)\?\)-.*/\1/p' <<<"$1"; }

    # Offer upgrade for pve-container/lxc-pve if candidate > installed; optional auto-retry pct create
    # Returns:
    #   0 = no upgrade needed
    #   1 = upgraded (and if do_retry=yes and retry succeeded, creation done)
    #   2 = user declined
    #   3 = upgrade attempted but failed OR retry failed
    offer_lxc_stack_upgrade_and_maybe_retry() {
        local do_retry="${1:-no}" # yes|no
        local _pvec_i _pvec_c _lxcp_i _lxcp_c need=0

        _pvec_i="$(pkg_ver pve-container)"
        _lxcp_i="$(pkg_ver lxc-pve)"
        _pvec_c="$(pkg_cand pve-container)"
        _lxcp_c="$(pkg_cand lxc-pve)"

        if [[ -n "$_pvec_c" && "$_pvec_c" != "none" ]]; then
            ver_gt "$_pvec_c" "${_pvec_i:-0}" && need=1
        fi
        if [[ -n "$_lxcp_c" && "$_lxcp_c" != "none" ]]; then
            ver_gt "$_lxcp_c" "${_lxcp_i:-0}" && need=1
        fi
        if [[ $need -eq 0 ]]; then
            msg_debug "No newer candidate for pve-container/lxc-pve (installed=$_pvec_i/$_lxcp_i, cand=$_pvec_c/$_lxcp_c)"
            return 0
        fi

        echo
        echo "An update for the Proxmox LXC stack is available:"
        echo "  pve-container: installed=${_pvec_i:-n/a}  candidate=${_pvec_c:-n/a}"
        echo "  lxc-pve     : installed=${_lxcp_i:-n/a}  candidate=${_lxcp_c:-n/a}"
        echo
        read -rp "Do you want to upgrade now? [y/N] " _ans
        case "${_ans,,}" in
        y | yes)
            msg_info "Upgrading Proxmox LXC stack (pve-container, lxc-pve)"
            if $STD apt-get update && $STD apt-get install -y --only-upgrade pve-container lxc-pve; then
                msg_ok "LXC stack upgraded."
                if [[ "$do_retry" == "yes" ]]; then
                    msg_info "Retrying container creation after upgrade"
                    if pct create "$CTID" "${TEMPLATE_STORAGE}:vztmpl/${TEMPLATE}" $PCT_OPTIONS >>"$LOGFILE" 2>&1; then
                        msg_ok "Container created successfully after upgrade."
                        return 0
                    else
                        msg_error "pct create still failed after upgrade. See $LOGFILE"
                        return 3
                    fi
                fi
                return 1
            else
                msg_error "Upgrade failed. Please check APT output."
                return 3
            fi
            ;;
        *) return 2 ;;
        esac
    }

    # ------------------------------------------------------------------------------
    # Required input variables
    # ------------------------------------------------------------------------------
    [[ "${CTID:-}" ]] || {
        msg_error "You need to set 'CTID' variable."
        exit 203
    }
    [[ "${PCT_OSTYPE:-}" ]] || {
        msg_error "You need to set 'PCT_OSTYPE' variable."
        exit 204
    }

    msg_debug "CTID=$CTID"
    msg_debug "PCT_OSTYPE=$PCT_OSTYPE"
    msg_debug "PCT_OSVERSION=${PCT_OSVERSION:-default}"

    # ID checks
    [[ "$CTID" -ge 100 ]] || {
        msg_error "ID cannot be less than 100."
        exit 205
    }
    if qm status "$CTID" &>/dev/null || pct status "$CTID" &>/dev/null; then
        echo -e "ID '$CTID' is already in use."
        unset CTID
        msg_error "Cannot use ID that is already in use."
        exit 206
    fi

    # Storage capability check
    check_storage_support "rootdir" || {
        msg_error "No valid storage found for 'rootdir' [Container]"
        exit 1
    }
    check_storage_support "vztmpl" || {
        msg_error "No valid storage found for 'vztmpl' [Template]"
        exit 1
    }

    # Template storage selection
    if resolve_storage_preselect template "${TEMPLATE_STORAGE:-}"; then
        TEMPLATE_STORAGE="$STORAGE_RESULT"
        TEMPLATE_STORAGE_INFO="$STORAGE_INFO"
        msg_ok "Storage ${BL}${TEMPLATE_STORAGE}${CL} (${TEMPLATE_STORAGE_INFO}) [Template]"
    else
        while true; do
            if [[ -z "${var_template_storage:-}" ]]; then
                if select_storage template; then
                    TEMPLATE_STORAGE="$STORAGE_RESULT"
                    TEMPLATE_STORAGE_INFO="$STORAGE_INFO"
                    msg_ok "Storage ${BL}${TEMPLATE_STORAGE}${CL} (${TEMPLATE_STORAGE_INFO}) [Template]"
                    break
                fi
            fi
        done
    fi

    # Container storage selection
    if resolve_storage_preselect container "${CONTAINER_STORAGE:-}"; then
        CONTAINER_STORAGE="$STORAGE_RESULT"
        CONTAINER_STORAGE_INFO="$STORAGE_INFO"
        msg_ok "Storage ${BL}${CONTAINER_STORAGE}${CL} (${CONTAINER_STORAGE_INFO}) [Container]"
    else
        if [[ -z "${var_container_storage:-}" ]]; then
            if select_storage container; then
                CONTAINER_STORAGE="$STORAGE_RESULT"
                CONTAINER_STORAGE_INFO="$STORAGE_INFO"
                msg_ok "Storage ${BL}${CONTAINER_STORAGE}${CL} (${CONTAINER_STORAGE_INFO}) [Container]"
            fi
        fi
    fi

    msg_info "Validating storage '$CONTAINER_STORAGE'"
    STORAGE_TYPE=$(grep -E "^[^:]+: $CONTAINER_STORAGE$" /etc/pve/storage.cfg | cut -d: -f1 | head -1)

    case "$STORAGE_TYPE" in
    iscsidirect) exit 212 ;;
    iscsi | zfs) exit 213 ;;
    cephfs) exit 219 ;;
    pbs) exit 224 ;;
    linstor | rbd | nfs | cifs)
        pvesm status -storage "$CONTAINER_STORAGE" &>/dev/null || exit 217
        ;;
    esac

    pvesm status -content rootdir 2>/dev/null | awk 'NR>1{print $1}' | grep -qx "$CONTAINER_STORAGE" || exit 213
    msg_ok "Storage '$CONTAINER_STORAGE' ($STORAGE_TYPE) validated"

    msg_info "Validating template storage '$TEMPLATE_STORAGE'"
    TEMPLATE_TYPE=$(grep -E "^[^:]+: $TEMPLATE_STORAGE$" /etc/pve/storage.cfg | cut -d: -f1)

    if ! pvesm status -content vztmpl 2>/dev/null | awk 'NR>1{print $1}' | grep -qx "$TEMPLATE_STORAGE"; then
        msg_warn "Template storage '$TEMPLATE_STORAGE' may not support 'vztmpl'"
    fi
    msg_ok "Template storage '$TEMPLATE_STORAGE' validated"

    # Free space check
    STORAGE_FREE=$(pvesm status | awk -v s="$CONTAINER_STORAGE" '$1 == s { print $6 }')
    REQUIRED_KB=$((${PCT_DISK_SIZE:-8} * 1024 * 1024))
    [[ "$STORAGE_FREE" -ge "$REQUIRED_KB" ]] || {
        msg_error "Not enough space on '$CONTAINER_STORAGE'. Needed: ${PCT_DISK_SIZE:-8}G."
        exit 214
    }

    # Cluster quorum (if cluster)
    if [[ -f /etc/pve/corosync.conf ]]; then
        msg_info "Checking cluster quorum"
        if ! pvecm status | awk -F':' '/^Quorate/ { exit ($2 ~ /Yes/) ? 0 : 1 }'; then
            msg_error "Cluster is not quorate. Start all nodes or configure quorum device (QDevice)."
            exit 210
        fi
        msg_ok "Cluster is quorate"
    fi

    # ------------------------------------------------------------------------------
    # Template discovery & validation
    # Supported OS types (PVE 9.x): alpine, archlinux, centos, debian, devuan,
    #                               fedora, gentoo, nixos, opensuse, ubuntu, unmanaged
    # ------------------------------------------------------------------------------
    TEMPLATE_SEARCH="${PCT_OSTYPE}-${PCT_OSVERSION:-}"
    case "$PCT_OSTYPE" in
    debian | ubuntu | devuan) TEMPLATE_PATTERN="-standard_" ;;
    alpine | fedora | rocky | centos | almalinux) TEMPLATE_PATTERN="-default_" ;;
    archlinux) TEMPLATE_PATTERN="-base_" ;;
    gentoo) TEMPLATE_PATTERN="-current_" ;;
    opensuse) TEMPLATE_PATTERN="-default_" ;;
    nixos) TEMPLATE_PATTERN="-" ;;
    *) TEMPLATE_PATTERN="" ;;
    esac

    msg_info "Searching for template '$TEMPLATE_SEARCH'"

    # Build regex patterns outside awk/grep for clarity
    SEARCH_PATTERN="^${TEMPLATE_SEARCH}"

    #echo "[DEBUG] TEMPLATE_SEARCH='$TEMPLATE_SEARCH'"
    #echo "[DEBUG] SEARCH_PATTERN='$SEARCH_PATTERN'"
    #echo "[DEBUG] TEMPLATE_PATTERN='$TEMPLATE_PATTERN'"

    mapfile -t LOCAL_TEMPLATES < <(
        pveam list "$TEMPLATE_STORAGE" 2>/dev/null |
            awk -v search="${SEARCH_PATTERN}" -v pattern="${TEMPLATE_PATTERN}" '$1 ~ search && $1 ~ pattern {print $1}' |
            sed 's|.*/||' | sort -t - -k 2 -V
    )

    pveam update >/dev/null 2>&1 || msg_warn "Could not update template catalog (pveam update failed)."

    msg_ok "Template search completed"

    #echo "[DEBUG] pveam available output (first 5 lines with .tar files):"
    #pveam available -section system 2>/dev/null | grep -E '\.(tar\.zst|tar\.xz|tar\.gz)$' | head -5 | sed 's/^/  /'

    set +u
    mapfile -t ONLINE_TEMPLATES < <(pveam available -section system 2>/dev/null | grep -E '\.(tar\.zst|tar\.xz|tar\.gz)$' | awk '{print $2}' | grep -E "${SEARCH_PATTERN}.*${TEMPLATE_PATTERN}" | sort -t - -k 2 -V 2>/dev/null || true)
    #echo "[DEBUG] After filtering: ${#ONLINE_TEMPLATES[@]} online templates found"
    set -u

    ONLINE_TEMPLATE=""
    [[ ${#ONLINE_TEMPLATES[@]} -gt 0 ]] && ONLINE_TEMPLATE="${ONLINE_TEMPLATES[-1]}"

    #msg_debug "SEARCH_PATTERN='${SEARCH_PATTERN}' TEMPLATE_PATTERN='${TEMPLATE_PATTERN}'"
    #msg_debug "Found ${#LOCAL_TEMPLATES[@]} local templates, ${#ONLINE_TEMPLATES[@]} online templates"
    if [[ ${#ONLINE_TEMPLATES[@]} -gt 0 ]]; then
        #msg_debug "First 3 online templates:"
        count=0
        for idx in "${!ONLINE_TEMPLATES[@]}"; do
            #msg_debug "  [$idx]: ${ONLINE_TEMPLATES[$idx]}"
            ((count++))
            [[ $count -ge 3 ]] && break
        done
    fi
    #msg_debug "ONLINE_TEMPLATE='$ONLINE_TEMPLATE'"

    if [[ ${#LOCAL_TEMPLATES[@]} -gt 0 ]]; then
        TEMPLATE="${LOCAL_TEMPLATES[-1]}"
        TEMPLATE_SOURCE="local"
    else
        TEMPLATE="$ONLINE_TEMPLATE"
        TEMPLATE_SOURCE="online"
    fi

    # If still no template, try to find alternatives
    if [[ -z "$TEMPLATE" ]]; then
        echo ""
        echo "[DEBUG] No template found for ${PCT_OSTYPE} ${PCT_OSVERSION}, searching for alternatives..."

        # Get all available versions for this OS type
        mapfile -t AVAILABLE_VERSIONS < <(
            pveam available -section system 2>/dev/null |
                grep -E '\.(tar\.zst|tar\.xz|tar\.gz)$' |
                awk -F'\t' '{print $1}' |
                grep "^${PCT_OSTYPE}-" |
                sed -E "s/.*${PCT_OSTYPE}-([0-9]+(\.[0-9]+)?).*/\1/" |
                sort -u -V 2>/dev/null
        )

        if [[ ${#AVAILABLE_VERSIONS[@]} -gt 0 ]]; then
            echo ""
            echo "${BL}Available ${PCT_OSTYPE} versions:${CL}"
            for i in "${!AVAILABLE_VERSIONS[@]}"; do
                echo "  [$((i + 1))] ${AVAILABLE_VERSIONS[$i]}"
            done
            echo ""
            read -p "Select version [1-${#AVAILABLE_VERSIONS[@]}] or press Enter to cancel: " choice

            if [[ "$choice" =~ ^[0-9]+$ ]] && [[ "$choice" -ge 1 ]] && [[ "$choice" -le ${#AVAILABLE_VERSIONS[@]} ]]; then
                PCT_OSVERSION="${AVAILABLE_VERSIONS[$((choice - 1))]}"
                TEMPLATE_SEARCH="${PCT_OSTYPE}-${PCT_OSVERSION}"
                SEARCH_PATTERN="^${TEMPLATE_SEARCH}-"

                #echo "[DEBUG] Retrying with version: $PCT_OSVERSION"

                mapfile -t ONLINE_TEMPLATES < <(
                    pveam available -section system 2>/dev/null |
                        grep -E '\.(tar\.zst|tar\.xz|tar\.gz)$' |
                        awk -F'\t' '{print $1}' |
                        grep -E "${SEARCH_PATTERN}.*${TEMPLATE_PATTERN}" |
                        sort -t - -k 2 -V 2>/dev/null || true
                )

                if [[ ${#ONLINE_TEMPLATES[@]} -gt 0 ]]; then
                    TEMPLATE="${ONLINE_TEMPLATES[-1]}"
                    TEMPLATE_SOURCE="online"
                    #echo "[DEBUG] Found alternative: $TEMPLATE"
                else
                    msg_error "No templates available for ${PCT_OSTYPE} ${PCT_OSVERSION}"
                    exit 225
                fi
            else
                msg_custom "🚫" "${YW}" "Installation cancelled"
                exit 0
            fi
        else
            msg_error "No ${PCT_OSTYPE} templates available at all"
            exit 225
        fi
    fi

    #echo "[DEBUG] Selected TEMPLATE='$TEMPLATE' SOURCE='$TEMPLATE_SOURCE'"
    #msg_debug "Selected TEMPLATE='$TEMPLATE' SOURCE='$TEMPLATE_SOURCE'"

    TEMPLATE_PATH="$(pvesm path $TEMPLATE_STORAGE:vztmpl/$TEMPLATE 2>/dev/null || true)"
    if [[ -z "$TEMPLATE_PATH" ]]; then
        TEMPLATE_BASE=$(awk -v s="$TEMPLATE_STORAGE" '$1==s {f=1} f && /path/ {print $2; exit}' /etc/pve/storage.cfg)
        [[ -n "$TEMPLATE_BASE" ]] && TEMPLATE_PATH="$TEMPLATE_BASE/template/cache/$TEMPLATE"
    fi

    # If we still don't have a path but have a valid template name, construct it
    if [[ -z "$TEMPLATE_PATH" && -n "$TEMPLATE" ]]; then
        TEMPLATE_PATH="/var/lib/vz/template/cache/$TEMPLATE"
    fi

    [[ -n "$TEMPLATE_PATH" ]] || {
        if [[ -z "$TEMPLATE" ]]; then
            msg_error "Template ${PCT_OSTYPE} ${PCT_OSVERSION} not available"

            # Get available versions
            mapfile -t AVAILABLE_VERSIONS < <(
                pveam available -section system 2>/dev/null |
                    grep "^${PCT_OSTYPE}-" |
                    sed -E 's/.*'"${PCT_OSTYPE}"'-([0-9]+\.[0-9]+).*/\1/' |
                    grep -E '^[0-9]+\.[0-9]+$' |
                    sort -u -V 2>/dev/null || sort -u
            )

            if [[ ${#AVAILABLE_VERSIONS[@]} -gt 0 ]]; then
                echo -e "\n${BL}Available versions:${CL}"
                for i in "${!AVAILABLE_VERSIONS[@]}"; do
                    echo "  [$((i + 1))] ${AVAILABLE_VERSIONS[$i]}"
                done

                echo ""
                read -p "Select version [1-${#AVAILABLE_VERSIONS[@]}] or Enter to exit: " choice

                if [[ "$choice" =~ ^[0-9]+$ ]] && [[ "$choice" -ge 1 ]] && [[ "$choice" -le ${#AVAILABLE_VERSIONS[@]} ]]; then
                    export var_version="${AVAILABLE_VERSIONS[$((choice - 1))]}"
                    export PCT_OSVERSION="$var_version"
                    msg_ok "Switched to ${PCT_OSTYPE} ${var_version}"

                    # Retry template search with new version
                    TEMPLATE_SEARCH="${PCT_OSTYPE}-${PCT_OSVERSION:-}"
                    SEARCH_PATTERN="^${TEMPLATE_SEARCH}-"

                    mapfile -t LOCAL_TEMPLATES < <(
                        pveam list "$TEMPLATE_STORAGE" 2>/dev/null |
                            awk -v search="${SEARCH_PATTERN}" -v pattern="${TEMPLATE_PATTERN}" '$1 ~ search && $1 ~ pattern {print $1}' |
                            sed 's|.*/||' | sort -t - -k 2 -V
                    )
                    mapfile -t ONLINE_TEMPLATES < <(
                        pveam available -section system 2>/dev/null |
                            grep -E '\.(tar\.zst|tar\.xz|tar\.gz)$' |
                            awk -F'\t' '{print $1}' |
                            grep -E "${SEARCH_PATTERN}.*${TEMPLATE_PATTERN}" |
                            sort -t - -k 2 -V 2>/dev/null || true
                    )
                    ONLINE_TEMPLATE=""
                    [[ ${#ONLINE_TEMPLATES[@]} -gt 0 ]] && ONLINE_TEMPLATE="${ONLINE_TEMPLATES[-1]}"

                    if [[ ${#LOCAL_TEMPLATES[@]} -gt 0 ]]; then
                        TEMPLATE="${LOCAL_TEMPLATES[-1]}"
                        TEMPLATE_SOURCE="local"
                    else
                        TEMPLATE="$ONLINE_TEMPLATE"
                        TEMPLATE_SOURCE="online"
                    fi

                    TEMPLATE_PATH="$(pvesm path $TEMPLATE_STORAGE:vztmpl/$TEMPLATE 2>/dev/null || true)"
                    if [[ -z "$TEMPLATE_PATH" ]]; then
                        TEMPLATE_BASE=$(awk -v s="$TEMPLATE_STORAGE" '$1==s {f=1} f && /path/ {print $2; exit}' /etc/pve/storage.cfg)
                        [[ -n "$TEMPLATE_BASE" ]] && TEMPLATE_PATH="$TEMPLATE_BASE/template/cache/$TEMPLATE"
                    fi

                    # If we still don't have a path but have a valid template name, construct it
                    if [[ -z "$TEMPLATE_PATH" && -n "$TEMPLATE" ]]; then
                        TEMPLATE_PATH="/var/lib/vz/template/cache/$TEMPLATE"
                    fi

                    [[ -n "$TEMPLATE_PATH" ]] || {
                        msg_error "Template still not found after version change"
                        exit 220
                    }
                else
                    msg_custom "🚫" "${YW}" "Installation cancelled"
                    exit 1
                fi
            else
                msg_error "No ${PCT_OSTYPE} templates available"
                exit 220
            fi
        fi
    }

    # Validate that we found a template
    if [[ -z "$TEMPLATE" ]]; then
        msg_error "No template found for ${PCT_OSTYPE} ${PCT_OSVERSION}"
        msg_custom "ℹ️" "${YW}" "Please check:"
        msg_custom "  •" "${YW}" "Is pveam catalog available? (run: pveam available -section system)"
        msg_custom "  •" "${YW}" "Does the template exist for your OS version?"
        exit 225
    fi

    msg_ok "Template ${BL}$TEMPLATE${CL} [$TEMPLATE_SOURCE]"
    msg_debug "Resolved TEMPLATE_PATH=$TEMPLATE_PATH"

    NEED_DOWNLOAD=0
    if [[ ! -f "$TEMPLATE_PATH" ]]; then
        msg_info "Template not present locally – will download."
        NEED_DOWNLOAD=1
    elif [[ ! -r "$TEMPLATE_PATH" ]]; then
        msg_error "Template file exists but is not readable – check permissions."
        exit 221
    elif [[ "$(stat -c%s "$TEMPLATE_PATH")" -lt 1000000 ]]; then
        if [[ -n "$ONLINE_TEMPLATE" ]]; then
            msg_warn "Template file too small (<1MB) – re-downloading."
            NEED_DOWNLOAD=1
        else
            msg_warn "Template looks too small, but no online version exists. Keeping local file."
        fi
    elif ! tar -tf "$TEMPLATE_PATH" &>/dev/null; then
        if [[ -n "$ONLINE_TEMPLATE" ]]; then
            msg_warn "Template appears corrupted – re-downloading."
            NEED_DOWNLOAD=1
        else
            msg_warn "Template appears corrupted, but no online version exists. Keeping local file."
        fi
    else
        $STD msg_ok "Template $TEMPLATE is present and valid."
    fi

    if [[ "$TEMPLATE_SOURCE" == "local" && -n "$ONLINE_TEMPLATE" && "$TEMPLATE" != "$ONLINE_TEMPLATE" ]]; then
        msg_warn "Local template is outdated: $TEMPLATE (latest available: $ONLINE_TEMPLATE)"
        if whiptail --yesno "A newer template is available:\n$ONLINE_TEMPLATE\n\nDo you want to download and use it instead?" 12 70; then
            TEMPLATE="$ONLINE_TEMPLATE"
            NEED_DOWNLOAD=1
        else
            msg_custom "ℹ️" "${BL}" "Continuing with local template $TEMPLATE"
        fi
    fi

    if [[ "$NEED_DOWNLOAD" -eq 1 ]]; then
        [[ -f "$TEMPLATE_PATH" ]] && rm -f "$TEMPLATE_PATH"
        for attempt in {1..3}; do
            msg_info "Attempt $attempt: Downloading template $TEMPLATE to $TEMPLATE_STORAGE"
            if pveam download "$TEMPLATE_STORAGE" "$TEMPLATE" >/dev/null 2>&1; then
                msg_ok "Template download successful."
                break
            fi
            if [[ $attempt -eq 3 ]]; then
                msg_error "Failed after 3 attempts. Please check network access, permissions, or manually run:\n  pveam download $TEMPLATE_STORAGE $TEMPLATE"
                exit 222
            fi
            sleep $((attempt * 5))
        done
    fi

    if ! pveam list "$TEMPLATE_STORAGE" 2>/dev/null | grep -q "$TEMPLATE"; then
        msg_error "Template $TEMPLATE not available in storage $TEMPLATE_STORAGE after download."
        exit 223
    fi

    # ------------------------------------------------------------------------------
    # Dynamic preflight for Debian 13.x: offer upgrade if available (no hard mins)
    # ------------------------------------------------------------------------------
    if [[ "$PCT_OSTYPE" == "debian" ]]; then
        OSVER="$(parse_template_osver "$TEMPLATE")"
        if [[ -n "$OSVER" ]]; then
            # Proactive, aber ohne Abbruch – nur Angebot
            offer_lxc_stack_upgrade_and_maybe_retry "no" || true
        fi
    fi

    # ------------------------------------------------------------------------------
    # Create LXC Container
    # ------------------------------------------------------------------------------
    msg_info "Creating LXC container"

    # Ensure subuid/subgid entries exist
    grep -q "root:100000:65536" /etc/subuid || echo "root:100000:65536" >>/etc/subuid
    grep -q "root:100000:65536" /etc/subgid || echo "root:100000:65536" >>/etc/subgid

    # PCT_OPTIONS is now a string (exported from build_container)
    # Add rootfs if not already specified
    if [[ ! "$PCT_OPTIONS" =~ "-rootfs" ]]; then
        PCT_OPTIONS="$PCT_OPTIONS
  -rootfs $CONTAINER_STORAGE:${PCT_DISK_SIZE:-8}"
    fi

    # Lock by template file (avoid concurrent downloads/creates)
    lockfile="/tmp/template.${TEMPLATE}.lock"
    exec 9>"$lockfile" || {
        msg_error "Failed to create lock file '$lockfile'."
        exit 200
    }
    flock -w 60 9 || {
        msg_error "Timeout while waiting for template lock."
        exit 211
    }

    LOGFILE="/tmp/pct_create_${CTID}_$(date +%Y%m%d_%H%M%S)_${SESSION_ID}.log"

    # # DEBUG: Show the actual command that will be executed
    # echo "[DEBUG] ===== PCT CREATE COMMAND DETAILS ====="
    # echo "[DEBUG] CTID: $CTID"
    # echo "[DEBUG] Template: ${TEMPLATE_STORAGE}:vztmpl/${TEMPLATE}"
    # echo "[DEBUG] PCT_OPTIONS (will be word-split):"
    # echo "$PCT_OPTIONS" | sed 's/^/  /'
    # echo "[DEBUG] Full command line:"
    # echo "  pct create $CTID ${TEMPLATE_STORAGE}:vztmpl/${TEMPLATE} $PCT_OPTIONS"
    # echo "[DEBUG] ========================================"

    msg_debug "pct create command: pct create $CTID ${TEMPLATE_STORAGE}:vztmpl/${TEMPLATE} $PCT_OPTIONS"
    msg_debug "Logfile: $LOGFILE"

    # First attempt (PCT_OPTIONS is a multi-line string, use it directly)
    if ! pct create "$CTID" "${TEMPLATE_STORAGE}:vztmpl/${TEMPLATE}" $PCT_OPTIONS >"$LOGFILE" 2>&1; then
        msg_debug "Container creation failed on ${TEMPLATE_STORAGE}. Validating template..."

        # Validate template file
        if [[ ! -s "$TEMPLATE_PATH" || "$(stat -c%s "$TEMPLATE_PATH")" -lt 1000000 ]]; then
            msg_warn "Template file too small or missing – re-downloading."
            rm -f "$TEMPLATE_PATH"
            pveam download "$TEMPLATE_STORAGE" "$TEMPLATE"
        elif ! tar -tf "$TEMPLATE_PATH" &>/dev/null; then
            if [[ -n "$ONLINE_TEMPLATE" ]]; then
                msg_warn "Template appears corrupted – re-downloading."
                rm -f "$TEMPLATE_PATH"
                pveam download "$TEMPLATE_STORAGE" "$TEMPLATE"
            else
                msg_warn "Template appears corrupted, but no online version exists. Skipping re-download."
            fi
        fi

        # Retry after repair
        if ! pct create "$CTID" "${TEMPLATE_STORAGE}:vztmpl/${TEMPLATE}" $PCT_OPTIONS >>"$LOGFILE" 2>&1; then
            # Fallback to local storage if not already on local
            if [[ "$TEMPLATE_STORAGE" != "local" ]]; then
                msg_info "Retrying container creation with fallback to local storage..."
                LOCAL_TEMPLATE_PATH="/var/lib/vz/template/cache/$TEMPLATE"
                if [[ ! -f "$LOCAL_TEMPLATE_PATH" ]]; then
                    msg_info "Downloading template to local..."
                    pveam download local "$TEMPLATE" >/dev/null 2>&1
                fi
                if ! pct create "$CTID" "local:vztmpl/${TEMPLATE}" $PCT_OPTIONS >>"$LOGFILE" 2>&1; then
                    # Local fallback also failed - check for LXC stack version issue
                    if grep -qiE 'unsupported .* version' "$LOGFILE"; then
                        echo
                        echo "pct reported 'unsupported ... version' – your LXC stack might be too old for this template."
                        echo "We can try to upgrade 'pve-container' and 'lxc-pve' now and retry automatically."
                        offer_lxc_stack_upgrade_and_maybe_retry "yes"
                        rc=$?
                        case $rc in
                        0) : ;; # success - container created, continue
                        2)
                            echo "Upgrade was declined. Please update and re-run:
  apt update && apt install --only-upgrade pve-container lxc-pve"
                            exit 231
                            ;;
                        3)
                            echo "Upgrade and/or retry failed. Please inspect: $LOGFILE"
                            exit 231
                            ;;
                        esac
                    else
                        msg_error "Container creation failed. See $LOGFILE"
                        if whiptail --yesno "pct create failed.\nDo you want to enable verbose debug mode and view detailed logs?" 12 70; then
                            set -x
                            pct create "$CTID" "local:vztmpl/${TEMPLATE}" $PCT_OPTIONS 2>&1 | tee -a "$LOGFILE"
                            set +x
                        fi
                        exit 209
                    fi
                else
                    msg_ok "Container successfully created using local fallback."
                fi
            else
                # Already on local storage and still failed - check LXC stack version
                if grep -qiE 'unsupported .* version' "$LOGFILE"; then
                    echo
                    echo "pct reported 'unsupported ... version' – your LXC stack might be too old for this template."
                    echo "We can try to upgrade 'pve-container' and 'lxc-pve' now and retry automatically."
                    offer_lxc_stack_upgrade_and_maybe_retry "yes"
                    rc=$?
                    case $rc in
                    0) : ;; # success - container created, continue
                    2)
                        echo "Upgrade was declined. Please update and re-run:
  apt update && apt install --only-upgrade pve-container lxc-pve"
                        exit 231
                        ;;
                    3)
                        echo "Upgrade and/or retry failed. Please inspect: $LOGFILE"
                        exit 231
                        ;;
                    esac
                else
                    msg_error "Container creation failed. See $LOGFILE"
                    if whiptail --yesno "pct create failed.\nDo you want to enable verbose debug mode and view detailed logs?" 12 70; then
                        set -x
                        pct create "$CTID" "local:vztmpl/${TEMPLATE}" $PCT_OPTIONS 2>&1 | tee -a "$LOGFILE"
                        set +x
                    fi
                    exit 209
                fi
            fi
        else
            msg_ok "Container successfully created after template repair."
        fi
    fi

    # Verify container exists
    pct list | awk '{print $1}' | grep -qx "$CTID" || {
        msg_error "Container ID $CTID not listed in 'pct list'. See $LOGFILE"
        exit 215
    }

    # Verify config rootfs
    grep -q '^rootfs:' "/etc/pve/lxc/$CTID.conf" || {
        msg_error "RootFS entry missing in container config. See $LOGFILE"
        exit 216
    }

    msg_ok "LXC Container ${BL}$CTID${CL} ${GN}was successfully created."

    # Report container creation to API
    post_to_api
}

# ==============================================================================
# SECTION 9: POST-INSTALLATION & FINALIZATION
# ==============================================================================

# ------------------------------------------------------------------------------
# description()
#
# - Sets container description with formatted HTML content
# - Includes:
#   * Community-Scripts logo
#   * Application name
#   * Links to GitHub, Discussions, Issues
#   * Ko-fi donation badge
# - Restarts ping-instances.service if present (monitoring)
# - Posts final "done" status to API telemetry
# ------------------------------------------------------------------------------
description() {
    IP=$(pct exec "$CTID" ip a s dev eth0 | awk '/inet / {print $2}' | cut -d/ -f1)

    # Generate LXC Description
    DESCRIPTION=$(
        cat <<EOF
<div align='center'>
  <a href='https://Helper-Scripts.com' target='_blank' rel='noopener noreferrer'>
    <img src='https://git.community-scripts.org/community-scripts/ProxmoxVED/raw/branch/main/misc/images/logo-81x112.png' alt='Logo' style='width:81px;height:112px;'/>
  </a>

  <h2 style='font-size: 24px; margin: 20px 0;'>${APP} LXC</h2>

  <p style='margin: 16px 0;'>
    <a href='https://ko-fi.com/community_scripts' target='_blank' rel='noopener noreferrer'>
      <img src='https://img.shields.io/badge/&#x2615;-Buy us a coffee-blue' alt='spend Coffee' />
    </a>
  </p>

  <span style='margin: 0 10px;'>
    <i class="fa fa-github fa-fw" style="color: #f5f5f5;"></i>
    <a href='https://github.com/community-scripts/ProxmoxVED' target='_blank' rel='noopener noreferrer' style='text-decoration: none; color: #00617f;'>GitHub</a>
  </span>
  <span style='margin: 0 10px;'>
    <i class="fa fa-comments fa-fw" style="color: #f5f5f5;"></i>
    <a href='https://github.com/community-scripts/ProxmoxVED/discussions' target='_blank' rel='noopener noreferrer' style='text-decoration: none; color: #00617f;'>Discussions</a>
  </span>
  <span style='margin: 0 10px;'>
    <i class="fa fa-exclamation-circle fa-fw" style="color: #f5f5f5;"></i>
    <a href='https://github.com/community-scripts/ProxmoxVED/issues' target='_blank' rel='noopener noreferrer' style='text-decoration: none; color: #00617f;'>Issues</a>
  </span>
</div>
EOF
    )
    pct set "$CTID" -description "$DESCRIPTION"

    if [[ -f /etc/systemd/system/ping-instances.service ]]; then
        systemctl start ping-instances.service
    fi

    post_update_to_api "done" "none"
}

# ==============================================================================
# SECTION 10: ERROR HANDLING & EXIT TRAPS
# ==============================================================================

# ------------------------------------------------------------------------------
# api_exit_script()
#
# - Exit trap handler for reporting to API telemetry
# - Captures exit code and reports to API using centralized error descriptions
# - Uses explain_exit_code() from error_handler.func for consistent error messages
# - Posts failure status with exit code to API (error description added automatically)
# - Only executes on non-zero exit codes
# ------------------------------------------------------------------------------
api_exit_script() {
    exit_code=$?
    if [ $exit_code -ne 0 ]; then
        post_update_to_api "failed" "$exit_code"
    fi
}

if command -v pveversion >/dev/null 2>&1; then
    trap 'api_exit_script' EXIT
fi
trap 'post_update_to_api "failed" "$BASH_COMMAND"' ERR
trap 'post_update_to_api "failed" "INTERRUPTED"' SIGINT
trap 'post_update_to_api "failed" "TERMINATED"' SIGTERM
