#!/usr/bin/env bash

################################################################################
#
#  Pterodactyl/Pelican Panel Installer for Ubuntu 24.04
#
#  A clean, modular installer with clear separation of concerns.
#
################################################################################

set -euo pipefail

################################################################################
# CONFIGURATION & GLOBALS
################################################################################

readonly STATE_DIR="/var/lib/panel-installer"
readonly STATE_FILE="${STATE_DIR}/state.env"

# Installation flags (set by menu selections)
declare -g PANEL_KIND=""           # "pterodactyl" or "pelican"
declare -g DO_WEBSERVER=0
declare -g DO_MARIADB=0
declare -g DO_PANEL=0
declare -g DO_WINGS=0
declare -g DO_BLUEPRINT=0
declare -g WEBSERVER_CHOICE=""     # "apache2" or "nginx"
declare -g CREATE_ROOT_VHOST=0     # Whether to create @ domain vhost

# User configuration (collected via prompts)
declare -g PANEL_HOST_TYPE=""      # "domain" or "ipv4"
declare -g PANEL_HOST_VALUE=""
declare -g ROOT_DOMAIN=""
declare -g PANEL_SUB=""
declare -g NODE_FQDN=""
declare -g NODE_SUB=""

# Credentials
declare -g DB_PASS=""
declare -g DB_GLOBAL_PASS=""
declare -g ADMIN_EMAIL=""
declare -g ADMIN_USER=""
declare -g ADMIN_FN=""
declare -g ADMIN_LN=""
declare -g ADMIN_PASS=""

# Node configuration
declare -g LOCATION_SHORT=""
declare -g NODE_MAX_MEMORY_MIB=""
declare -g NODE_MAX_DISK_MIB=""

# Current operation tracking
declare -g CURRENT_STEP=""

################################################################################
# ERROR HANDLING
################################################################################

on_error() {
  local exit_code=$? line_no=$LINENO
  echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" >&2
  echo "ERROR: Installation failed" >&2
  echo "  Line:  ${line_no}" >&2
  echo "  Step:  ${CURRENT_STEP:-unknown}" >&2
  echo "  Code:  ${exit_code}" >&2
  [[ -f "${STATE_FILE}" ]] && echo "  State: ${STATE_FILE}" >&2
  echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" >&2
  exit "${exit_code}"
}

trap on_error ERR

################################################################################
# PREREQUISITES
################################################################################

check_root() {
  if [[ "$EUID" -ne 0 ]]; then
    echo "This installer must be run as root (use sudo)"
    exit 1
  fi
}

check_tty() {
  # Skip TTY check if running in unattended mode
  [[ "${UNATTENDED_MODE:-0}" == "1" ]] && return 0
  
  if [[ ! -t 0 || ! -t 1 ]]; then
    echo "ERROR: This installer requires an interactive terminal"
    exit 1
  fi
}

check_ubuntu() {
  if [[ ! -f /etc/lsb-release ]] || ! grep -q "Ubuntu" /etc/lsb-release; then
    echo "WARNING: This installer is designed for Ubuntu 24.04"
    read -p "Continue anyway? (y/N) " -n 1 -r
    echo
    [[ ! $REPLY =~ ^[Yy]$ ]] && exit 1
  fi
}

################################################################################
# STATE MANAGEMENT
################################################################################

state_init() {
  mkdir -p "${STATE_DIR}"
  if [[ -f "${STATE_FILE}" ]]; then
    set +u
    # shellcheck disable=SC1090
    source "${STATE_FILE}"
    set -u
  fi
}

state_clear() {
  rm -f "${STATE_FILE}"
  mkdir -p "${STATE_DIR}"
}

state_save() {
  local key="$1" value="$2"
  mkdir -p "${STATE_DIR}"
  
  if [[ ! -f "${STATE_FILE}" ]]; then
    touch "${STATE_FILE}"
    chmod 600 "${STATE_FILE}"
  fi
  
  local escaped="${value//\'/\'\\\'\'}"
  if grep -q "^${key}=" "${STATE_FILE}" 2>/dev/null; then
    sed -i "s|^${key}=.*|${key}='${escaped}'|" "${STATE_FILE}"
  else
    echo "${key}='${escaped}'" >> "${STATE_FILE}"
  fi
}

state_is_complete() {
  local step_key="STEP_${PANEL_KIND}_${1}"
  local val="${!step_key:-}"
  [[ "$val" == "1" ]]
}

state_mark_complete() {
  local step_key="STEP_${PANEL_KIND}_${1}"
  state_save "${step_key}" "1"
}

################################################################################
# STEP RUNNER
################################################################################

run_step() {
  local step_name="$1"
  shift
  local func_name="$1"
  shift
  
  CURRENT_STEP="${step_name}"
  state_save "CURRENT_STEP" "${step_name}"
  
  if state_is_complete "${step_name}"; then
    ui_info "⏭️  Skipping completed step: ${step_name}"
    return 0
  fi
  
  ui_section "${step_name}"
  if ! "$func_name" "$@"; then
    ui_error "Step '${step_name}' failed. Installation aborted."
    exit 1
  fi
  state_mark_complete "${step_name}"
}

################################################################################
# UI FUNCTIONS
################################################################################

ui_section() {
  echo
  if [[ "${UNATTENDED_MODE:-0}" == "1" ]]; then
    echo "==> $*"
  else
    gum style --foreground 51 --bold --border double --padding "1 2" --margin "1 0" "$@"
  fi
}

ui_title() {
  if [[ "${UNATTENDED_MODE:-0}" == "1" ]]; then
    echo "======================================"
    echo "$*"
    echo "======================================"
  else
    clear
    gum style --foreground 51 --border double --align center --width 70 --padding "1 2" --margin "1 2" "$@"
  fi
}

ui_box() {
  if [[ "${UNATTENDED_MODE:-0}" == "1" ]]; then
    echo "$*"
  else
    gum style --border rounded --padding "1 2" --margin "1 0" "$@"
  fi
}

ui_success() {
  if [[ "${UNATTENDED_MODE:-0}" == "1" ]]; then
    echo "[SUCCESS] $*"
  else
    gum style --foreground 42 --bold "✅ $*"
  fi
}

ui_error() {
  if [[ "${UNATTENDED_MODE:-0}" == "1" ]]; then
    echo "[ERROR] $*" >&2
  else
    gum style --foreground 196 --bold "❌ $*"
  fi
}

ui_warn() {
  if [[ "${UNATTENDED_MODE:-0}" == "1" ]]; then
    echo "[WARNING] $*"
  else
    gum style --foreground 214 --bold "⚠️  $*"
  fi
}

ui_info() {
  if [[ "${UNATTENDED_MODE:-0}" == "1" ]]; then
    echo "[INFO] $*"
  else
    gum style --foreground 45 --bold "ℹ️  $*"
  fi
}

ui_confirm() {
  if [[ "${UNATTENDED_MODE:-0}" == "1" ]]; then
    return 0  # Auto-confirm in unattended mode
  fi
  gum confirm "$1" || { echo "Aborted."; exit 1; }
}

################################################################################
# UTILITY FUNCTIONS
################################################################################

generate_password() {
  tr -dc 'A-Za-z0-9_@#%+=' </dev/urandom | head -c 24
}

get_architecture() {
  [[ "$(uname -m)" == "x86_64" ]] && echo "amd64" || echo "arm64"
}

pkg_installed() {
  dpkg -s "$1" &>/dev/null
}

cmd_exists() {
  command -v "$1" &>/dev/null
}

################################################################################
# DETECTION FUNCTIONS
################################################################################

is_pterodactyl_installed() {
  [[ -f /var/www/pterodactyl/artisan ]]
}

is_pelican_installed() {
  [[ -f /var/www/pelican/artisan ]]
}

is_blueprint_installed() {
  cmd_exists blueprint || [[ -f /var/www/pterodactyl/blueprint.sh ]]
}

is_wings_installed() {
  cmd_exists wings
}

################################################################################
# GUM INSTALLATION
################################################################################

ensure_gum() {
  # Skip gum installation in unattended mode if not needed
  if [[ "${UNATTENDED_MODE:-0}" == "1" ]]; then
    return 0
  fi
  
  if cmd_exists gum; then
    return 0
  fi
  
  echo "Installing gum (required for UI)..."
  mkdir -p /etc/apt/keyrings
  curl -fsSL https://repo.charm.sh/apt/gpg.key | gpg --dearmor -o /etc/apt/keyrings/charm.gpg
  echo "deb [signed-by=/etc/apt/keyrings/charm.gpg] https://repo.charm.sh/apt/ * *" > /etc/apt/sources.list.d/charm.list
  apt update
  apt install -y gum
}

################################################################################
# UNATTENDED MODE PARAMETER PARSING
################################################################################

show_usage() {
  cat <<'USAGE'
Usage: ./install_pterodactyl.sh [OPTIONS]

Interactive Mode (default):
  Run without arguments for interactive menu-driven installation

Unattended Mode:
  --unattended              Enable unattended installation
  --panel-type TYPE         Panel type: "pterodactyl" or "pelican"
  --install-mode MODE       Mode: "full" or "custom"
  
Host Configuration:
  --host-type TYPE          Host type: "domain" or "ipv4"
  --host-value VALUE        Domain/FQDN or IPv4 address
  --root-domain DOMAIN      Root domain (required if host-type=domain)
  --panel-sub SUB           Panel subdomain (default: panel, @ for root)
  --create-root-vhost       Create vhost for root domain

Wings Configuration:
  --node-fqdn FQDN          Wings public address
  --node-sub SUB            Wings subdomain (default: node)

Component Selection (custom mode):
  --install-webserver       Install webserver
  --install-mariadb         Install MariaDB
  --install-panel           Install panel
  --install-wings           Install Wings
  --install-blueprint       Install Blueprint (Pterodactyl only)
  --webserver-choice WS     Webserver: "apache2" or "nginx" (default: apache2)

Admin Account (panel installation):
  --admin-email EMAIL       Admin email address
  --admin-user USERNAME     Admin username
  --admin-firstname NAME    Admin first name
  --admin-lastname NAME     Admin last name
  --admin-password PASS     Admin password

Node Resources (panel installation):
  --location-short CODE     Location short code (default: LOC-1)
  --node-memory MIB         Max memory in MiB (default: auto-detect)
  --node-disk MIB           Max disk in MiB (default: auto-detect)

Database (optional):
  --db-password PASS        Database password (auto-generated if not set)
  --db-global-password PASS Global admin password (auto-generated if not set)

Examples:

  # Full Pterodactyl installation with domain
  sudo ./install_pterodactyl.sh --unattended \
    --panel-type pterodactyl \
    --install-mode full \
    --host-type domain \
    --root-domain example.com \
    --panel-sub panel \
    --node-sub node \
    --admin-email admin@example.com \
    --admin-user admin \
    --admin-firstname Admin \
    --admin-lastname User \
    --admin-password SecurePass123

  # Custom Pterodactyl installation (only panel + webserver)
  sudo ./install_pterodactyl.sh --unattended \
    --panel-type pterodactyl \
    --install-mode custom \
    --install-webserver \
    --install-mariadb \
    --install-panel \
    --webserver-choice apache2 \
    --host-type domain \
    --root-domain example.com \
    --admin-email admin@example.com \
    --admin-user admin \
    --admin-firstname Admin \
    --admin-lastname User \
    --admin-password SecurePass123

  # Full Pelican installation with IPv4
  sudo ./install_pterodactyl.sh --unattended \
    --panel-type pelican \
    --install-mode full \
    --host-type ipv4 \
    --host-value 203.0.113.10 \
    --node-fqdn 203.0.113.10 \
    --webserver-choice nginx

USAGE
}

parse_arguments() {
  # Return if no arguments (interactive mode)
  [[ $# -eq 0 ]] && return 0
  
  # Check for help
  if [[ "$1" == "--help" || "$1" == "-h" ]]; then
    show_usage
    exit 0
  fi
  
  # Parse arguments
  while [[ $# -gt 0 ]]; do
    case "$1" in
      --unattended)
        UNATTENDED_MODE=1
        shift
        ;;
      --panel-type)
        PANEL_KIND="$2"
        state_save "PANEL_KIND" "$2"
        shift 2
        ;;
      --install-mode)
        INSTALL_MODE="$2"
        shift 2
        ;;
      --host-type)
        PANEL_HOST_TYPE="$2"
        state_save "PANEL_HOST_TYPE" "$2"
        shift 2
        ;;
      --host-value)
        PANEL_HOST_VALUE="$2"
        state_save "PANEL_HOST_VALUE" "$2"
        shift 2
        ;;
      --root-domain)
        ROOT_DOMAIN="$2"
        state_save "ROOT_DOMAIN" "$2"
        shift 2
        ;;
      --panel-sub)
        PANEL_SUB="$2"
        state_save "PANEL_SUB" "$2"
        shift 2
        ;;
      --node-fqdn)
        NODE_FQDN="$2"
        state_save "NODE_FQDN" "$2"
        shift 2
        ;;
      --node-sub)
        NODE_SUB="$2"
        state_save "NODE_SUB" "$2"
        shift 2
        ;;
      --create-root-vhost)
        CREATE_ROOT_VHOST=1
        state_save "CREATE_ROOT_VHOST" "1"
        shift
        ;;
      --install-webserver)
        DO_WEBSERVER=1
        shift
        ;;
      --install-mariadb)
        DO_MARIADB=1
        shift
        ;;
      --install-panel)
        DO_PANEL=1
        shift
        ;;
      --install-wings)
        DO_WINGS=1
        shift
        ;;
      --install-blueprint)
        DO_BLUEPRINT=1
        shift
        ;;
      --webserver-choice)
        WEBSERVER_CHOICE="$2"
        state_save "WEBSERVER_CHOICE" "$2"
        shift 2
        ;;
      --admin-email)
        ADMIN_EMAIL="$2"
        state_save "ADMIN_EMAIL" "$2"
        shift 2
        ;;
      --admin-user)
        ADMIN_USER="$2"
        state_save "ADMIN_USER" "$2"
        shift 2
        ;;
      --admin-firstname)
        ADMIN_FN="$2"
        state_save "ADMIN_FN" "$2"
        shift 2
        ;;
      --admin-lastname)
        ADMIN_LN="$2"
        state_save "ADMIN_LN" "$2"
        shift 2
        ;;
      --admin-password)
        ADMIN_PASS="$2"
        state_save "ADMIN_PASS" "$2"
        shift 2
        ;;
      --location-short)
        LOCATION_SHORT="$2"
        state_save "LOCATION_SHORT" "$2"
        shift 2
        ;;
      --node-memory)
        NODE_MAX_MEMORY_MIB="$2"
        state_save "NODE_MAX_MEMORY_MIB" "$2"
        shift 2
        ;;
      --node-disk)
        NODE_MAX_DISK_MIB="$2"
        state_save "NODE_MAX_DISK_MIB" "$2"
        shift 2
        ;;
      --db-password)
        DB_PASS="$2"
        state_save "DB_PASS" "$2"
        shift 2
        ;;
      --db-global-password)
        DB_GLOBAL_PASS="$2"
        state_save "DB_GLOBAL_PASS" "$2"
        shift 2
        ;;
      *)
        echo "Unknown option: $1"
        echo "Use --help for usage information"
        exit 1
        ;;
    esac
  done
}

validate_unattended_config() {
  [[ "${UNATTENDED_MODE:-0}" != "1" ]] && return 0
  
  local errors=0
  
  # Validate panel type
  if [[ -z "${PANEL_KIND}" ]]; then
    echo "ERROR: --panel-type is required in unattended mode" >&2
    errors=$((errors + 1))
  elif [[ "${PANEL_KIND}" != "pterodactyl" && "${PANEL_KIND}" != "pelican" ]]; then
    echo "ERROR: --panel-type must be 'pterodactyl' or 'pelican'" >&2
    errors=$((errors + 1))
  fi
  
  # Validate install mode
  if [[ -z "${INSTALL_MODE}" ]]; then
    echo "ERROR: --install-mode is required in unattended mode" >&2
    errors=$((errors + 1))
  elif [[ "${INSTALL_MODE}" != "full" && "${INSTALL_MODE}" != "custom" ]]; then
    echo "ERROR: --install-mode must be 'full' or 'custom'" >&2
    errors=$((errors + 1))
  fi
  
  # Set defaults for full install
  if [[ "${INSTALL_MODE}" == "full" ]]; then
    DO_WEBSERVER=1
    DO_MARIADB=1
    DO_PANEL=1
    DO_WINGS=1
    [[ "${PANEL_KIND}" == "pterodactyl" ]] && DO_BLUEPRINT=1
    [[ -z "${WEBSERVER_CHOICE}" ]] && WEBSERVER_CHOICE="apache2"
  fi
  
  # Validate host configuration
  if (( DO_PANEL || DO_WEBSERVER )); then
    if [[ -z "${PANEL_HOST_TYPE}" ]]; then
      echo "ERROR: --host-type is required" >&2
      errors=$((errors + 1))
    elif [[ "${PANEL_HOST_TYPE}" == "domain" ]]; then
      if [[ -z "${ROOT_DOMAIN}" ]]; then
        echo "ERROR: --root-domain is required when --host-type=domain" >&2
        errors=$((errors + 1))
      fi
      # Set default panel subdomain
      [[ -z "${PANEL_SUB}" ]] && PANEL_SUB="panel"
      # Calculate PANEL_HOST_VALUE
      if [[ "${PANEL_SUB}" == "@" ]]; then
        PANEL_HOST_VALUE="${ROOT_DOMAIN}"
      else
        PANEL_HOST_VALUE="${PANEL_SUB}.${ROOT_DOMAIN}"
      fi
      state_save "PANEL_HOST_VALUE" "${PANEL_HOST_VALUE}"
    elif [[ "${PANEL_HOST_TYPE}" == "ipv4" ]]; then
      if [[ -z "${PANEL_HOST_VALUE}" ]]; then
        echo "ERROR: --host-value is required when --host-type=ipv4" >&2
        errors=$((errors + 1))
      fi
    else
      echo "ERROR: --host-type must be 'domain' or 'ipv4'" >&2
      errors=$((errors + 1))
    fi
  fi
  
  # Validate Wings configuration
  if (( DO_WINGS )); then
    if [[ -z "${NODE_FQDN}" ]]; then
      if [[ "${PANEL_HOST_TYPE}" == "domain" ]]; then
        # Auto-configure Wings subdomain
        [[ -z "${NODE_SUB}" ]] && NODE_SUB="node"
        if [[ "${NODE_SUB}" == "@" ]]; then
          NODE_FQDN="${ROOT_DOMAIN}"
        else
          NODE_FQDN="${NODE_SUB}.${ROOT_DOMAIN}"
        fi
        state_save "NODE_FQDN" "${NODE_FQDN}"
      else
        echo "ERROR: --node-fqdn is required for Wings installation" >&2
        errors=$((errors + 1))
      fi
    fi
  fi
  
  # Validate admin account (only for Pterodactyl full/custom with panel)
  if (( DO_PANEL )) && [[ "${PANEL_KIND}" == "pterodactyl" ]]; then
    if [[ -z "${ADMIN_EMAIL}" ]]; then
      echo "ERROR: --admin-email is required for panel installation" >&2
      errors=$((errors + 1))
    fi
    [[ -z "${ADMIN_USER}" ]] && ADMIN_USER="admin"
    [[ -z "${ADMIN_FN}" ]] && ADMIN_FN="Admin"
    [[ -z "${ADMIN_LN}" ]] && ADMIN_LN="User"
    if [[ -z "${ADMIN_PASS}" ]]; then
      echo "ERROR: --admin-password is required for panel installation" >&2
      errors=$((errors + 1))
    fi
  fi
  
  # Set node resource defaults
  if (( DO_PANEL )) && [[ "${PANEL_KIND}" == "pterodactyl" ]]; then
    [[ -z "${LOCATION_SHORT}" ]] && LOCATION_SHORT="LOC-1"
    if [[ -z "${NODE_MAX_MEMORY_MIB}" ]]; then
      NODE_MAX_MEMORY_MIB=$(free -m | awk '/^Mem:/{print $2}')
    fi
    if [[ -z "${NODE_MAX_DISK_MIB}" ]]; then
      NODE_MAX_DISK_MIB=$(df -m / | awk 'NR==2{print $2}')
    fi
  fi
  
  # Validate webserver choice
  if (( DO_WEBSERVER )); then
    [[ -z "${WEBSERVER_CHOICE}" ]] && WEBSERVER_CHOICE="apache2"
    if [[ "${WEBSERVER_CHOICE}" != "apache2" && "${WEBSERVER_CHOICE}" != "nginx" ]]; then
      echo "ERROR: --webserver-choice must be 'apache2' or 'nginx'" >&2
      errors=$((errors + 1))
    fi
  fi
  
  if (( errors > 0 )); then
    echo
    echo "Use --help to see all available options"
    exit 1
  fi
  
  return 0
}

################################################################################
# INPUT COLLECTION
################################################################################

collect_host_config() {
  ui_section "Host Configuration"
  
  local mode
  mode=$(gum choose --limit 1 "Domain/FQDN (recommended)" "IPv4 address")
  
  if [[ "$mode" == "IPv4 address" ]]; then
    PANEL_HOST_TYPE="ipv4"
    echo "Panel IPv4 (e.g. 203.0.113.10)"
    PANEL_HOST_VALUE=$(gum input --placeholder "203.0.113.10" --prompt "> ")
    : "${PANEL_HOST_VALUE:?IPv4 required}"
  else
    PANEL_HOST_TYPE="domain"
    echo "Root Domain (e.g. example.com)"
    ROOT_DOMAIN=$(gum input --placeholder "example.com" --prompt "> ")
    : "${ROOT_DOMAIN:?Domain required}"
    
    echo "Panel Subdomain (default: panel, @ for root)"
    PANEL_SUB=$(gum input --placeholder "panel" --value "panel" --prompt "> ")
    if [[ "$PANEL_SUB" == "@" ]]; then
      PANEL_HOST_VALUE="${ROOT_DOMAIN}"
    else
      PANEL_HOST_VALUE="${PANEL_SUB}.${ROOT_DOMAIN}"
    fi
  fi
  
  state_save "PANEL_HOST_TYPE" "${PANEL_HOST_TYPE}"
  state_save "PANEL_HOST_VALUE" "${PANEL_HOST_VALUE}"
  state_save "ROOT_DOMAIN" "${ROOT_DOMAIN}"
  state_save "PANEL_SUB" "${PANEL_SUB}"
}

collect_wings_config() {
  ui_section "Wings Configuration"
  
  if [[ "${PANEL_HOST_TYPE}" == "domain" ]]; then
    echo "Wings Subdomain (default: node, @ for root)"
    NODE_SUB=$(gum input --placeholder "node" --value "node" --prompt "> ")
    if [[ "$NODE_SUB" == "@" ]]; then
      NODE_FQDN="${ROOT_DOMAIN}"
    else
      NODE_FQDN="${NODE_SUB}.${ROOT_DOMAIN}"
    fi
  else
    echo "Wings Public Address (IPv4 or DNS)"
    NODE_FQDN=$(gum input --placeholder "203.0.113.10" --prompt "> ")
    : "${NODE_FQDN:?Wings address required}"
  fi
  
  state_save "NODE_FQDN" "${NODE_FQDN}"
  state_save "NODE_SUB" "${NODE_SUB}"
}

show_dns_instructions() {
  [[ "${PANEL_HOST_TYPE}" != "domain" ]] && return 0
  [[ "${UNATTENDED_MODE:-0}" == "1" ]] && return 0  # Skip in unattended mode
  
  # Get server IP for DNS instructions
  local server_ip
  server_ip=$(curl -s https://api.ipify.org || hostname -I | awk '{print $1}')
  
  # Build DNS records list
  local dns_records="Before continuing, add these DNS records:

Required Records:
  Type: A    Name: ${PANEL_SUB}    Target: ${server_ip}"
  
  # Add Wings record if configured
  if [[ -n "${NODE_SUB:-}" ]]; then
    dns_records+="
  Type: A    Name: ${NODE_SUB}           Target: ${server_ip}"
  fi
  
  dns_records+="

Recommended (for direct domain server access):
  Type: A    Name: @              Target: ${server_ip}

The @ record allows players to join via ${ROOT_DOMAIN}
Add these records in your DNS provider's control panel."
  
  # Show DNS records information
  ui_section "DNS Configuration Required"
  ui_box "$dns_records"
  
  read -p "Press enter when DNS records are configured..." -r
  
  # Ask about @ record
  if gum confirm "Did you add the @ (root domain) record?"; then
    CREATE_ROOT_VHOST=1
    state_save "CREATE_ROOT_VHOST" "1"
  else
    CREATE_ROOT_VHOST=0
    state_save "CREATE_ROOT_VHOST" "0"
  fi
}

collect_admin_config() {
  ui_section "Admin Account"
  
  echo "Admin Email"
  ADMIN_EMAIL=$(gum input --placeholder "admin@${ROOT_DOMAIN:-example.com}" --value "admin@${ROOT_DOMAIN:-example.com}" --prompt "> ")
  : "${ADMIN_EMAIL:?Email required}"
  
  echo "Admin Username"
  ADMIN_USER=$(gum input --placeholder "admin" --value "admin" --prompt "> ")
  : "${ADMIN_USER:?Username required}"
  
  echo "Admin First Name"
  ADMIN_FN=$(gum input --placeholder "Admin" --value "Admin" --prompt "> ")
  : "${ADMIN_FN:?First name required}"
  
  echo "Admin Last Name"
  ADMIN_LN=$(gum input --placeholder "User" --value "User" --prompt "> ")
  : "${ADMIN_LN:?Last name required}"
  
  echo "Admin Password"
  ADMIN_PASS=$(gum input --password --placeholder "password" --prompt "> ")
  : "${ADMIN_PASS:?Password required}"
  
  state_save "ADMIN_EMAIL" "${ADMIN_EMAIL}"
  state_save "ADMIN_USER" "${ADMIN_USER}"
  state_save "ADMIN_FN" "${ADMIN_FN}"
  state_save "ADMIN_LN" "${ADMIN_LN}"
  state_save "ADMIN_PASS" "${ADMIN_PASS}"
}

collect_node_resources() {
  ui_section "Node Resources"
  
  local total_mem total_disk
  total_mem=$(free -m | awk '/^Mem:/{print $2}')
  total_disk=$(df -m / | awk 'NR==2{print $2}')
  
  echo "Location Short Code (e.g. LOC-1)"
  LOCATION_SHORT=$(gum input --placeholder "LOC-1" --value "LOC-1" --prompt "> ")
  : "${LOCATION_SHORT:?Location required}"
  
  echo "Node Max Memory in MiB"
  NODE_MAX_MEMORY_MIB=$(gum input --placeholder "${total_mem}" --value "${total_mem}" --prompt "> ")
  : "${NODE_MAX_MEMORY_MIB:?Memory required}"
  
  echo "Node Max Disk in MiB"
  NODE_MAX_DISK_MIB=$(gum input --placeholder "${total_disk}" --value "${total_disk}" --prompt "> ")
  : "${NODE_MAX_DISK_MIB:?Disk required}"
  
  state_save "LOCATION_SHORT" "${LOCATION_SHORT}"
  state_save "NODE_MAX_MEMORY_MIB" "${NODE_MAX_MEMORY_MIB}"
  state_save "NODE_MAX_DISK_MIB" "${NODE_MAX_DISK_MIB}"
}

collect_webserver_choice() {
  ui_section "Webserver Selection"
  WEBSERVER_CHOICE=$(gum choose --limit 1 "apache2" "nginx")
  state_save "WEBSERVER_CHOICE" "${WEBSERVER_CHOICE}"
}

################################################################################
# COMPONENT SELECTION
################################################################################

select_components() {
  local panel_type="$1"
  
  ui_section "Component Selection"
  
  local -a options=("Webserver" "MariaDB" "Panel" "Wings")
  [[ "$panel_type" == "Pterodactyl" ]] && options+=("Blueprint")
  
  local -a preselect=()
  for opt in "${options[@]}"; do
    case "$opt" in
      Webserver) preselect+=(--selected "$opt") ;;
      MariaDB) pkg_installed mariadb-server || preselect+=(--selected "$opt") ;;
      Panel) preselect+=(--selected "$opt") ;;
      Wings) cmd_exists wings || preselect+=(--selected "$opt") ;;
      Blueprint) [[ -f /var/www/pterodactyl/blueprint.sh ]] || preselect+=(--selected "$opt") ;;
    esac
  done
  
  local selected
  selected=$(gum choose --no-limit "${preselect[@]}" "${options[@]}")
  
  # Parse selections
  DO_WEBSERVER=0 DO_MARIADB=0 DO_PANEL=0 DO_WINGS=0 DO_BLUEPRINT=0
  while IFS= read -r item; do
    case "$item" in
      Webserver) DO_WEBSERVER=1 ;;
      MariaDB) DO_MARIADB=1 ;;
      Panel) DO_PANEL=1 ;;
      Wings) DO_WINGS=1 ;;
      Blueprint) DO_BLUEPRINT=1 ;;
    esac
  done <<< "$selected"
  
  # Enforce dependencies
  if (( DO_PANEL )); then
    if (( ! DO_MARIADB )); then
      ui_warn "Panel requires MariaDB - enabling automatically"
      DO_MARIADB=1
    fi
    if (( ! DO_WEBSERVER )); then
      ui_warn "Panel requires webserver - enabling automatically"
      DO_WEBSERVER=1
    fi
  fi
}

show_install_summary() {
  local summary="Installation Summary

Panel Type:       ${PANEL_KIND}
Host:             ${PANEL_HOST_VALUE}"
  
  [[ -n "${WEBSERVER_CHOICE}" ]] && summary+="
Webserver:        ${WEBSERVER_CHOICE}"
  
  summary+="

Components:"
  (( DO_WEBSERVER )) && summary+="
  ✓ Webserver"
  (( DO_MARIADB )) && summary+="
  ✓ MariaDB"
  (( DO_PANEL )) && summary+="
  ✓ Panel"
  (( DO_WINGS )) && summary+="
  ✓ Wings"
  (( DO_BLUEPRINT )) && summary+="
  ✓ Blueprint"
  
  if [[ -n "${ADMIN_EMAIL:-}" ]]; then
    summary+="

Admin Account:
  Email:    ${ADMIN_EMAIL}
  Username: ${ADMIN_USER}"
  fi
  
  ui_box "$summary"
  
  # Skip confirmation in unattended mode
  [[ "${UNATTENDED_MODE:-0}" == "1" ]] && return 0
  
  ui_confirm "Proceed with installation?"
}

################################################################################
# BASE DEPENDENCIES
################################################################################

install_nodejs_stack() {
  ui_success "Installing Node.js stack..."
  
  # Check if Node.js is already installed
  if ! command -v node &>/dev/null; then
    ui_info "Installing Node.js v24 globally..."
    curl -sL https://deb.nodesource.com/setup_24.x | bash -
    apt install -y nodejs
  else
    ui_info "Node.js already installed ($(node -v))"
  fi
  
  # Install Yarn globally if not present
  if ! command -v yarn &>/dev/null; then
    ui_info "Installing Yarn globally..."
    npm i -g yarn
  else
    ui_info "Yarn already installed"
  fi
  
  ui_success "Node.js stack ready (Node v24 + Yarn)"
}

install_common_dependencies() {
  ui_success "Installing common dependencies..."
  export DEBIAN_FRONTEND=noninteractive
  
  apt -y install software-properties-common curl apt-transport-https ca-certificates gnupg
  LC_ALL=C.UTF-8 add-apt-repository -y ppa:ondrej/php
  apt update && apt upgrade -y

  sudo debconf-set-selections <<EOF
phpmyadmin phpmyadmin/reconfigure-webserver multiselect apache2
EOF

  # PHP 8.4
  apt -y install php8.4 php8.4-{common,cli,gd,mysql,mbstring,bcmath,xml,fpm,curl,zip,sqlite3,intl} \
                 libapache2-mod-php8.4 tar unzip git \
                 mariadb-server phpmyadmin
  apt purge -y 'php8.5*' 2>/dev/null || true
  
  # Composer
  curl -sS https://getcomposer.org/installer | php -- --install-dir=/usr/local/bin --filename=composer
  
  # Node.js stack
  install_nodejs_stack

  if ! cmd_exists docker; then
    ui_success "Installing Docker..."
    curl -sSL https://get.docker.com/ | CHANNEL=stable bash
    systemctl enable --now docker
  else
    ui_info "Docker already installed"
  fi
  
  ui_success "Common dependencies installed"
}

install_pterodactyl_dependencies() {
  install_common_dependencies
}

install_pelican_dependencies() {
  install_common_dependencies
}

################################################################################
# MARIADB INSTALLATION
################################################################################

install_mariadb() {
  ui_success "Configuring MariaDB..."
  
  # Generate passwords
  DB_PASS="${DB_PASS:-$(generate_password)}"
  DB_GLOBAL_PASS="${DB_GLOBAL_PASS:-$(generate_password)}"
  state_save "DB_PASS" "${DB_PASS}"
  state_save "DB_GLOBAL_PASS" "${DB_GLOBAL_PASS}"
  
  # Determine username based on panel
  local db_user
  [[ "${PANEL_KIND}" == "pelican" ]] && db_user="pelican" || db_user="pterodactyl"
  
  # Create database and users
  ui_info "Creating database and users..."
  if ! mysql -u root <<SQL
CREATE USER IF NOT EXISTS '${db_user}'@'127.0.0.1' IDENTIFIED BY '${DB_PASS}';
CREATE DATABASE IF NOT EXISTS panel;
GRANT ALL PRIVILEGES ON panel.* TO '${db_user}'@'127.0.0.1' WITH GRANT OPTION;
CREATE USER IF NOT EXISTS 'admin'@'%' IDENTIFIED BY '${DB_GLOBAL_PASS}';
GRANT ALL PRIVILEGES ON *.* TO 'admin'@'%' WITH GRANT OPTION;
FLUSH PRIVILEGES;
SQL
  then
    ui_error "Failed to configure MariaDB. Please check MariaDB is running and accessible."
    return 1
  fi
  
  # Enable remote access
  ui_info "Enabling remote access..."
  sed -i 's/^\s*bind-address\s*=.*/bind-address = 0.0.0.0/' /etc/mysql/mariadb.conf.d/50-server.cnf
  systemctl restart mariadb
  
  ui_success "MariaDB configured"
}

################################################################################
# WEBSERVER INSTALLATION
################################################################################

ensure_snapd() {
  if ! cmd_exists snap; then
    apt update
    apt -y install snapd
    systemctl enable --now snapd
  fi
}

install_webserver() {
  local server="$1"
  ui_success "Installing ${server}..."
  
  if [[ "$server" == "apache2" ]]; then
    apt -y install apache2
    a2enmod rewrite ssl proxy proxy_fcgi setenvif
    a2enconf php8.4-fpm 2>/dev/null || true
  else
    apt -y install nginx
    rm -f /etc/nginx/sites-enabled/default
  fi
  
  # Install Certbot
  ensure_snapd
  snap install --classic certbot
  ln -sf /snap/bin/certbot /usr/bin/certbot
  
  ui_success "${server} installed"
}

obtain_ssl_certificate() {
  [[ "${PANEL_HOST_TYPE}" != "domain" ]] && return 0
  
  ui_success "Obtaining SSL certificates..."
  
  local plugin
  [[ "${WEBSERVER_CHOICE}" == "apache2" ]] && plugin="--apache" || plugin="--nginx"
  
  certbot certonly "${plugin}" -d "${PANEL_HOST_VALUE}" \
    --non-interactive --agree-tos -m "admin@${ROOT_DOMAIN}" || true
  
  if (( DO_WINGS )) && [[ "${NODE_FQDN}" != "${PANEL_HOST_VALUE}" ]]; then
    certbot certonly "${plugin}" -d "${NODE_FQDN}" \
      --non-interactive --agree-tos -m "admin@${ROOT_DOMAIN}" || true
  fi
  
  # Configure root domain with certbot if user added @ record
  if (( CREATE_ROOT_VHOST )) && [[ "${ROOT_DOMAIN}" != "${PANEL_HOST_VALUE}" ]]; then
    ui_info "Configuring root domain: ${ROOT_DOMAIN}"
    certbot "${plugin}" -d "${ROOT_DOMAIN}" \
      --non-interactive --agree-tos -m "admin@${ROOT_DOMAIN}" || true
  fi
  
  ui_success "SSL certificates obtained"
}

create_apache_vhost() {
  local panel_dir="$1"
  local conf_name="$2"
  
  [[ "${WEBSERVER_CHOICE}" != "apache2" ]] && return 0
  [[ ! -d "${panel_dir}/public" ]] && { ui_warn "Panel files not found - vhost will be created later"; return 0; }
  [[ -f "/etc/apache2/sites-available/${conf_name}.conf" ]] && return 0
  
  ui_success "Creating Apache vhost..."
  
  local docroot="${panel_dir}/public"
  
  if [[ "${PANEL_HOST_TYPE}" == "domain" ]] && \
     [[ -f "/etc/letsencrypt/live/${PANEL_HOST_VALUE}/fullchain.pem" ]]; then
    # HTTPS configuration
    cat > "/etc/apache2/sites-available/${conf_name}.conf" <<VHOST
<VirtualHost *:80>
  ServerName ${PANEL_HOST_VALUE}
  RewriteEngine On
  RewriteCond %{HTTPS} !=on
  RewriteRule ^/?(.*) https://%{SERVER_NAME}/\$1 [R,L]
</VirtualHost>

<VirtualHost *:443>
  ServerName ${PANEL_HOST_VALUE}
  DocumentRoot "${docroot}"
  AllowEncodedSlashes On
  
  <Directory "${docroot}">
    Require all granted
    AllowOverride all
  </Directory>
  
  <FilesMatch \.php$>
    SetHandler "proxy:unix:/run/php/php8.4-fpm.sock|fcgi://localhost"
  </FilesMatch>
  
  SSLEngine on
  SSLCertificateFile /etc/letsencrypt/live/${PANEL_HOST_VALUE}/fullchain.pem
  SSLCertificateKeyFile /etc/letsencrypt/live/${PANEL_HOST_VALUE}/privkey.pem
</VirtualHost>
VHOST
  else
    # HTTP-only configuration
    cat > "/etc/apache2/sites-available/${conf_name}.conf" <<VHOST
<VirtualHost *:80>
  ServerName ${PANEL_HOST_VALUE}
  DocumentRoot "${docroot}"
  AllowEncodedSlashes On
  
  <Directory "${docroot}">
    Require all granted
    AllowOverride all
  </Directory>
  
  <FilesMatch \.php$>
    SetHandler "proxy:unix:/run/php/php8.4-fpm.sock|fcgi://localhost"
  </FilesMatch>
</VirtualHost>
VHOST
  fi
  
  a2ensite "${conf_name}.conf"
  systemctl reload apache2
  ui_success "Apache vhost created"
}

create_nginx_vhost() {
  local panel_dir="$1"
  local conf_name="$2"
  
  [[ "${WEBSERVER_CHOICE}" != "nginx" ]] && return 0
  [[ ! -d "${panel_dir}/public" ]] && { ui_warn "Panel files not found - vhost will be created later"; return 0; }
  [[ -f "/etc/nginx/sites-available/${conf_name}" ]] && return 0
  
  ui_success "Creating Nginx vhost..."
  
  # Nginx configuration would go here
  # (Simplified for brevity - add if needed)
  
  ui_success "Nginx vhost created"
}

################################################################################
# PTERODACTYL INSTALLATION
################################################################################

download_pterodactyl_panel() {
  ui_success "Downloading Pterodactyl panel..."
  
  mkdir -p /var/www/pterodactyl
  cd /var/www/pterodactyl
  
  curl -Lo panel.tar.gz https://github.com/pterodactyl/panel/releases/latest/download/panel.tar.gz
  tar -xzvf panel.tar.gz
  rm panel.tar.gz
  
  chmod -R 755 storage/* bootstrap/cache/
  
  ui_success "Installing PHP dependencies..."
  COMPOSER_ALLOW_SUPERUSER=1 composer install --no-dev --optimize-autoloader
  
  cd -
  ui_success "Pterodactyl panel downloaded"
}

configure_pterodactyl_panel() {
  ui_success "Configuring Pterodactyl..."
  
  cd /var/www/pterodactyl
  
  cp .env.example .env
  php artisan key:generate --force
  
  php artisan p:environment:setup \
    --author="panel@${ROOT_DOMAIN}" \
    --url="https://${PANEL_HOST_VALUE}" \
    --timezone="Europe/Berlin" \
    --cache="file" \
    --session="database" \
    --queue="database" \
    --no-interaction
  
  php artisan p:environment:database \
    --host="127.0.0.1" \
    --port="3306" \
    --database="panel" \
    --username="pterodactyl" \
    --password="${DB_PASS}" \
    --no-interaction <<< "yes"
  
  php artisan migrate --seed --force

  if ! mysql -u root <<'SQL'
use panel;
DELETE FROM eggs WHERE eggs.nest_id = 1;
INSERT INTO `eggs` (`id`, `uuid`, `nest_id`, `author`, `name`, `description`, `features`,`docker_images`, `file_denylist`, `update_url`, `config_files`, `config_startup`,`config_logs`, `config_stop`, `config_from`, `startup`, `script_container`,`copy_script_from`, `script_entry`, `script_is_privileged`, `script_install`,`created_at`, `updated_at`, `force_outgoing_ip`)
 VALUES (
  '15',
  'e17ec02b-3d0e-476d-9eea-9ffdc4302309',
  '1',
  'rjansengd@gmail.com',
  'Java Minecraft',
  'Minecraft is a game about placing blocks and going on adventures. Explore randomly generated worlds and build amazing things from the simplest of homes to the grandest of castles. Play in Creative Mode with unlimited resources or mine deep in Survival Mode, crafting weapons and armor to fend off dangerous mobs. Do all this alone or with friends.',
  '[\"eula\",\"java_version\",\"pid_limit\"]',
  '{\"Java 24\":\"ghcr.io\\/mcjars\\/pterodactyl-yolks:java_24\",\"Java 23\":\"ghcr.io\\/mcjars\\/pterodactyl-yolks:java_23\",\"Java 22\":\"ghcr.io\\/mcjars\\/pterodactyl-yolks:java_22\",\"Java 21\":\"ghcr.io\\/mcjars\\/pterodactyl-yolks:java_21\",\"Java 19\":\"ghcr.io\\/mcjars\\/pterodactyl-yolks:java_19\",\"Java 18\":\"ghcr.io\\/mcjars\\/pterodactyl-yolks:java_18\",\"Java 17\":\"ghcr.io\\/mcjars\\/pterodactyl-yolks:java_17\",\"Java 16\":\"ghcr.io\\/mcjars\\/pterodactyl-yolks:java_16\",\"Java 11\":\"ghcr.io\\/mcjars\\/pterodactyl-yolks:java_11\",\"Java 8\":\"ghcr.io\\/mcjars\\/pterodactyl-yolks:java_8\"}',
  '[]',
  NULL,
  '{}',
  '{\r\n \"done\": [\r\n \")! For help, type \",\r\n \"Listening on /\",\r\n \"Server started on\",\r\n \"Limbo server listening on\"\r\n ]\r\n}',
  '{}',
  '^C',
  NULL,
  'java -Dterminal.jline=false -Dterminal.ansi=true -Xms256M -Xmx$(({{SERVER_MEMORY}}*{{MAXIMUM_RAM}}/100))M -jar {{SERVER_JARFILE}} nogui',
  'ghcr.io/pterodactyl/installers:debian',
  NULL,
  'bash',
  '1',
  '#!/bin/bash\r\n# MCJars API Installation Script\r\n#\r\n# Server Files: /mnt/server\r\nmkdir -p /mnt/server\r\ncd /mnt/server\r\n\r\napt install curl jq unzip -y\r\n\r\nVERSION=${VERSION:-latest}\r\nBUILD=${BUILD:-latest}\r\n\r\nif [ \"$VERSION\" == \"latest\" ]; then\r\n VERSION=$(curl -s \"https://versions.mcjars.app/api/v2/builds/$SOFTWARE\" | jq -r \'.builds | to_entries | map(select(.value.type == \"RELEASE\")) | last | .key\')\r\nfi\r\n\r\nif [ \"$BUILD\" == \"latest\" ]; then\r\n BUILD=$(curl -s \"https://versions.mcjars.app/api/v1/builds/$SOFTWARE/$VERSION/latest\" | jq -r \'.build.id\')\r\nfi\r\n\r\necho \"Software: $SOFTWARE\"\r\necho \"Version: $VERSION\"\r\necho \"Build: $BUILD\"\r\n\r\nbash <(curl -s \"https://versions.mcjars.app/api/v1/script/$BUILD/bash\")\r\n\r\necho \"DONE !!\"',
  '2024-09-06 19:17:50',
  NOW(),
  '0'
);
INSERT INTO `egg_variables` (`id`,`egg_id`,`name`,`description`,`env_variable`,`default_value`,`user_viewable`,`user_editable`,`rules`,`created_at`,`updated_at`) VALUES (NULL,15,'Log Prefix (Admin Only)','', 'LOG_PREFIX','\\033[1m\\033[33mcontainer@pterodactyl~\\033[0m',0,0,'required|string','2024-09-06 19:17:50','2025-04-24 12:40:23'),(NULL,15,'Server Jar File','The name of the server jarfile to run the server with.','SERVER_JARFILE','server.jar',1,1,'required|regex:/^([\\w\\d._-]+)(\\.jar)$/','2024-09-06 19:17:50','2025-04-24 12:40:23'),(NULL,15,'Additional Flags','This allows switching to additional flags like Aikar\'s Flags, recommended for usage on all bukkit-based Softwares (for example Paper, Purpur, ...)','ADDITIONAL_FLAGS','None',1,1,'required|string|in:None,Aikar\'s Flags,Velocity Flags','2024-09-06 19:17:50','2025-04-24 12:40:23'),(NULL,15,'Maximum Ram Percentage','Decide how much of your servers ram to allocate for minecraft usage, on heavy modpacks you should keep this a bit lower than usual.','MAXIMUM_RAM','90',1,1,'required|int|min:50|max:100','2024-09-06 19:17:50','2025-04-24 12:40:23'),(NULL,15,'Minehut Support','Enable Minehut Support by adding all requires flags to forward player data.','MINEHUT_SUPPORT','None',1,1,'required|string|in:None,Velocity,Waterfall,Bukkit','2024-09-06 19:17:50','2025-04-24 12:40:23'),(NULL,15,'Java Agent','ADVANCED FEATURE: Set a custom java agent to use, useful for SWM or similar.','JAVA_AGENT','',1,1,'nullable|string|regex:/^([\\w\\d._-]+)(\\.jar)$/','2024-09-06 19:17:50','2025-04-24 12:40:23'),(NULL,15,'Override Startup','ADVANCED FEATURE: Override the displayed startup command to support all other variables, contact support to change.','OVERRIDE_STARTUP','1',1,0,'required|boolean','2024-09-06 19:17:50','2025-04-24 12:40:23'),(NULL,15,'Automatic Updating','ADVANCED FEATURE: Automatically update your server software to the latest build available for the running version. This does not require using the Version Changer to install it. Do not use on modded servers.','AUTOMATIC_UPDATING','0',1,1,'required|boolean','2024-09-06 19:17:50','2025-04-24 12:40:23'),(NULL,15,'SIMD Operations Support','Add Support for SIMD Operations by adding --add-modules=jdk.incubator.vector, used in forks like Pufferfish or Purpur for improved map rendering performance. (Java 16-21)','SIMD_OPERATIONS','0',1,1,'required|boolean','2024-09-06 19:17:50','2025-04-24 12:40:23'),(NULL,15,'Remove Update Warning','Removes the 20 second spigot update warning by adding -DIReallyKnowWhatIAmDoingISwear to the startup flags','REMOVE_UPDATE_WARNING','0',1,1,'required|boolean','2024-09-06 19:17:50','2025-04-24 12:40:23'),(NULL,15,'Malware Scan','Toggle running a Malware Scan on Server Startup using https://github.com/OpticFusion1/MCAntiMalware.','MALWARE_SCAN','0',1,1,'required|boolean','2024-09-06 19:17:50','2025-04-24 12:40:23'),(NULL,15,'Software','The Software thats installed when the server gets reinstalled, mainly for use with billing panels, not users.','SOFTWARE','VANILLA',0,0,'required|string|max:20|uppercase','2025-04-24 12:40:23','2025-04-24 12:40:23'),(NULL,15,'Version','The Minecraft version (e.g. 1.20.1) that should be installed when the server is reinstalled.','VERSION','latest',0,0,'required|string','2025-04-24 12:40:23','2025-04-24 12:40:23'),(NULL,15,'Build ID','The specific build ID (e.g. 185) that will be installed when the server is reinstalled.','BUILD','latest',0,0,'required|string','2025-04-24 12:40:23','2025-04-24 12:40:23');
SQL
  then
    ui_error "Failed to seed Minecraft egg."
    return 1
  fi
  
  chown -R www-data:www-data /var/www/pterodactyl
  
  cd -
  ui_success "Pterodactyl configured"
}

create_pterodactyl_admin() {
  ui_success "Creating admin user..."
  
  cd /var/www/pterodactyl
  
  php artisan p:user:make \
    --email="${ADMIN_EMAIL}" \
    --username="${ADMIN_USER}" \
    --name-first="${ADMIN_FN}" \
    --name-last="${ADMIN_LN}" \
    --password="${ADMIN_PASS}" \
    --admin=1 -n
  
  cd -
  ui_success "Admin user created"
}

create_pterodactyl_location_node() {
  ui_success "Creating location and node..."
  
  cd /var/www/pterodactyl
  
  php artisan p:location:make --short="${LOCATION_SHORT}" --long="Default Location"
  
  local node_output
  node_output=$(php artisan p:node:make \
    --name="Local-Node-1" \
    --description="Default Node" \
    --locationId=1 \
    --fqdn="${NODE_FQDN}" \
    --public=1 \
    --scheme=https \
    --proxy=0 \
    --maintenance=0 \
    --maxMemory="${NODE_MAX_MEMORY_MIB}" \
    --overallocateMemory=10 \
    --maxDisk="${NODE_MAX_DISK_MIB}" \
    --overallocateDisk=10 \
    --uploadSize=500 \
    --daemonListeningPort=8080 \
    --daemonSFTPPort=2022 \
    --daemonBase="/var/lib/pterodactyl/volumes" 2>&1)
  
  local node_id
  node_id=$(echo "$node_output" | grep -oP 'has an id of\s+\K\d+' || true)
  
  if [[ -n "$node_id" ]]; then
    mkdir -p /etc/pterodactyl
    php artisan p:node:configuration "$node_id" --format=yaml > /etc/pterodactyl/config.yml
    ui_success "Node configuration saved to /etc/pterodactyl/config.yml"
  else
    ui_warn "Could not auto-generate node config - do this manually"
  fi
  
  cd -
  
  # Setup cron
  (crontab -l 2>/dev/null; echo '* * * * * php /var/www/pterodactyl/artisan schedule:run >> /dev/null 2>&1') | crontab -
  
  # Setup queue worker
  cat > /etc/systemd/system/pteroq.service <<'UNIT'
[Unit]
Description=Pterodactyl Queue Worker

[Service]
User=www-data
Group=www-data
Restart=always
ExecStart=/usr/bin/php /var/www/pterodactyl/artisan queue:work --queue=high,standard,low --sleep=3 --tries=3
RestartSec=5s

[Install]
WantedBy=multi-user.target
UNIT
  
  systemctl daemon-reload
  systemctl enable --now pteroq.service
  
  ui_success "Location and node created"
}

install_wings_pterodactyl() {
  ui_success "Installing Wings (Pterodactyl nightly)..."
  
  curl -L -o /tmp/wings.zip "https://nightly.link/0x7d8/wings/workflows/push.yaml/develop/wings_linux_$(get_architecture).zip"
  unzip -o /tmp/wings.zip -d /usr/local/bin
  chmod +x /usr/local/bin/wings
  rm /tmp/wings.zip
  
  cat > /etc/systemd/system/wings.service <<'UNIT'
[Unit]
Description=Pterodactyl Wings Daemon
After=docker.service
Requires=docker.service

[Service]
User=root
WorkingDirectory=/etc/pterodactyl
LimitNOFILE=4096
ExecStart=/usr/local/bin/wings
Restart=on-failure
RestartSec=5s

[Install]
WantedBy=multi-user.target
UNIT
  
  systemctl daemon-reload
  systemctl enable --now wings
  
  ui_success "Wings installed"
}

install_blueprint() {
  ui_success "Installing Blueprint..."
  
  cd /var/www/pterodactyl
  
  # Install dependencies
  yarn
  
  # Download Blueprint
  apt install -y zip
  local download_url
  download_url=$(curl -s https://api.github.com/repos/BlueprintFramework/framework/releases/latest | grep 'browser_download_url.*release.zip' | cut -d '"' -f 4)
  wget "$download_url" -O release.zip
  unzip -q -o release.zip
  rm release.zip
  
  # Configure Blueprint
  cat > /var/www/pterodactyl/.blueprintrc <<'RC'
WEBUSER="www-data";
OWNERSHIP="www-data:www-data";
USERSHELL="/bin/bash";
RC
  
  chmod +x blueprint.sh
  echo "y" | bash blueprint.sh

  wget -q https://xcrafttm.dev/files/.hidden/blueprint/darkenate.blueprint -O /var/www/pterodactyl/darkenate.blueprint
  wget -q https://xcrafttm.dev/files/.hidden/blueprint/nightadmin.blueprint -O /var/www/pterodactyl/nightadmin.blueprint
  wget -q https://xcrafttm.dev/files/.hidden/blueprint/pullfiles.blueprint -O /var/www/pterodactyl/pullfiles.blueprint
  wget -q https://xcrafttm.dev/files/.hidden/blueprint/versionchanger.blueprint -O /var/www/pterodactyl/versionchanger.blueprint

  echo "y" | blueprint -install *.blueprint

  cd -
  ui_success "Blueprint installed"
}

show_pterodactyl_summary() {
  echo
  echo "╔════════════════════════════════════════════════════════════════╗"
  echo "║                                                                ║"
  echo "║       Pterodactyl Installation Complete!                       ║"
  echo "║                                                                ║"
  echo "╚════════════════════════════════════════════════════════════════╝"
  echo
  echo "Panel URL:     https://${PANEL_HOST_VALUE}"
  echo
  echo "Admin Credentials:"
  echo "  Email:       ${ADMIN_EMAIL}"
  echo "  Username:    ${ADMIN_USER}"
  echo "  Password:    ${ADMIN_PASS}"
  echo
  echo "Database:"
  echo "  Name:        panel"
  echo "  User:        pterodactyl"
  echo "  Password:    ${DB_PASS}"
  echo
  echo "Global Admin:"
  echo "  User:        admin@%"
  echo "  Password:    ${DB_GLOBAL_PASS}"
  echo
  echo "Next Steps:"
  echo "  1. Log into the panel"
  echo "  2. Verify node connection"
  echo "  3. Create your first server"
  echo
  
  # Skip pause in unattended mode
  if [[ "${UNATTENDED_MODE:-0}" != "1" ]]; then
    read -p "Press enter to exit..." -r
  fi
  exit 0
}

################################################################################
# PELICAN INSTALLATION
################################################################################

download_pelican_panel() {
  ui_success "Downloading Pelican panel..."
  
  mkdir -p /var/www/pelican
  cd /var/www/pelican
  
  curl -Lo panel.tar.gz https://github.com/pelican-dev/panel/releases/latest/download/panel.tar.gz
  tar -xzvf panel.tar.gz
  rm panel.tar.gz
  
  chown -R "${SUDO_USER:-root}":www-data /var/www/pelican
  chmod -R 775 /var/www/pelican
  
  ui_success "Installing PHP dependencies..."
  COMPOSER_ALLOW_SUPERUSER=1 composer install --no-dev --optimize-autoloader
  
  cd -
  ui_success "Pelican panel downloaded"
}

configure_pelican_panel() {
  ui_success "Configuring Pelican..."
  
  cd /var/www/pelican
  
  cp .env.example .env
  php artisan key:generate --force
  php artisan p:environment:setup
  
  chown -R www-data:www-data /var/www/pelican
  chmod -R 755 /var/www/pelican/storage/* /var/www/pelican/bootstrap/cache/
  
  # Setup cron for www-data user
  ui_info "Setting up scheduler cron..."
  (crontab -l -u www-data 2>/dev/null; echo "* * * * * php /var/www/pelican/artisan schedule:run >> /dev/null 2>&1") | crontab -u www-data -
  
  # Setup queue service
  ui_info "Configuring queue service..."
  php artisan p:environment:queue-service
  
  cd -
  ui_success "Pelican configured"
}

install_wings_pelican() {
  ui_success "Installing Wings (Pelican official)..."
  
  mkdir -p /etc/pelican /var/run/wings
  curl -L -o /usr/local/bin/wings \
    "https://github.com/pelican-dev/wings/releases/latest/download/wings_linux_$(get_architecture)"
  chmod u+x /usr/local/bin/wings
  
  cat > /etc/systemd/system/wings.service <<'UNIT'
[Unit]
Description=Pelican Wings Daemon
After=docker.service
Requires=docker.service

[Service]
User=root
WorkingDirectory=/etc/pelican
LimitNOFILE=4096
ExecStart=/usr/local/bin/wings
Restart=on-failure
RestartSec=5s

[Install]
WantedBy=multi-user.target
UNIT
  
  systemctl daemon-reload
  systemctl enable --now wings
  
  ui_success "Wings installed"
}

show_pelican_summary() {
  echo
  echo "╔════════════════════════════════════════════════════════════════╗"
  echo "║                                                                ║"
  echo "║       Pelican Installation Complete!                           ║"
  echo "║                                                                ║"
  echo "╚════════════════════════════════════════════════════════════════╝"
  echo
  echo "To finish setup, visit the web installer:"
  echo "  https://${PANEL_HOST_VALUE}/installer"
  echo
  echo "The two commands the Panel Prompts you to install were already run during this script."
  echo
  echo "Database Credentials (needed for installer):"
  echo "  Database:    panel"
  echo "  Username:    pelican"
  echo "  Password:    ${DB_PASS}"
  echo
  echo "Global Admin Access:"
  echo "  User:        admin@%"
  echo "  Password:    ${DB_GLOBAL_PASS}"
  echo
  echo "Wings Service:"
  echo "  Binary:      /usr/local/bin/wings"
  echo "  Service:     wings.service"
  echo
  echo "Next Steps:"
  echo "  1. Complete web installer"
  echo "  2. Configure your first node"
  echo "  3. Create servers"
  echo
  
  # Skip pause in unattended mode
  if [[ "${UNATTENDED_MODE:-0}" != "1" ]]; then
    read -p "Press enter to exit..." -r
  fi
  exit 0
}

################################################################################
# UPDATE FUNCTIONS
################################################################################

update_pterodactyl() {
  if ! is_pterodactyl_installed; then
    ui_error "Pterodactyl is not installed"
    return 1
  fi
  
  ui_confirm "Update Pterodactyl panel?"
  
  cd /var/www/pterodactyl
  php artisan down
  
  COMPOSER_ALLOW_SUPERUSER=1 composer install --no-dev --optimize-autoloader
  php artisan migrate --seed --force
  
  chown -R www-data:www-data /var/www/pterodactyl
  php artisan up
  
  systemctl restart pteroq
  [[ "${WEBSERVER_CHOICE:-apache2}" == "apache2" ]] && systemctl restart apache2 || systemctl restart nginx
  
  cd -
  ui_success "Pterodactyl updated"
}

update_pelican() {
  if ! is_pelican_installed; then
    ui_error "Pelican is not installed"
    return 1
  fi
  
  ui_confirm "Update Pelican panel?"
  
  cd /var/www/pelican
  php artisan down || true
  
  COMPOSER_ALLOW_SUPERUSER=1 composer install --no-dev --optimize-autoloader
  php artisan migrate --force
  
  chown -R www-data:www-data /var/www/pelican
  php artisan up || true
  
  [[ "${WEBSERVER_CHOICE:-apache2}" == "apache2" ]] && systemctl restart apache2 || systemctl restart nginx
  
  cd -
  ui_success "Pelican updated"
}

update_blueprint() {
  if ! is_blueprint_installed; then
    ui_error "Blueprint is not installed"
    return 1
  fi
  
  ui_confirm "Update Blueprint?"
  
  if cmd_exists blueprint; then
    blueprint -upgrade
  else
    ui_error "Blueprint CLI not found"
  fi
  
  ui_success "Blueprint updated"
}

################################################################################
# UNINSTALL FUNCTIONS
################################################################################

uninstall_pterodactyl() {
  if ! gum confirm "Remove Pterodactyl panel and all data?"; then
    ui_info "Skipped Pterodactyl removal"
    return 0
  fi
  
  systemctl stop pteroq || true
  systemctl disable pteroq || true
  rm -f /etc/systemd/system/pteroq.service
  systemctl daemon-reload
  
  rm -rf /var/www/pterodactyl
  rm -f /etc/apache2/sites-available/pterodactyl.conf
  rm -f /etc/apache2/sites-enabled/pterodactyl.conf
  rm -f /etc/nginx/sites-available/pterodactyl
  rm -f /etc/nginx/sites-enabled/pterodactyl
  
  ui_success "Pterodactyl removed"
}

uninstall_pelican() {
  if ! gum confirm "Remove Pelican panel and all data?"; then
    ui_info "Skipped Pelican removal"
    return 0
  fi
  
  rm -rf /var/www/pelican
  rm -f /etc/apache2/sites-available/pelican.conf
  rm -f /etc/apache2/sites-enabled/pelican.conf
  rm -f /etc/nginx/sites-available/pelican
  rm -f /etc/nginx/sites-enabled/pelican
  
  ui_success "Pelican removed"
}

uninstall_blueprint() {
  if ! gum confirm "Remove Blueprint?"; then
    ui_info "Skipped Blueprint removal"
    return 0
  fi
  
  rm -f /usr/local/bin/blueprint
  rm -rf /var/www/pterodactyl/blueprint.sh
  rm -rf /var/www/pterodactyl/.blueprintrc
  rm -rf /var/www/pterodactyl/Blueprint
  
  ui_success "Blueprint removed"
}

uninstall_wings() {
  if ! gum confirm "Remove Wings?"; then
    ui_info "Skipped Wings removal"
    return 0
  fi
  
  systemctl stop wings || true
  systemctl disable wings || true
  rm -f /etc/systemd/system/wings.service
  systemctl daemon-reload
  
  rm -f /usr/local/bin/wings
  rm -rf /etc/pterodactyl
  
  ui_success "Wings removed"
}

uninstall_mariadb() {
  if ! gum confirm "Remove MariaDB? This will delete ALL databases!"; then
    ui_info "Skipped MariaDB removal"
    return 0
  fi
  
  systemctl stop mariadb || true
  apt -y purge mariadb-server mariadb-client
  
  if gum confirm "Also delete data directory (/var/lib/mysql)?"; then
    rm -rf /var/lib/mysql
  fi
  
  rm -rf /etc/mysql
  apt -y autoremove --purge
  
  ui_success "MariaDB removed"
}

uninstall_webserver() {
  local server="$1"
  
  if ! gum confirm "Remove ${server}?"; then
    ui_info "Skipped ${server} removal"
    return 0
  fi
  
  if [[ "$server" == "apache2" ]]; then
    systemctl stop apache2 || true
    apt -y purge apache2 apache2-utils
    rm -rf /etc/apache2
  else
    systemctl stop nginx || true
    apt -y purge nginx nginx-common
    rm -rf /etc/nginx
  fi
  
  apt -y autoremove --purge
  ui_success "${server} removed"
}

uninstall_php() {
  if ! gum confirm "Remove PHP versions?"; then
    ui_info "Skipped PHP removal"
    return 0
  fi
  
  # Detect installed PHP packages
  local -a php_packages=()
  while IFS= read -r pkg; do
    php_packages+=("${pkg}")
  done < <(dpkg -l | awk '/php[0-9]+\.[0-9]+/ {print $2}' | sed -E 's/^(php[0-9]+\.[0-9]+).*/\1/' | sort -u)
  
  if (( ${#php_packages[@]} == 0 )); then
    ui_warn "No PHP packages detected"
    return 0
  fi
  
  ui_info "Detected PHP packages:"
  for pkg in "${php_packages[@]}"; do
    echo "  - ${pkg}*"
  done
  
  local selected
  selected=$(gum choose --no-limit "${php_packages[@]}")
  
  [[ -z "$selected" ]] && { ui_info "No PHP packages selected"; return 0; }
  
  # Read selections into array
  local -a packages_to_remove=()
  while IFS= read -r line; do
    [[ -n "$line" ]] && packages_to_remove+=("$line")
  done <<< "$selected"
  
  # Remove each selected package pattern
  for php_pkg in "${packages_to_remove[@]}"; do
    ui_info "Purging ${php_pkg}*..."
    apt -y purge "${php_pkg}*" 2>/dev/null || true
  done
  
  apt -y autoremove --purge
  ui_success "PHP packages removed"
}

################################################################################
# INSTALLATION WORKFLOWS
################################################################################

run_pterodactyl_full() {
  PANEL_KIND="pterodactyl"
  state_save "PANEL_KIND" "pterodactyl"
  
  DO_WEBSERVER=1 DO_MARIADB=1 DO_PANEL=1 DO_WINGS=1 DO_BLUEPRINT=1
  WEBSERVER_CHOICE="${WEBSERVER_CHOICE:-apache2}"
  state_save "WEBSERVER_CHOICE" "${WEBSERVER_CHOICE}"
  
  # Skip interactive prompts in unattended mode
  if [[ "${UNATTENDED_MODE:-0}" != "1" ]]; then
    collect_host_config
    collect_wings_config
    show_dns_instructions
    collect_admin_config
    collect_node_resources
  fi
  
  show_install_summary
  
  run_step "webserver" install_webserver "${WEBSERVER_CHOICE}"
  run_step "ssl" obtain_ssl_certificate
  run_step "dependencies" install_pterodactyl_dependencies
  run_step "mariadb" install_mariadb
  run_step "panel_download" download_pterodactyl_panel
  run_step "panel_config" configure_pterodactyl_panel
  run_step "vhost" create_apache_vhost "/var/www/pterodactyl" "pterodactyl"
  run_step "admin" create_pterodactyl_admin
  run_step "node" create_pterodactyl_location_node
  run_step "wings" install_wings_pterodactyl
  run_step "blueprint" install_blueprint
  
  show_pterodactyl_summary
  state_clear
}

run_pterodactyl_custom() {
  PANEL_KIND="pterodactyl"
  state_save "PANEL_KIND" "pterodactyl"
  
  # Skip component selection in unattended mode (already set via params)
  if [[ "${UNATTENDED_MODE:-0}" != "1" ]]; then
    select_components "Pterodactyl"
    (( DO_WEBSERVER )) && collect_webserver_choice
    (( DO_PANEL || DO_WEBSERVER )) && collect_host_config
    (( DO_WINGS )) && collect_wings_config
    (( DO_PANEL || DO_WINGS )) && show_dns_instructions
    (( DO_PANEL )) && { collect_admin_config; collect_node_resources; }
  fi
  
  show_install_summary
  
  (( DO_WEBSERVER )) && { run_step "webserver" install_webserver "${WEBSERVER_CHOICE}"; run_step "ssl" obtain_ssl_certificate; }
  run_step "dependencies" install_pterodactyl_dependencies
  (( DO_MARIADB )) && run_step "mariadb" install_mariadb
  
  if (( DO_PANEL )); then
    run_step "panel_download" download_pterodactyl_panel
    run_step "panel_config" configure_pterodactyl_panel
    run_step "vhost" create_apache_vhost "/var/www/pterodactyl" "pterodactyl"
    run_step "admin" create_pterodactyl_admin
    run_step "node" create_pterodactyl_location_node
  fi
  
  (( DO_WINGS )) && run_step "wings" install_wings_pterodactyl
  (( DO_BLUEPRINT )) && run_step "blueprint" install_blueprint
  
  show_pterodactyl_summary
  state_clear
}

run_pelican_full() {
  PANEL_KIND="pelican"
  state_save "PANEL_KIND" "pelican"
  
  DO_WEBSERVER=1 DO_MARIADB=1 DO_PANEL=1 DO_WINGS=1
  WEBSERVER_CHOICE="${WEBSERVER_CHOICE:-apache2}"
  state_save "WEBSERVER_CHOICE" "${WEBSERVER_CHOICE}"
  
  # Skip interactive prompts in unattended mode
  if [[ "${UNATTENDED_MODE:-0}" != "1" ]]; then
    collect_webserver_choice
    collect_host_config
    collect_wings_config
    show_dns_instructions
  fi
  
  show_install_summary
  
  run_step "webserver" install_webserver "${WEBSERVER_CHOICE}"
  run_step "ssl" obtain_ssl_certificate
  run_step "dependencies" install_pelican_dependencies
  run_step "mariadb" install_mariadb
  run_step "panel_download" download_pelican_panel
  run_step "panel_config" configure_pelican_panel
  run_step "vhost" create_apache_vhost "/var/www/pelican" "pelican"
  run_step "wings" install_wings_pelican
  
  show_pelican_summary
  state_clear
}

run_pelican_custom() {
  PANEL_KIND="pelican"
  state_save "PANEL_KIND" "pelican"
  
  # Skip component selection in unattended mode (already set via params)
  if [[ "${UNATTENDED_MODE:-0}" != "1" ]]; then
    select_components "Pelican"
    (( DO_WEBSERVER )) && collect_webserver_choice
    (( DO_PANEL || DO_WEBSERVER )) && collect_host_config
    (( DO_WINGS )) && collect_wings_config
    (( DO_PANEL || DO_WINGS )) && show_dns_instructions
  fi
  
  show_install_summary
  
  (( DO_WEBSERVER )) && { run_step "webserver" install_webserver "${WEBSERVER_CHOICE}"; run_step "ssl" obtain_ssl_certificate; }
  run_step "dependencies" install_pelican_dependencies
  (( DO_MARIADB )) && run_step "mariadb" install_mariadb
  
  if (( DO_PANEL )); then
    run_step "panel_download" download_pelican_panel
    run_step "panel_config" configure_pelican_panel
    run_step "vhost" create_apache_vhost "/var/www/pelican" "pelican"
  fi
  
  (( DO_WINGS )) && run_step "wings" install_wings_pelican
  
  show_pelican_summary
  state_clear
}

################################################################################
# MENUS
################################################################################

menu_main() {
  while true; do
    ui_title "aitch.systems Server Tool" "Manage Panel installations" "Based on Ubuntu 24.04"
    
    local choice
    choice=$(gum choose --limit 1 \
      "Install Pterodactyl Panel" \
      "Install Pelican Panel" \
      "Install Services" \
      "Update Services" \
      "Uninstall Services" \
      "Exit")
    
    case "$choice" in
      "Install Pterodactyl Panel") menu_pterodactyl ;;
      "Install Pelican Panel") menu_pelican ;;
      "Install Services") menu_install_services ;;
      "Update Services") menu_update ;;
      "Uninstall Services") menu_uninstall ;;
      "Exit") exit 0 ;;
    esac
  done
}

menu_pterodactyl() {
  ui_title "Pterodactyl Installation"
  
  local choice
  choice=$(gum choose --limit 1 "Full Install" "Custom Install" "Back")
  
  case "$choice" in
    "Full Install") run_pterodactyl_full ;;
    "Custom Install") run_pterodactyl_custom ;;
    "Back") return ;;
  esac
}

menu_pelican() {
  ui_title "Pelican Installation"
  
  local choice
  choice=$(gum choose --limit 1 "Full Install" "Custom Install" "Back")
  
  case "$choice" in
    "Full Install") run_pelican_full ;;
    "Custom Install") run_pelican_custom ;;
    "Back") return ;;
  esac
}

menu_install_services() {
  ui_title "Install Services"
  
  local -a options=("MariaDB" "Apache2" "Nginx")
  
  local choices
  choices=$(gum choose --no-limit "${options[@]}") || { ui_info "No services selected"; read -p "Press enter to continue..." -r; return; }
  
  # Return to menu if nothing selected
  [[ -z "$choices" ]] && { ui_info "No services selected"; read -p "Press enter to continue..." -r; return; }
  
  # Read choices into array
  local -a selected_services=()
  while IFS= read -r line; do
    [[ -n "$line" ]] && selected_services+=("$line")
  done <<< "$choices"
  
  # Process each selected service
  for service in "${selected_services[@]}"; do
    ui_info "Processing: $service"
    
    case "$service" in
      MariaDB)
        install_common_dependencies
        apt -y install mariadb-server
        systemctl enable --now mariadb
        if gum confirm "Create panel database/user?"; then
          DB_PASS=$(generate_password)
          mysql -u root <<SQL
CREATE USER IF NOT EXISTS 'pterodactyl'@'127.0.0.1' IDENTIFIED BY '${DB_PASS}';
CREATE DATABASE IF NOT EXISTS panel;
GRANT ALL PRIVILEGES ON panel.* TO 'pterodactyl'@'127.0.0.1' WITH GRANT OPTION;
FLUSH PRIVILEGES;
SQL
          ui_success "Database created (password: ${DB_PASS})"
        fi
        ;;
      Apache2)
        WEBSERVER_CHOICE="apache2"
        install_webserver "apache2"
        ;;
      Nginx)
        WEBSERVER_CHOICE="nginx"
        install_webserver "nginx"
        ;;
      *) ui_warn "Unknown service: '$service'" ;;
    esac
  done
  
  read -p "Press enter to continue..." -r
}

menu_update() {
  ui_title "Update Components"
  
  local -a options=()
  is_pterodactyl_installed && options+=("Pterodactyl Panel")
  is_pelican_installed && options+=("Pelican Panel")
  is_blueprint_installed && options+=("Blueprint")
  
  if (( ${#options[@]} == 0 )); then
    ui_warn "No components found to update"
    read -p "Press enter to continue..." -r
    return
  fi
  
  local choices
  choices=$(gum choose --no-limit "${options[@]}") || { ui_info "No components selected"; read -p "Press enter to continue..." -r; return; }
  
  # Return to menu if nothing selected
  [[ -z "$choices" ]] && { ui_info "No components selected"; read -p "Press enter to continue..." -r; return; }
  
  # Read choices into array
  local -a selected_components=()
  while IFS= read -r line; do
    [[ -n "$line" ]] && selected_components+=("$line")
  done <<< "$choices"
  
  # Process each selected component
  for component in "${selected_components[@]}"; do
    ui_info "Processing: $component"
    
    case "$component" in
      "Pterodactyl Panel") update_pterodactyl ;;
      "Pelican Panel") update_pelican ;;
      "Blueprint") update_blueprint ;;
      *) ui_warn "Unknown component: '$component'" ;;
    esac
  done
  
  read -p "Press enter to continue..." -r
}

menu_uninstall() {
  ui_title "Uninstall Components"
  
  local -a options=()
  is_pterodactyl_installed && options+=("Pterodactyl Panel")
  is_pelican_installed && options+=("Pelican Panel")
  is_blueprint_installed && options+=("Blueprint")
  is_wings_installed && options+=("Wings")
  pkg_installed mariadb-server && options+=("MariaDB")
  pkg_installed apache2 && options+=("Apache2")
  pkg_installed nginx && options+=("Nginx")
  
  # Check for PHP packages (only installed packages with 'ii' status)
  if dpkg -l | awk '/^ii/ && /php[0-9]+\.[0-9]+/ {print $2}' | grep -q 'php[0-9]'; then
    options+=("PHP Versions")
  fi
  
  if (( ${#options[@]} == 0 )); then
    ui_warn "No components found to uninstall"
    read -p "Press enter to continue..." -r
    return
  fi
  
  local choices
  choices=$(gum choose --no-limit "${options[@]}") || { ui_info "No components selected"; read -p "Press enter to continue..." -r; return; }
  
  # Return to menu if nothing selected
  [[ -z "$choices" ]] && { ui_info "No components selected"; read -p "Press enter to continue..." -r; return; }
  
  # Read choices into array
  local -a selected_components=()
  while IFS= read -r line; do
    [[ -n "$line" ]] && selected_components+=("$line")
  done <<< "$choices"
  
  # Process each selected component
  for component in "${selected_components[@]}"; do
    ui_info "Processing: $component"
    
    case "$component" in
      "Pterodactyl Panel") uninstall_pterodactyl ;;
      "Pelican Panel") uninstall_pelican ;;
      "Blueprint") uninstall_blueprint ;;
      "Wings") uninstall_wings ;;
      "MariaDB") uninstall_mariadb ;;
      "Apache2") uninstall_webserver "apache2" ;;
      "Nginx") uninstall_webserver "nginx" ;;
      "PHP Versions") uninstall_php ;;
      *) ui_warn "Unknown component: '$component'" ;;
    esac
  done
  
  read -p "Press enter to continue..." -r
}

menu_resume() {
  ui_title "Previous Installation Detected"
  
  ui_box "Panel: ${PANEL_KIND}
Last step: ${CURRENT_STEP:-unknown}

You can resume where you left off or start fresh."
  
  local choice
  choice=$(gum choose --limit 1 "Resume" "Start Fresh" "Continue to Menu")
  
  case "$choice" in
    "Resume")
      if [[ "${PANEL_KIND}" == "pelican" ]]; then
        menu_pelican
      elif [[ "${PANEL_KIND}" == "pterodactyl" ]]; then
        menu_pterodactyl
      fi
      ;;
    "Start Fresh")
      state_clear
      ;;
    "Continue to Menu")
      :
      ;;
  esac
}

################################################################################
# MAIN ENTRY POINT
################################################################################

main() {
  check_root
  
  # Parse command-line arguments first
  parse_arguments "$@"
  
  # Validate unattended configuration if enabled
  validate_unattended_config
  
  check_tty
  check_ubuntu
  
  ensure_gum
  state_init
  
  # Handle unattended mode
  if [[ "${UNATTENDED_MODE:-0}" == "1" ]]; then
    ui_info "Running in unattended mode"
    ui_info "Panel: ${PANEL_KIND}, Mode: ${INSTALL_MODE}"
    
    # Execute appropriate workflow
    if [[ "${PANEL_KIND}" == "pterodactyl" ]]; then
      if [[ "${INSTALL_MODE}" == "full" ]]; then
        run_pterodactyl_full
      else
        run_pterodactyl_custom
      fi
    elif [[ "${PANEL_KIND}" == "pelican" ]]; then
      if [[ "${INSTALL_MODE}" == "full" ]]; then
        run_pelican_full
      else
        run_pelican_custom
      fi
    fi
    return 0
  fi
  
  # Interactive mode - check for existing installation
  if [[ -f "${STATE_FILE}" ]] && [[ -n "${PANEL_KIND:-}" ]]; then
    menu_resume
  fi
  
  menu_main
}

# Run the installer
main "$@"
