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

# ------------------------------------------------------------------------------
# variables()
#
# - Normalize application name (NSAPP = lowercase, no spaces)
# - Build installer filename (var_install)
# - Define regex for integer validation
# - Fetch hostname of Proxmox node
# - Set default values for diagnostics/method
# - Generate random UUID for tracking
# ------------------------------------------------------------------------------
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.
  CTTYPE="${CTTYPE:-${CT_TYPE:-1}}"
  #CT_TYPE=${var_unprivileged:-$CT_TYPE}
}

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

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

  echo -e "${CM}${GN} All kernel key limits are within safe thresholds.${CL}"
}

# ------------------------------------------------------------------------------
# get_current_ip()
#
# - Returns current container IP depending on OS type
# - Debian/Ubuntu: uses `hostname -I`
# - Alpine: parses eth0 via `ip -4 addr`
# ------------------------------------------------------------------------------
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
}

# ------------------------------------------------------------------------------
# base_settings()
#
# - Defines all base/default variables for container creation
# - Reads from environment variables (var_*)
# - Provides fallback defaults for OS type/version
# ------------------------------------------------------------------------------
base_settings() {
  # Default Settings
  CT_TYPE=${var_unprivileged:-"1"}
  DISK_SIZE=${var_disk:-"4"}
  CORE_COUNT=${var_cpu:-"1"}
  RAM_SIZE=${var_ram:-"1024"}
  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:-""}
  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
}

# ------------------------------------------------------------------------------
# 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 "${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 "  "
}

# ------------------------------------------------------------------------------
# exit_script()
#
# - Called when user cancels an action
# - Clears screen and exits gracefully
# ------------------------------------------------------------------------------
exit_script() {
  clear
  echo -e "\n${CROSS}${RD}User exited script${CL}\n"
  exit
}

# ------------------------------------------------------------------------------
# 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[*]}"
  )
}

# ------------------------------------------------------------------------------
# advanced_settings()
#
# - Interactive whiptail menu for advanced configuration
# - Lets user set container type, password, CT ID, hostname, disk, CPU, RAM
# - Supports IPv4/IPv6, DNS, MAC, VLAN, tags, SSH keys, FUSE, verbose mode
# - Ends with confirmation or re-entry if cancelled
# ------------------------------------------------------------------------------
advanced_settings() {
  whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --msgbox --title "Here is an instructional tip:" "To make a selection, use the Spacebar." 8 58
  # Setting Default Tag for Advanced Settings
  TAGS="community-script;${var_tags:-}"
  CT_DEFAULT_TYPE="${CT_TYPE}"
  CT_TYPE=""
  while [ -z "$CT_TYPE" ]; do
    if [ "$CT_DEFAULT_TYPE" == "1" ]; then
      if CT_TYPE=$(whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --title "CONTAINER TYPE" --radiolist "Choose Type" 10 58 2 \
        "1" "Unprivileged" ON \
        "0" "Privileged" OFF \
        3>&1 1>&2 2>&3); then
        if [ -n "$CT_TYPE" ]; then
          CT_TYPE_DESC="Unprivileged"
          if [ "$CT_TYPE" -eq 0 ]; then
            CT_TYPE_DESC="Privileged"
          fi
          echo -e "${OS}${BOLD}${DGN}Operating System: ${BGN}$var_os | ${OSVERSION}${BOLD}${DGN}Version: ${BGN}$var_version${CL}"
          echo -e "${CONTAINERTYPE}${BOLD}${DGN}Container Type: ${BGN}$CT_TYPE_DESC${CL}"
        fi
      else
        exit_script
      fi
    fi
    if [ "$CT_DEFAULT_TYPE" == "0" ]; then
      if CT_TYPE=$(whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --title "CONTAINER TYPE" --radiolist "Choose Type" 10 58 2 \
        "1" "Unprivileged" OFF \
        "0" "Privileged" ON \
        3>&1 1>&2 2>&3); then
        if [ -n "$CT_TYPE" ]; then
          CT_TYPE_DESC="Unprivileged"
          if [ "$CT_TYPE" -eq 0 ]; then
            CT_TYPE_DESC="Privileged"
          fi
          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_DESC${CL}"
        fi
      else
        exit_script
      fi
    fi
  done

  while true; do
    if PW1=$(whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --passwordbox "\nSet Root Password (needed for root ssh access)" 9 58 --title "PASSWORD (leave blank for automatic login)" 3>&1 1>&2 2>&3); then
      # Empty = Autologin
      if [[ -z "$PW1" ]]; then
        PW=""
        PW1="Automatic Login"
        echo -e "${VERIFYPW}${BOLD}${DGN}Root Password: ${BGN}$PW1${CL}"
        break
      fi

      # Invalid: contains spaces
      if [[ "$PW1" == *" "* ]]; then
        whiptail --msgbox "Password cannot contain spaces." 8 58
        continue
      fi

      # Invalid: too short
      if ((${#PW1} < 5)); then
        whiptail --msgbox "Password must be at least 5 characters." 8 58
        continue
      fi

      # Confirm password
      if PW2=$(whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --passwordbox "\nVerify Root Password" 9 58 --title "PASSWORD VERIFICATION" 3>&1 1>&2 2>&3); then
        if [[ "$PW1" == "$PW2" ]]; then
          PW="-password $PW1"
          echo -e "${VERIFYPW}${BOLD}${DGN}Root Password: ${BGN}********${CL}"
          break
        else
          whiptail --msgbox "Passwords do not match. Please try again." 8 58
        fi
      else
        exit_script
      fi
    else
      exit_script
    fi
  done

 if CT_ID=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Set Container ID" 8 58 "$NEXTID" --title "CONTAINER ID" 3>&1 1>&2 2>&3); then
    if [ -z "$CT_ID" ]; then
      CT_ID="$NEXTID"
    fi
  else
    exit_script
  fi
  echo -e "${CONTAINERID}${BOLD}${DGN}Container ID: ${BGN}$CT_ID${CL}"

  while true; do
    if CT_NAME=$(whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --inputbox "Set Hostname" 8 58 "$NSAPP" --title "HOSTNAME" 3>&1 1>&2 2>&3); then
      if [ -z "$CT_NAME" ]; then
        HN="$NSAPP"
      else
        HN=$(echo "${CT_NAME,,}" | tr -d ' ')
      fi
      # Hostname validate (RFC 1123)
      if [[ "$HN" =~ ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$ ]]; then
        echo -e "${HOSTNAME}${BOLD}${DGN}Hostname: ${BGN}$HN${CL}"
        break
      else
        whiptail --backtitle "[dev] Proxmox VE Helper Scripts" \
          --msgbox "❌ Invalid hostname: '$HN'\n\nOnly lowercase letters, digits and hyphens (-) are allowed.\nUnderscores (_) or other characters are not permitted!" 10 70
      fi
    else
      exit_script
    fi
  done

  while true; do
    DISK_SIZE=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Set Disk Size in GB" 8 58 "$var_disk" --title "DISK SIZE" 3>&1 1>&2 2>&3) || exit_script

    if [ -z "$DISK_SIZE" ]; then
      DISK_SIZE="$var_disk"
    fi

    if [[ "$DISK_SIZE" =~ ^[1-9][0-9]*$ ]]; then
      echo -e "${DISKSIZE}${BOLD}${DGN}Disk Size: ${BGN}${DISK_SIZE} GB${CL}"
      break
    else
      whiptail --msgbox "Disk size must be a positive integer!" 8 58
    fi
  done

  while true; do
    CORE_COUNT=$(whiptail --backtitle "Proxmox VE Helper Scripts" \
      --inputbox "Allocate CPU Cores" 8 58 "$var_cpu" --title "CORE COUNT" 3>&1 1>&2 2>&3) || exit_script

    if [ -z "$CORE_COUNT" ]; then
      CORE_COUNT="$var_cpu"
    fi

    if [[ "$CORE_COUNT" =~ ^[1-9][0-9]*$ ]]; then
      echo -e "${CPUCORE}${BOLD}${DGN}CPU Cores: ${BGN}$CORE_COUNT${CL}"
      break
    else
      whiptail --msgbox "CPU core count must be a positive integer!" 8 58
    fi
  done

  while true; do
    RAM_SIZE=$(whiptail --backtitle "Proxmox VE Helper Scripts" \
      --inputbox "Allocate RAM in MiB" 8 58 "$var_ram" --title "RAM" 3>&1 1>&2 2>&3) || exit_script

    if [ -z "$RAM_SIZE" ]; then
      RAM_SIZE="$var_ram"
    fi

    if [[ "$RAM_SIZE" =~ ^[1-9][0-9]*$ ]]; then
      echo -e "${RAMSIZE}${BOLD}${DGN}RAM Size: ${BGN}${RAM_SIZE} MiB${CL}"
      break
    else
      whiptail --msgbox "RAM size must be a positive integer!" 8 58
    fi
  done

  IFACE_FILEPATH_LIST="/etc/network/interfaces"$'\n'$(find "/etc/network/interfaces.d/" -type f)
  BRIDGES=""
  OLD_IFS=$IFS
  IFS=$'\n'
  for iface_filepath in ${IFACE_FILEPATH_LIST}; do

    iface_indexes_tmpfile=$(mktemp -q -u '.iface-XXXX')
    (grep -Pn '^\s*iface' "${iface_filepath}" | cut -d':' -f1 && wc -l "${iface_filepath}" | cut -d' ' -f1) | awk 'FNR==1 {line=$0; next} {print line":"$0-1; line=$0}' >"${iface_indexes_tmpfile}" || true

    if [ -f "${iface_indexes_tmpfile}" ]; then

      while read -r pair; do
        start=$(echo "${pair}" | cut -d':' -f1)
        end=$(echo "${pair}" | cut -d':' -f2)

        if awk "NR >= ${start} && NR <= ${end}" "${iface_filepath}" | grep -qP '^\s*(bridge[-_](ports|stp|fd|vlan-aware|vids)|ovs_type\s+OVSBridge)\b'; then
          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)
  if [[ -z "$BRIDGES" ]]; then
    BRG="vmbr0"
    echo -e "${BRIDGE}${BOLD}${DGN}Bridge: ${BGN}$BRG${CL}"
  else
    # Build bridge menu with descriptions
    BRIDGE_MENU_OPTIONS=()
    while IFS= read -r bridge; do
      if [[ -n "$bridge" ]]; then
        # Get description from Proxmox built-in method - find comment for this specific bridge
        description=$(grep -A 10 "iface $bridge" /etc/network/interfaces | grep '^#' | head -n1 | sed 's/^#\s*//')
        if [[ -n "$description" ]]; then
          BRIDGE_MENU_OPTIONS+=("$bridge" "${description}")
        else
          BRIDGE_MENU_OPTIONS+=("$bridge" " ")
        fi
      fi
    done <<<"$BRIDGES"

    BRG=$(whiptail --backtitle "Proxmox VE Helper Scripts" --menu "Select network bridge: " 18 55 6 "${BRIDGE_MENU_OPTIONS[@]}" 3>&1 1>&2 2>&3)
    if [[ -z "$BRG" ]]; then
      exit_script
    else
      echo -e "${BRIDGE}${BOLD}${DGN}Bridge: ${BGN}$BRG${CL}"
    fi
  fi

  # IPv4 methods: dhcp, static, none
  while true; do
    IPV4_METHOD=$(whiptail --backtitle "[dev] Proxmox VE Helper Scripts" \
      --title "IPv4 Address Management" \
      --menu "Select IPv4 Address Assignment Method:" 12 60 2 \
      "dhcp" "Automatic (DHCP, recommended)" \
      "static" "Static (manual entry)" \
      3>&1 1>&2 2>&3)

    exit_status=$?
    if [ $exit_status -ne 0 ]; then
      exit_script
    fi

    case "$IPV4_METHOD" in
    dhcp)
      NET="dhcp"
      GATE=""
      echo -e "${NETWORK}${BOLD}${DGN}IPv4: DHCP${CL}"
      break
      ;;
    static)
      # Static: call and validate CIDR address
      while true; do
        NET=$(whiptail --backtitle "[dev] Proxmox VE Helper Scripts" \
          --inputbox "Enter Static IPv4 CIDR Address (e.g. 192.168.100.50/24)" 8 58 "" \
          --title "IPv4 ADDRESS" 3>&1 1>&2 2>&3)
        if [ -z "$NET" ]; then
          whiptail --msgbox "IPv4 address must not be empty." 8 58
          continue
        elif [[ "$NET" =~ ^([0-9]{1,3}\.){3}[0-9]{1,3}/([0-9]|[1-2][0-9]|3[0-2])$ ]]; then
          echo -e "${NETWORK}${BOLD}${DGN}IPv4 Address: ${BGN}$NET${CL}"
          break
        else
          whiptail --msgbox "$NET is not a valid IPv4 CIDR address. Please enter a correct value!" 8 58
        fi
      done

      # call and validate Gateway
      while true; do
        GATE1=$(whiptail --backtitle "[dev] Proxmox VE Helper Scripts" \
          --inputbox "Enter Gateway IP address for static IPv4" 8 58 "" \
          --title "Gateway IP" 3>&1 1>&2 2>&3)
        if [ -z "$GATE1" ]; then
          whiptail --msgbox "Gateway IP address cannot be empty." 8 58
        elif [[ ! "$GATE1" =~ ^([0-9]{1,3}\.){3}[0-9]{1,3}$ ]]; then
          whiptail --msgbox "Invalid Gateway IP address format." 8 58
        else
          GATE=",gw=$GATE1"
          echo -e "${GATEWAY}${BOLD}${DGN}Gateway IP Address: ${BGN}$GATE1${CL}"
          break
        fi
      done
      break
      ;;
    esac
  done

  # IPv6 Address Management selection
  while true; do
    IPV6_METHOD=$(whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --menu \
      "Select IPv6 Address Management Type:" 15 58 4 \
      "auto" "SLAAC/AUTO (recommended, default)" \
      "dhcp" "DHCPv6" \
      "static" "Static (manual entry)" \
      "none" "Disabled" \
      --default-item "auto" 3>&1 1>&2 2>&3)
    [ $? -ne 0 ] && exit_script

    case "$IPV6_METHOD" in
    auto)
      echo -e "${NETWORK}${BOLD}${DGN}IPv6: ${BGN}SLAAC/AUTO${CL}"
      IPV6_ADDR=""
      IPV6_GATE=""
      break
      ;;
    dhcp)
      echo -e "${NETWORK}${BOLD}${DGN}IPv6: ${BGN}DHCPv6${CL}"
      IPV6_ADDR="dhcp"
      IPV6_GATE=""
      break
      ;;
    static)
      # Ask for static IPv6 address (CIDR notation, e.g., 2001:db8::1234/64)
      while true; do
        IPV6_ADDR=$(whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --inputbox \
          "Set a static IPv6 CIDR address (e.g., 2001:db8::1234/64)" 8 58 "" \
          --title "IPv6 STATIC ADDRESS" 3>&1 1>&2 2>&3) || exit_script
        if [[ "$IPV6_ADDR" =~ ^([0-9a-fA-F:]+:+)+[0-9a-fA-F]+(/[0-9]{1,3})$ ]]; then
          echo -e "${NETWORK}${BOLD}${DGN}IPv6 Address: ${BGN}$IPV6_ADDR${CL}"
          break
        else
          whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --msgbox \
            "$IPV6_ADDR is an invalid IPv6 CIDR address. Please enter a valid IPv6 CIDR address (e.g., 2001:db8::1234/64)" 8 58
        fi
      done
      # Optional: ask for IPv6 gateway for static config
      while true; do
        IPV6_GATE=$(whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --inputbox \
          "Enter IPv6 gateway address (optional, leave blank for none)" 8 58 "" --title "IPv6 GATEWAY" 3>&1 1>&2 2>&3)
        if [ -z "$IPV6_GATE" ]; then
          IPV6_GATE=""
          break
        elif [[ "$IPV6_GATE" =~ ^([0-9a-fA-F:]+:+)+[0-9a-fA-F]+$ ]]; then
          break
        else
          whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --msgbox \
            "Invalid IPv6 gateway format." 8 58
        fi
      done
      break
      ;;
    none)
      echo -e "${NETWORK}${BOLD}${DGN}IPv6: ${BGN}Disabled${CL}"
      IPV6_ADDR="none"
      IPV6_GATE=""
      break
      ;;
    *)
      exit_script
      ;;
    esac
  done

  if [ "$var_os" == "alpine" ]; then
    APT_CACHER=""
    APT_CACHER_IP=""
  else
    if APT_CACHER_IP=$(whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --inputbox "Set APT-Cacher IP (leave blank for none)" 8 58 --title "APT-Cacher IP" 3>&1 1>&2 2>&3); then
      APT_CACHER="${APT_CACHER_IP:+yes}"
      echo -e "${NETWORK}${BOLD}${DGN}APT-Cacher IP Address: ${BGN}${APT_CACHER_IP:-Default}${CL}"
    else
      exit_script
    fi
  fi

  # if (whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --defaultno --title "IPv6" --yesno "Disable IPv6?" 10 58); then
  #   DISABLEIP6="yes"
  # else
  #   DISABLEIP6="no"
  # fi
  # echo -e "${DISABLEIPV6}${BOLD}${DGN}Disable IPv6: ${BGN}$DISABLEIP6${CL}"

  if MTU1=$(whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --inputbox "Set Interface MTU Size (leave blank for default [The MTU of your selected vmbr, default is 1500])" 8 58 --title "MTU SIZE" 3>&1 1>&2 2>&3); then
    if [ -z "$MTU1" ]; then
      MTU1="Default"
      MTU=""
    else
      MTU=",mtu=$MTU1"
    fi
    echo -e "${DEFAULT}${BOLD}${DGN}Interface MTU Size: ${BGN}$MTU1${CL}"
  else
    exit_script
  fi

  if SD=$(whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --inputbox "Set a DNS Search Domain (leave blank for HOST)" 8 58 --title "DNS Search Domain" 3>&1 1>&2 2>&3); then
    if [ -z "$SD" ]; then
      SX=Host
      SD=""
    else
      SX=$SD
      SD="-searchdomain=$SD"
    fi
    echo -e "${SEARCH}${BOLD}${DGN}DNS Search Domain: ${BGN}$SX${CL}"
  else
    exit_script
  fi

  if NX=$(whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --inputbox "Set a DNS Server IP (leave blank for HOST)" 8 58 --title "DNS SERVER IP" 3>&1 1>&2 2>&3); then
    if [ -z "$NX" ]; then
      NX=Host
      NS=""
    else
      NS="-nameserver=$NX"
    fi
    echo -e "${NETWORK}${BOLD}${DGN}DNS Server IP Address: ${BGN}$NX${CL}"
  else
    exit_script
  fi

  if [ "$var_os" == "alpine" ] && [ "$NET" == "dhcp" ] && [ "$NX" != "Host" ]; then
    UDHCPC_FIX="yes"
  else
    UDHCPC_FIX="no"
  fi
  export UDHCPC_FIX

  if MAC1=$(whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --inputbox "Set a MAC Address(leave blank for generated MAC)" 8 58 --title "MAC ADDRESS" 3>&1 1>&2 2>&3); then
    if [ -z "$MAC1" ]; then
      MAC1="Default"
      MAC=""
    else
      MAC=",hwaddr=$MAC1"
      echo -e "${MACADDRESS}${BOLD}${DGN}MAC Address: ${BGN}$MAC1${CL}"
    fi
  else
    exit_script
  fi

  if VLAN1=$(whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --inputbox "Set a Vlan(leave blank for no VLAN)" 8 58 --title "VLAN" 3>&1 1>&2 2>&3); then
    if [ -z "$VLAN1" ]; then
      VLAN1="Default"
      VLAN=""
    else
      VLAN=",tag=$VLAN1"
    fi
    echo -e "${VLANTAG}${BOLD}${DGN}Vlan: ${BGN}$VLAN1${CL}"
  else
    exit_script
  fi

  if ADV_TAGS=$(whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --inputbox "Set Custom Tags?[If you remove all, there will be no tags!]" 8 58 "${TAGS}" --title "Advanced Tags" 3>&1 1>&2 2>&3); then
    if [ -n "${ADV_TAGS}" ]; then
      ADV_TAGS=$(echo "$ADV_TAGS" | tr -d '[:space:]')
      TAGS="${ADV_TAGS}"
    else
      TAGS=";"
    fi
    echo -e "${NETWORK}${BOLD}${DGN}Tags: ${BGN}$TAGS${CL}"
  else
    exit_script
  fi

  configure_ssh_settings
  export SSH_KEYS_FILE
  echo -e "${ROOTSSH}${BOLD}${DGN}Root SSH Access: ${BGN}$SSH${CL}"
  if (whiptail --backtitle "Proxmox VE Helper Scripts" --defaultno --title "FUSE Support" --yesno "Enable FUSE support?\nRequired for tools like rclone, mergerfs, AppImage, etc." 10 58); then
    ENABLE_FUSE="yes"
  else
    ENABLE_FUSE="no"
  fi
  echo -e "${FUSE}${BOLD}${DGN}Enable FUSE Support: ${BGN}$ENABLE_FUSE${CL}"

  if (whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --defaultno --title "VERBOSE MODE" --yesno "Enable Verbose Mode?" 10 58); then
    VERBOSE="yes"
  else
    VERBOSE="no"
  fi
  echo -e "${SEARCH}${BOLD}${DGN}Verbose Mode: ${BGN}$VERBOSE${CL}"

  if (whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --title "ADVANCED SETTINGS COMPLETE" --yesno "Ready to create ${APP} LXC?" 10 58); then
    echo -e "${CREATING}${BOLD}${RD}Creating a ${APP} LXC using the above advanced settings${CL}"
  else
    clear
    header_info
    echo -e "${ADVANCED}${BOLD}${RD}Using Advanced Settings on node $PVEHOST_NAME${CL}"
    advanced_settings
  fi
}

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

}

# ------------------------------------------------------------------------------
# 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)
  local VAR_WHITELIST=(
    var_apt_cacher var_apt_cacher_ip var_brg var_cpu var_ctid var_disk var_fuse
    var_gateway var_hostname var_ipv6_method var_ipv6_static 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
  )

  # 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"
    msg_info "No default.vars found. Creating ${canonical}"
    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.

# 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_ipv6_static=
# var_vlan=
# var_mtu=
# var_mac=
# var_ns=

# SSH
var_ssh=no
# var_ssh_authorized_key=

# APT cacher (optional)
# var_apt_cacher=yes
# var_apt_cacher_ip=192.168.1.10

# Features/Tags/verbosity
var_fuse=no
var_tun=no
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"
    msg_ok "Created ${canonical}"
  }

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

  # Safe parser for KEY=VALUE lines
  local _load_vars_file
  _load_vars_file() {
    local file="$1"
    [ -f "$file" ] || return 0
    msg_info "Loading defaults from ${file}"
    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_key "$var_key" || {
          msg_debug "Ignore non-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

        # Unsafe characters
        case $var_val in
        \"*\")
          var_val=${var_val#\"}
          var_val=${var_val%\"}
          ;;
        \'*\')
          var_val=${var_val#\'}
          var_val=${var_val%\'}
          ;;
        esac # Hard env wins
        [[ -n "${_HARD_ENV[$var_key]:-}" ]] && continue
        # Set only if not already exported
        [[ -z "${!var_key+x}" ]] && export "${var_key}=${var_val}"
      else
        msg_warn "Malformed line in ${file}: ${line}"
      fi
    done <"$file"
    msg_ok "Loaded ${file}"
  }

  # 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 My 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
  declare -ag VAR_WHITELIST=(
    var_apt_cacher var_apt_cacher_ip var_brg var_cpu var_ctid var_disk var_fuse
    var_gateway var_hostname var_ipv6_method var_ipv6_static 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

_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
declare -A _VARS_IN
_load_vars_file() {
  local file="$1"
  [ -f "$file" ] || return 0
  msg_info "Loading defaults from ${file}"
  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
        [ -z "${!key+x}" ] && export "$key=$val"
      fi
      ;;
    esac
  done <"$file"
  msg_ok "Loaded ${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}"
  _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")"
    [ -n "$_ipv6_static" ] && echo "var_ipv6_static=$(_sanitize_value "$_ipv6_static")"

    [ -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 "$_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_info "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_info "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

  msg_ok "Storage configuration saved to $(basename "$vf")"
}

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
}

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"
}

# ------------------------------------------------------------------------------
# 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)
  timezone=$(cat /etc/timezone)

  # 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" "My 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

  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

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

  # --- 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)"
    ;;
  2 | advanced | ADVANCED)
    header_info
    echo -e "${ADVANCED}${BOLD}${RD}Using Advanced Install on node $PVEHOST_NAME${CL}"
    METHOD="advanced"
    base_settings
    advanced_settings
    defaults_target="$(ensure_global_default_vars_file)"
    run_maybe_offer="yes"
    ;;
  3 | mydefaults | MYDEFAULTS)
    default_var_settings || {
      msg_error "Failed to apply default.vars"
      exit 1
    }
    defaults_target="/usr/local/community-scripts/default.vars"
    ;;
  "$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)"
    else
      msg_error "No App Defaults available for ${APP}"
      exit 1
    fi
    ;;
  "$SETTINGS_OPTION" | settings | SETTINGS)
    settings_menu
    defaults_target=""
    ;;
  *)
    echo -e "${CROSS}${RD}Invalid option: $CHOICE${CL}"
    exit 1
    ;;
  esac

  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"
      "3" "Edit Default Storage"
    )
    if [ -f "$(get_app_defaults_path)" ]; then
      settings_items+=("4" "Edit App.vars for ${APP}")
      settings_items+=("5" "Exit")
    else
      settings_items+=("4" "Exit")
    fi

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

    case "$choice" in
    1) diagnostics_menu ;;
    2) ${EDITOR:-nano} /usr/local/community-scripts/default.vars ;;
    3) edit_default_storage ;;
    4)
      if [ -f "$(get_app_defaults_path)" ]; then
        ${EDITOR:-nano} "$(get_app_defaults_path)"
      else
        exit_script
      fi
      ;;
    5) exit_script ;;
    esac
  done
}

# ===== Unified storage selection & writing to vars files =====
_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

  msg_ok "Updated ${key} → ${STORAGE_RESULT}"
}

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

# ------------------------------------------------------------------------------
# build_container()
#
# - Creates and configures the LXC container
# - Builds network string and applies features (FUSE, TUN, VAAPI passthrough)
# - Starts container and waits for network connectivity
# - Installs base packages, SSH keys, and runs <app>-install.sh
# ------------------------------------------------------------------------------
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

  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

  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
  export DIAGNOSTICS="$DIAGNOSTICS"
  export RANDOM_UUID="$RANDOM_UUID"
  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"
  export PCT_OPTIONS="
  -features $FEATURES
  -hostname $HN
  -tags $TAGS
  $SD
  $NS
  $NET_STRING
  -onboot 1
  -cores $CORE_COUNT
  -memory $RAM_SIZE
  -unprivileged $CT_TYPE
  $PW
"
  export TEMPLATE_STORAGE="${var_template_storage:-}"
  export CONTAINER_STORAGE="${var_container_storage:-}"
  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_info "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_info "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_info "Detected NVIDIA GPU"
      if ! check_nvidia_host_setup; then
        msg_error "NVIDIA host setup incomplete. Skipping GPU passthrough."
        msg_info "Fix NVIDIA drivers on host, then recreate container or passthrough manually."
        return 0
      fi

      for d in /dev/nvidia* /dev/nvidiactl /dev/nvidia-modeset; do
        [[ -e "$d" ]] && NVIDIA_DEVICES+=("$d")
      done

      if [[ ${#NVIDIA_DEVICES[@]} -eq 0 ]]; then
        msg_warn "NVIDIA GPU detected but no /dev/nvidia* devices found"
        msg_warn "Please install NVIDIA drivers on host: apt install nvidia-driver"
      else
        if [[ "$CT_TYPE" == "0" ]]; then
          cat <<EOF >>"$LXC_CONFIG"
    # NVIDIA GPU Passthrough (privileged)
    lxc.cgroup2.devices.allow: c 195:* rwm
    lxc.cgroup2.devices.allow: c 243:* rwm
    lxc.mount.entry: /dev/nvidia0 dev/nvidia0 none bind,optional,create=file
    lxc.mount.entry: /dev/nvidiactl dev/nvidiactl none bind,optional,create=file
    lxc.mount.entry: /dev/nvidia-uvm dev/nvidia-uvm none bind,optional,create=file
    lxc.mount.entry: /dev/nvidia-uvm-tools dev/nvidia-uvm-tools none bind,optional,create=file
EOF

          if [[ -e /dev/dri/renderD128 ]]; then
            echo "lxc.mount.entry: /dev/dri/renderD128 dev/dri/renderD128 none bind,optional,create=file" >>"$LXC_CONFIG"
          fi

          export GPU_TYPE="NVIDIA"
          export NVIDIA_DRIVER_VERSION=$(nvidia-smi --query-gpu=driver_version --format=csv,noheader 2>/dev/null | head -n1)
          msg_ok "NVIDIA GPU passthrough configured (driver: ${NVIDIA_DRIVER_VERSION})"
        else
          msg_warn "NVIDIA passthrough only supported for privileged containers"
          return 0
        fi
      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_info "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_info "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[@]}")

      # For Proxmox WebUI visibility, add as dev0, dev1 etc.
      for dev in "${devices[@]}"; do
        if [[ "$CT_TYPE" == "0" ]]; then
          # Privileged container - use dev entries for WebUI visibility
          # Use initial GID 104 (render) for renderD*, 44 (video) for card*
          if [[ "$dev" =~ renderD ]]; then
            echo "dev${dev_idx}: $dev,gid=104" >>"$LXC_CONFIG"
          else
            echo "dev${dev_idx}: $dev,gid=44" >>"$LXC_CONFIG"
          fi
          dev_idx=$((dev_idx + 1))

          # Also add cgroup allows for privileged containers
          local major minor
          major=$(stat -c '%t' "$dev" 2>/dev/null || echo "0")
          minor=$(stat -c '%T' "$dev" 2>/dev/null || echo "0")

          if [[ "$major" != "0" && "$minor" != "0" ]]; then
            echo "lxc.cgroup2.devices.allow: c $((0x$major)):$((0x$minor)) rwm" >>"$LXC_CONFIG"
          fi
        else
          # Unprivileged container
          if [[ "$dev" =~ renderD ]]; then
            echo "dev${dev_idx}: $dev,uid=0,gid=104" >>"$LXC_CONFIG"
          else
            echo "dev${dev_idx}: $dev,uid=0,gid=44" >>"$LXC_CONFIG"
          fi
          dev_idx=$((dev_idx + 1))
        fi
      done

      export GPU_TYPE="$selected_gpu"
      msg_ok "${selected_gpu} GPU passthrough configured (${dev_idx} devices)"
      ;;

    NVIDIA)
      if [[ ${#NVIDIA_DEVICES[@]} -eq 0 ]]; then
        msg_error "NVIDIA drivers not installed on host. Please install: apt install nvidia-driver"
        return 1
      fi

      for dev in "${NVIDIA_DEVICES[@]}"; do
        # NVIDIA devices typically need different handling
        echo "dev${dev_idx}: $dev,uid=0,gid=44" >>"$LXC_CONFIG"
        dev_idx=$((dev_idx + 1))

        if [[ "$CT_TYPE" == "0" ]]; then
          local major minor
          major=$(stat -c '%t' "$dev" 2>/dev/null || echo "0")
          minor=$(stat -c '%T' "$dev" 2>/dev/null || echo "0")

          if [[ "$major" != "0" && "$minor" != "0" ]]; then
            echo "lxc.cgroup2.devices.allow: c $((0x$major)):$((0x$minor)) rwm" >>"$LXC_CONFIG"
          fi
        fi
      done

      export GPU_TYPE="NVIDIA"
      msg_ok "NVIDIA GPU passthrough configured (${dev_idx} devices)"
      ;;
    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_info "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
    for i in {1..20}; do
      ip_in_lxc=$(pct exec "$CTID" -- ip -4 addr show dev eth0 | awk '/inet / {print $2}' | cut -d/ -f1)
      [ -n "$ip_in_lxc" ] && break
      sleep 1
    done

    if [ -z "$ip_in_lxc" ]; then
      msg_error "No IP assigned to CT $CTID after 20s"
      exit 1
    fi

    # Try to reach gateway
    gw_ok=0
    for i in {1..10}; do
      if pct exec "$CTID" -- ping -c1 -W1 "${GATEWAY:-8.8.8.8}" >/dev/null 2>&1; then
        gw_ok=1
        break
      fi
      sleep 1
    done

    if [ "$gw_ok" -eq 1 ]; then
      msg_ok "Network in LXC is reachable (IP $ip_in_lxc)"
    else
      msg_warn "Network reachable but gateway check failed"
    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
      pct exec "$CTID" -- bash -c "tz='$tz'; echo \"\$tz\" >/etc/timezone && ln -sf \"/usr/share/zoneinfo/\$tz\" /etc/localtime"
    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"

  # Verify GPU access if enabled
  if [[ "${ENABLE_VAAPI:-0}" == "1" ]] && [ "$var_os" != "alpine" ]; then
    pct exec "$CTID" -- bash -c "vainfo >/dev/null 2>&1" &&
      msg_ok "VAAPI verified working" ||
      msg_warn "VAAPI verification failed - may need additional configuration"
  fi

  if [[ "${ENABLE_NVIDIA:-0}" == "1" ]] && [ "$var_os" != "alpine" ]; then
    pct exec "$CTID" -- bash -c "nvidia-smi >/dev/null 2>&1" &&
      msg_ok "NVIDIA verified working" ||
      msg_warn "NVIDIA verification failed - may need additional configuration"
  fi

  # Install SSH keys
  install_ssh_keys_into_ct

  # Run application installer
  if ! lxc-attach -n "$CTID" -- bash -c "$(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVED/main/install/${var_install}.sh)"; then
    exit $?
  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_info "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"

  # Ermittle die tatsächlichen GIDs aus dem 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")

  # Fallbacks wenn Gruppen nicht existieren
  if [[ -z "$video_gid" ]]; then
    # Versuche die video Gruppe zu erstellen
    pct exec "$CTID" -- sh -c "groupadd -r video 2>/dev/null || true"
    video_gid=$(pct exec "$CTID" -- sh -c "getent group video 2>/dev/null | cut -d: -f3")
    [[ -z "$video_gid" ]] && video_gid="44" # Ultimate fallback
  fi

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

  msg_info "Container GIDs detected - video:${video_gid}, render:${render_gid}"

  # Prüfe ob die GIDs von den Defaults abweichen
  local need_update=0
  if [[ "$video_gid" != "44" ]] || [[ "$render_gid" != "104" ]]; then
    need_update=1
  fi

  if [[ $need_update -eq 1 ]]; then
    msg_info "Updating device GIDs in container config"

    # Stoppe Container für Config-Update
    pct stop "$CTID" >/dev/null 2>&1

    # Update die dev Einträge mit korrekten GIDs
    # Backup der Config
    cp "$LXC_CONFIG" "${LXC_CONFIG}.bak"

    # Parse und update jeden dev Eintrag
    while IFS= read -r line; do
      if [[ "$line" =~ ^dev[0-9]+: ]]; then
        # Extract device path
        local device_path=$(echo "$line" | sed -E 's/^dev[0-9]+: ([^,]+).*/\1/')
        local dev_num=$(echo "$line" | sed -E 's/^(dev[0-9]+):.*/\1/')

        if [[ "$device_path" =~ renderD ]]; then
          # RenderD device - use render GID
          echo "${dev_num}: ${device_path},gid=${render_gid}"
        elif [[ "$device_path" =~ card ]]; then
          # Card device - use video GID
          echo "${dev_num}: ${device_path},gid=${video_gid}"
        else
          # Keep original line
          echo "$line"
        fi
      else
        # Keep non-dev lines
        echo "$line"
      fi
    done <"$LXC_CONFIG" >"${LXC_CONFIG}.new"

    mv "${LXC_CONFIG}.new" "$LXC_CONFIG"

    # Starte Container wieder
    pct start "$CTID" >/dev/null 2>&1
    sleep 3

    msg_ok "Device GIDs updated successfully"
  else
    msg_ok "Device GIDs are already correct"
  fi
  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
}

# NVIDIA-spezific check on host
check_nvidia_host_setup() {
  if ! command -v nvidia-smi >/dev/null 2>&1; then
    msg_warn "NVIDIA GPU detected but nvidia-smi not found on host"
    msg_warn "Please install NVIDIA drivers on host first."
    #echo "  1. Download driver: wget https://us.download.nvidia.com/XFree86/Linux-x86_64/550.127.05/NVIDIA-Linux-x86_64-550.127.05.run"
    #echo "  2. Install: ./NVIDIA-Linux-x86_64-550.127.05.run --dkms"
    #echo "  3. Verify: nvidia-smi"
    return 1
  fi

  # check if nvidia-smi works
  if ! nvidia-smi >/dev/null 2>&1; then
    msg_warn "nvidia-smi installed but not working. Driver issue?"
    return 1
  fi

  return 0
}

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 apt-get update -qq >/dev/null && apt-get install -y --only-upgrade pve-container lxc-pve >/dev/null; 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

  # Validate content types
  msg_info "Validating content types of storage '$CONTAINER_STORAGE'"
  STORAGE_CONTENT=$(grep -A4 -E "^(zfspool|dir|lvmthin|lvm): $CONTAINER_STORAGE" /etc/pve/storage.cfg | grep content | awk '{$1=""; print $0}' | xargs)
  msg_debug "Storage '$CONTAINER_STORAGE' has content types: $STORAGE_CONTENT"
  grep -qw "rootdir" <<<"$STORAGE_CONTENT" || {
    msg_error "Storage '$CONTAINER_STORAGE' does not support 'rootdir'. Cannot create LXC."
    exit 217
  }
  $STD msg_ok "Storage '$CONTAINER_STORAGE' supports 'rootdir'"

  msg_info "Validating content types of template storage '$TEMPLATE_STORAGE'"
  TEMPLATE_CONTENT=$(grep -A4 -E "^[^:]+: $TEMPLATE_STORAGE" /etc/pve/storage.cfg | grep content | awk '{$1=""; print $0}' | xargs)
  msg_debug "Template storage '$TEMPLATE_STORAGE' has content types: $TEMPLATE_CONTENT"
  if ! grep -qw "vztmpl" <<<"$TEMPLATE_CONTENT"; then
    msg_warn "Template storage '$TEMPLATE_STORAGE' does not declare 'vztmpl'. This may cause pct create to fail."
  else
    $STD msg_ok "Template storage '$TEMPLATE_STORAGE' supports 'vztmpl'"
  fi

  # 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
  # ------------------------------------------------------------------------------
  TEMPLATE_SEARCH="${PCT_OSTYPE}-${PCT_OSVERSION:-}"
  case "$PCT_OSTYPE" in
  debian | ubuntu) TEMPLATE_PATTERN="-standard_" ;;
  alpine | fedora | rocky | centos) TEMPLATE_PATTERN="-default_" ;;
  *) 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)."

  #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
  if [[ ${#ONLINE_TEMPLATES[@]} -gt 0 ]]; then
    #echo "[DEBUG] Online templates:"
    for tmpl in "${ONLINE_TEMPLATES[@]}"; do
      echo "  - $tmpl"
    done
  fi

  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_info "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_info "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_info "Please check:"
    msg_info "  - Is pveam catalog available? (run: pveam available -section system)"
    msg_info "  - 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_info "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

  # Assemble pct options
  PCT_OPTIONS=(${PCT_OPTIONS[@]:-${DEFAULT_PCT_OPTIONS[@]}})
  [[ " ${PCT_OPTIONS[*]} " =~ " -rootfs " ]] || PCT_OPTIONS+=(-rootfs "$CONTAINER_STORAGE:${PCT_DISK_SIZE:-8}")

  # 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}.log"
  msg_debug "pct create command: pct create $CTID ${TEMPLATE_STORAGE}:vztmpl/${TEMPLATE} ${PCT_OPTIONS[*]}"
  msg_debug "Logfile: $LOGFILE"

  # First attempt
  if ! pct create "$CTID" "${TEMPLATE_STORAGE}:vztmpl/${TEMPLATE}" "${PCT_OPTIONS[@]}" >"$LOGFILE" 2>&1; then
    msg_error "Container creation failed on ${TEMPLATE_STORAGE}. Checking 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 [[ "$TEMPLATE_STORAGE" != "local" ]]; then
        msg_warn "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
          msg_ok "Container successfully created using local fallback."
        else
          # --- Dynamic stack upgrade + auto-retry on the well-known error pattern ---
          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 even with local fallback. 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
              bash -x -c "pct create $CTID local:vztmpl/${TEMPLATE} ${PCT_OPTIONS[*]}" 2>&1 | tee -a "$LOGFILE"
              set +x
            fi
            exit 209
          fi
        fi
      else
        msg_error "Container creation failed on local storage. See $LOGFILE"
        # --- Dynamic stack upgrade + auto-retry on the well-known error pattern ---
        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
            bash -x -c "pct create $CTID local:vztmpl/${TEMPLATE} ${PCT_OPTIONS[*]}" 2>&1 | tee -a "$LOGFILE"
            set +x
          fi
          exit 209
        fi
      fi
    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."
}

# ------------------------------------------------------------------------------
# description()
#
# - Sets container description with HTML content (logo, links, badges)
# - Restarts ping-instances.service if present
# - Posts status "done" to API
# ------------------------------------------------------------------------------
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"
}

# ------------------------------------------------------------------------------
# api_exit_script()
#
# - Exit trap handler
# - Reports exit codes to API with detailed reason
# - Handles known codes (100–209) and maps them to errors
# ------------------------------------------------------------------------------
api_exit_script() {
  exit_code=$?
  if [ $exit_code -ne 0 ]; then
    case $exit_code in
    100) post_update_to_api "failed" "100: Unexpected error in create_lxc.sh" ;;
    101) post_update_to_api "failed" "101: No network connection detected in create_lxc.sh" ;;
    200) post_update_to_api "failed" "200: LXC creation failed in create_lxc.sh" ;;
    201) post_update_to_api "failed" "201: Invalid Storage class in create_lxc.sh" ;;
    202) post_update_to_api "failed" "202: User aborted menu in create_lxc.sh" ;;
    203) post_update_to_api "failed" "203: CTID not set in create_lxc.sh" ;;
    204) post_update_to_api "failed" "204: PCT_OSTYPE not set in create_lxc.sh" ;;
    205) post_update_to_api "failed" "205: CTID cannot be less than 100 in create_lxc.sh" ;;
    206) post_update_to_api "failed" "206: CTID already in use in create_lxc.sh" ;;
    207) post_update_to_api "failed" "207: Template not found in create_lxc.sh" ;;
    208) post_update_to_api "failed" "208: Error downloading template in create_lxc.sh" ;;
    209) post_update_to_api "failed" "209: Container creation failed, but template is intact in create_lxc.sh" ;;
    *) post_update_to_api "failed" "Unknown error, exit code: $exit_code in create_lxc.sh" ;;
    esac
  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
