#!/usr/bin/env bash
# Copyright (c) 2021-2026 community-scripts ORG
# Author: tteck (tteckster)
# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE

# Source shared libraries
source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/api.func)
source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/core.func)
source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/error_handler.func)
load_functions
catch_errors

APP="TurnKey LXC"
NSAPP="turnkey"
DIAGNOSTICS="no"
METHOD="default"
RANDOM_UUID="$(cat /proc/sys/kernel/random/uuid)"
EXECUTION_ID="${RANDOM_UUID}"

header_info() {
  clear
  cat <<"EOF"
 ______              __ __           __   _  _______
/_  __/_ _________  / //_/__ __ __  / /  | |/_/ ___/
 / / / // / __/ _ \/ ,< / -_) // / / /___>  </ /__
/_/  \_,_/_/ /_//_/_/|_|\__/\_, / /____/_/|_|\___/
                           /___/
EOF
}

# Validate if a container ID is available (cluster-aware)
validate_container_id() {
  local ctid="$1"
  [[ "$ctid" =~ ^[0-9]+$ ]] || return 1

  # Cluster-wide check via pvesh
  if command -v pvesh &>/dev/null; then
    local cluster_ids
    cluster_ids=$(pvesh get /cluster/resources --type vm --output-format json 2>/dev/null |
      grep -oP '"vmid":\s*\K[0-9]+' 2>/dev/null || true)
    if [[ -n "$cluster_ids" ]] && echo "$cluster_ids" | grep -qw "$ctid"; then
      return 1
    fi
  fi

  # Local fallback
  if [[ -f "/etc/pve/qemu-server/${ctid}.conf" ]] || [[ -f "/etc/pve/lxc/${ctid}.conf" ]]; then
    return 1
  fi

  # Check all cluster nodes
  if [[ -d "/etc/pve/nodes" ]]; then
    for node_dir in /etc/pve/nodes/*/; do
      if [[ -f "${node_dir}qemu-server/${ctid}.conf" ]] || [[ -f "${node_dir}lxc/${ctid}.conf" ]]; then
        return 1
      fi
    done
  fi

  # Check LVM volumes
  if lvs --noheadings -o lv_name 2>/dev/null | grep -qE "(^|[-_])${ctid}($|[-_])"; then
    return 1
  fi
  return 0
}

get_valid_container_id() {
  local suggested_id="${1:-$(pvesh get /cluster/nextid 2>/dev/null || echo 100)}"
  while ! validate_container_id "$suggested_id"; do
    suggested_id=$((suggested_id + 1))
  done
  echo "$suggested_id"
}

cleanup_ctid() {
  if pct status "$CTID" &>/dev/null; then
    if [[ "$(pct status "$CTID" | awk '{print $2}')" == "running" ]]; then
      pct stop "$CTID"
    fi
    pct destroy "$CTID"
  fi
}

select_storage() {
  local class="$1" content content_label
  case "$class" in
  container)
    content='rootdir'
    content_label='Container'
    ;;
  template)
    content='vztmpl'
    content_label='Container template'
    ;;
  *)
    msg_error "Invalid storage class '$class'"
    return 1
    ;;
  esac

  local -a MENU=()
  local MSG_MAX_LENGTH=0

  while read -r line; do
    local TAG TYPE FREE ITEM OFFSET=2
    TAG=$(echo "$line" | awk '{print $1}')
    TYPE=$(echo "$line" | awk '{printf "%-10s", $2}')
    FREE=$(echo "$line" | numfmt --field 4-6 --from-unit=K --to=iec --format %.2f | awk '{printf( "%9sB", $6)}')
    ITEM="  Type: $TYPE Free: $FREE "
    ((${#ITEM} + OFFSET > MSG_MAX_LENGTH)) && MSG_MAX_LENGTH=$((${#ITEM} + OFFSET))
    MENU+=("$TAG" "$ITEM" "OFF")
  done < <(pvesm status -content "$content" | awk 'NR>1')

  if [[ $((${#MENU[@]} / 3)) -eq 0 ]]; then
    msg_error "'$content_label' needs to be selected for at least one storage location."
    return 1
  elif [[ $((${#MENU[@]} / 3)) -eq 1 ]]; then
    printf '%s' "${MENU[0]}"
  else
    local STORAGE
    while [[ -z "${STORAGE:+x}" ]]; do
      STORAGE=$(whiptail --backtitle "Proxmox VE Helper Scripts" --title "Storage Pools" --radiolist \
        "Which storage pool for the ${content_label,,}?\n\n" \
        16 $((MSG_MAX_LENGTH + 23)) 6 \
        "${MENU[@]}" 3>&1 1>&2 2>&3) || exit_script
    done
    printf '%s' "$STORAGE"
  fi
}

# ==============================================================================
# MAIN
# ==============================================================================

# Cleanup on error: destroy container, report telemetry, and restart monitor
turnkey_cleanup() {
  local exit_code=$?
  if [[ $exit_code -ne 0 ]]; then
    # Report failure to telemetry
    if [[ "${POST_TO_API_DONE:-}" == "true" && "${POST_UPDATE_DONE:-}" != "true" ]]; then
      post_update_to_api "failed" "$exit_code" 2>/dev/null || true
    fi
    # Destroy failed container
    if [[ -n "${CTID:-}" ]]; then
      cleanup_ctid 2>/dev/null || true
    fi
  fi
  if [[ -f /etc/systemd/system/ping-instances.service ]]; then
    systemctl start ping-instances.service 2>/dev/null || true
  fi
}
trap turnkey_cleanup EXIT

# Stop Proxmox VE Monitor-All if running
if systemctl is-active -q ping-instances.service; then
  systemctl stop ping-instances.service
fi

pve_check
shell_check
root_check

# Read diagnostics preference (same logic as build.func diagnostics_check)
DIAG_CONFIG="/usr/local/community-scripts/diagnostics"
if [[ -f "$DIAG_CONFIG" ]]; then
  DIAGNOSTICS=$(awk -F '=' '/^DIAGNOSTICS/ {print $2}' "$DIAG_CONFIG") || true
  DIAGNOSTICS="${DIAGNOSTICS:-no}"
fi

header_info
whiptail --backtitle "Proxmox VE Helper Scripts" --title "TurnKey LXCs" --yesno \
  "This will allow for the creation of one of the many TurnKey LXC Containers. Proceed?" 10 68 || exit_script

# Update template catalog early so the menu reflects the latest available templates
msg_info "Updating LXC template list"
pveam update >/dev/null
msg_ok "Updated LXC template list"

# Build TurnKey selection menu dynamically from available templates
# Requires gawk for regex capture groups in match()
command -v gawk &>/dev/null || apt-get install -y gawk &>/dev/null
declare -A TURNKEY_TEMPLATES
TURNKEY_MENU=()
MSG_MAX_LENGTH=0
while IFS=$'\t' read -r TEMPLATE_FILE TAG ITEM; do
  TURNKEY_TEMPLATES["$TAG"]="$TEMPLATE_FILE"
  OFFSET=2
  ((${#ITEM} + OFFSET > MSG_MAX_LENGTH)) && MSG_MAX_LENGTH=$((${#ITEM} + OFFSET))
  TURNKEY_MENU+=("$TAG" "$ITEM " "OFF")
done < <(pveam available -section turnkeylinux | gawk '{
  tpl = $2
  if (match(tpl, /debian-([0-9]+)-turnkey-([^_]+)_([^_]+)_/, m)) {
    app = m[2]; deb = m[1]; ver = m[3]
    display = app
    gsub(/-/, " ", display)
    n = split(display, words, " ")
    display = ""
    for (i = 1; i <= n; i++) {
      words[i] = toupper(substr(words[i], 1, 1)) substr(words[i], 2)
      display = display (i > 1 ? " " : "") words[i]
    }
    tag = app "-" deb
    printf "%s\t%s\t%s | Debian %s | %s\n", tpl, tag, display, deb, ver
  }
}' | sort -t$'\t' -k2,2)

if [[ ${#TURNKEY_MENU[@]} -eq 0 ]]; then
  msg_error "No TurnKey templates found. Check your internet connection or template repository."
  exit 1
fi

selected=$(whiptail --backtitle "Proxmox VE Helper Scripts" --title "TurnKey LXCs" --radiolist \
  "\nSelect a TurnKey LXC to create:\n" 20 $((MSG_MAX_LENGTH + 58)) 12 \
  "${TURNKEY_MENU[@]}" 3>&1 1>&2 2>&3 | tr -d '"') || exit_script

if [[ -z "$selected" ]]; then
  whiptail --backtitle "Proxmox VE Helper Scripts" --title "No TurnKey LXC Selected" \
    --msgbox "It appears that no TurnKey LXC container was selected" 10 68
  exit_script
fi

# Extract template filename and app name from selection
TEMPLATE="${TURNKEY_TEMPLATES[$selected]}"
turnkey="${selected%-*}"

# Generate random password
PASS="$(openssl rand -base64 8)"

# Prompt for Container ID
NEXT_ID=$(pvesh get /cluster/nextid 2>/dev/null || echo 100)
while true; do
  CTID=$(whiptail --backtitle "Proxmox VE Helper Scripts" --title "Container ID" \
    --inputbox "Enter the container ID..." 8 40 "$NEXT_ID" 3>&1 1>&2 2>&3) || exit_script

  if [[ -z "$CTID" ]]; then
    msg_error "No Container ID selected"
    exit_script
  fi

  if ! validate_container_id "$CTID"; then
    SUGGESTED_ID=$(get_valid_container_id "$CTID")
    if whiptail --backtitle "Proxmox VE Helper Scripts" --title "ID Already In Use" --yesno \
      "Container/VM ID $CTID is already in use.\n\nWould you like to use the next available ID ($SUGGESTED_ID)?" 10 58; then
      CTID="$SUGGESTED_ID"
      break
    fi
  else
    break
  fi
done

# Prompt for Hostname
HOST_NAME=$(whiptail --backtitle "Proxmox VE Helper Scripts" --title "Hostname" \
  --inputbox "Enter the container hostname..." 8 40 "turnkey-${turnkey}" 3>&1 1>&2 2>&3) || exit_script

# Container options
PCT_OPTIONS=(
  -features keyctl=1,nesting=1
  -hostname "$HOST_NAME"
  -tags community-script
  -onboot 1
  -cores 2
  -memory 2048
  -password "$PASS"
  -net0 name=eth0,bridge=vmbr0,ip=dhcp
  -unprivileged 1
  -arch "$(dpkg --print-architecture)"
)

# Storage selection
TEMPLATE_STORAGE=$(select_storage template) || {
  msg_error "Failed to select template storage"
  exit 1
}
msg_ok "Using '${BL}${TEMPLATE_STORAGE}${CL}' for template storage"

CONTAINER_STORAGE=$(select_storage container) || {
  msg_error "Failed to select container storage"
  exit 1
}
msg_ok "Using '${BL}${CONTAINER_STORAGE}${CL}' for container storage"

# Download template if not already cached
if ! pveam list "$TEMPLATE_STORAGE" | grep -q "$TEMPLATE"; then
  msg_info "Downloading LXC template"
  pveam download "$TEMPLATE_STORAGE" "$TEMPLATE" >/dev/null || {
    msg_error "Failed to download LXC template '${TEMPLATE}'"
    exit 1
  }
  msg_ok "Downloaded LXC template"
fi

# Add rootfs if not specified
[[ " ${PCT_OPTIONS[*]} " =~ " -rootfs " ]] || PCT_OPTIONS+=(-rootfs "${CONTAINER_STORAGE}:${PCT_DISK_SIZE:-8}")

# Set telemetry variables for the selected turnkey
TELEMETRY_TYPE="turnkey"
NSAPP="turnkey-${turnkey}"
CT_TYPE=1
DISK_SIZE="${PCT_DISK_SIZE:-8}"
CORE_COUNT=2
RAM_SIZE=2048
var_os="turnkey"
var_version="${turnkey}"

# Report installation start to telemetry
post_to_api

# Create LXC container
msg_info "Creating LXC container"
pct create "$CTID" "${TEMPLATE_STORAGE}:vztmpl/${TEMPLATE}" "${PCT_OPTIONS[@]}" >/dev/null || {
  msg_error "Failed to create container"
  exit 1
}
msg_ok "Created LXC container (ID: ${BL}${CTID}${CL})"

# Save credentials securely
CREDS_FILE=~/turnkey-${turnkey}.creds
echo "TurnKey ${turnkey} password: ${PASS}" >>"$CREDS_FILE"
chmod 600 "$CREDS_FILE"

# Configure TUN device access for VPN-based turnkeys
TUN_DEVICE_REQUIRED=("openvpn")
if printf '%s\n' "${TUN_DEVICE_REQUIRED[@]}" | grep -qw "${turnkey}"; then
  msg_info "Configuring TUN device access for ${turnkey}"
  {
    echo "lxc.cgroup2.devices.allow: c 10:200 rwm"
    echo "lxc.mount.entry: /dev/net/tun dev/net/tun none bind,create=file 0 0"
  } >>"/etc/pve/lxc/${CTID}.conf"
  msg_ok "TUN device access configured"
  sleep 5
fi

# Start container
msg_info "Starting LXC container"
pct start "$CTID"
msg_ok "Started LXC container"
sleep 10

# Detect container IP
msg_info "Detecting IP address"
IP=""
for attempt in $(seq 1 5); do
  IP=$(pct exec "$CTID" -- ip -4 a show dev eth0 2>/dev/null | grep -oP 'inet \K[^/]+' || true)
  if [[ -n "$IP" ]]; then
    break
  fi
  [[ $attempt -lt 5 ]] && sleep 5
done

if [[ -z "$IP" ]]; then
  msg_warn "IP address not found after 5 attempts"
  IP="NOT FOUND"
else
  msg_ok "IP address: ${BL}${IP}${CL}"
fi

# Report success to telemetry
post_update_to_api "done" "none"

# Success summary
header_info
echo
msg_ok "TurnKey ${BL}${turnkey}${CL} LXC container '${BL}${CTID}${CL}' was successfully created."
echo
echo -e "  ${TAB}${YW}IP Address:${CL}  ${BL}${IP}${CL}"
echo -e "  ${TAB}${YW}Login:${CL}       ${GN}root${CL}"
echo -e "  ${TAB}${YW}Password:${CL}    ${GN}${PASS}${CL}"
echo
echo -e "  ${TAB}Proceed to the LXC console to complete the TurnKey setup."
echo -e "  ${TAB}Credentials stored in: ${BL}~/turnkey-${turnkey}.creds${CL}"
echo
