# Copyright (c) 2021-2026 community-scripts ORG
# Author: michelroegl-brunner
# License: MIT | https://raw.githubusercontent.com/community-scripts/ProxmoxVED/main/LICENSE

# ==============================================================================
# API.FUNC - TELEMETRY & DIAGNOSTICS API
# ==============================================================================
#
# Provides functions for sending anonymous telemetry data to Community-Scripts
# API for analytics and diagnostics purposes.
#
# Features:
#   - Container/VM creation statistics
#   - Installation success/failure tracking
#   - Error code mapping and reporting
#   - Privacy-respecting anonymous telemetry
#
# Usage:
#   source <(curl -fsSL .../api.func)
#   post_to_api          # Report container creation
#   post_update_to_api   # Report installation status
#
# Privacy:
#   - Only anonymous statistics (no personal data)
#   - User can opt-out via diagnostics settings
#   - Random UUID for session tracking only
#
# ==============================================================================

# ==============================================================================
# SECTION 1: ERROR CODE DESCRIPTIONS
# ==============================================================================

# ------------------------------------------------------------------------------
# explain_exit_code()
#
# - Maps numeric exit codes to human-readable error descriptions
# - Supports:
#   * Generic/Shell errors (1, 2, 124, 126-130, 134, 137, 139, 141, 143)
#   * curl/wget errors (6, 7, 22, 28, 35)
#   * Package manager errors (APT, DPKG: 100-102, 255)
#   * Systemd/Service errors (150-154)
#   * Python/pip/uv errors (160-162)
#   * PostgreSQL errors (170-173)
#   * MySQL/MariaDB errors (180-183)
#   * MongoDB errors (190-193)
#   * Proxmox custom codes (200-231)
#   * Node.js/npm errors (243, 245-249)
# - Returns description string for given exit code
# - Shared function with error_handler.func for consistency
# ------------------------------------------------------------------------------
explain_exit_code() {
  local code="$1"
  case "$code" in
  # --- Generic / Shell ---
  1) echo "General error / Operation not permitted" ;;
  2) echo "Misuse of shell builtins (e.g. syntax error)" ;;

  # --- curl / wget errors (commonly seen in downloads) ---
  6) echo "curl: DNS resolution failed (could not resolve host)" ;;
  7) echo "curl: Failed to connect (network unreachable / host down)" ;;
  22) echo "curl: HTTP error returned (404, 429, 500+)" ;;
  28) echo "curl: Operation timeout (network slow or server not responding)" ;;
  35) echo "curl: SSL/TLS handshake failed (certificate error)" ;;

  # --- Package manager / APT / DPKG ---
  100) echo "APT: Package manager error (broken packages / dependency problems)" ;;
  101) echo "APT: Configuration error (bad sources.list, malformed config)" ;;
  102) echo "APT: Lock held by another process (dpkg/apt still running)" ;;

  # --- Common shell/system errors ---
  124) echo "Command timed out (timeout command)" ;;
  126) echo "Command invoked cannot execute (permission problem?)" ;;
  127) echo "Command not found" ;;
  128) echo "Invalid argument to exit" ;;
  130) echo "Terminated by Ctrl+C (SIGINT)" ;;
  134) echo "Process aborted (SIGABRT - possibly Node.js heap overflow)" ;;
  137) echo "Killed (SIGKILL / Out of memory?)" ;;
  139) echo "Segmentation fault (core dumped)" ;;
  141) echo "Broken pipe (SIGPIPE - output closed prematurely)" ;;
  143) echo "Terminated (SIGTERM)" ;;

  # --- Systemd / Service errors (150-154) ---
  150) echo "Systemd: Service failed to start" ;;
  151) echo "Systemd: Service unit not found" ;;
  152) echo "Permission denied (EACCES)" ;;
  153) echo "Build/compile failed (make/gcc/cmake)" ;;
  154) echo "Node.js: Native addon build failed (node-gyp)" ;;

  # --- Python / pip / uv (160-162) ---
  160) echo "Python: Virtualenv / uv environment missing or broken" ;;
  161) echo "Python: Dependency resolution failed" ;;
  162) echo "Python: Installation aborted (permissions or EXTERNALLY-MANAGED)" ;;

  # --- PostgreSQL (170-173) ---
  170) echo "PostgreSQL: Connection failed (server not running / wrong socket)" ;;
  171) echo "PostgreSQL: Authentication failed (bad user/password)" ;;
  172) echo "PostgreSQL: Database does not exist" ;;
  173) echo "PostgreSQL: Fatal error in query / syntax" ;;

  # --- MySQL / MariaDB (180-183) ---
  180) echo "MySQL/MariaDB: Connection failed (server not running / wrong socket)" ;;
  181) echo "MySQL/MariaDB: Authentication failed (bad user/password)" ;;
  182) echo "MySQL/MariaDB: Database does not exist" ;;
  183) echo "MySQL/MariaDB: Fatal error in query / syntax" ;;

  # --- MongoDB (190-193) ---
  190) echo "MongoDB: Connection failed (server not running)" ;;
  191) echo "MongoDB: Authentication failed (bad user/password)" ;;
  192) echo "MongoDB: Database not found" ;;
  193) echo "MongoDB: Fatal query error" ;;

  # --- Proxmox Custom Codes (200-231) ---
  200) echo "Proxmox: Failed to create lock file" ;;
  203) echo "Proxmox: Missing CTID variable" ;;
  204) echo "Proxmox: Missing PCT_OSTYPE variable" ;;
  205) echo "Proxmox: Invalid CTID (<100)" ;;
  206) echo "Proxmox: CTID already in use" ;;
  207) echo "Proxmox: Password contains unescaped special characters" ;;
  208) echo "Proxmox: Invalid configuration (DNS/MAC/Network format)" ;;
  209) echo "Proxmox: Container creation failed" ;;
  210) echo "Proxmox: Cluster not quorate" ;;
  211) echo "Proxmox: Timeout waiting for template lock" ;;
  212) echo "Proxmox: Storage type 'iscsidirect' does not support containers (VMs only)" ;;
  213) echo "Proxmox: Storage type does not support 'rootdir' content" ;;
  214) echo "Proxmox: Not enough storage space" ;;
  215) echo "Proxmox: Container created but not listed (ghost state)" ;;
  216) echo "Proxmox: RootFS entry missing in config" ;;
  217) echo "Proxmox: Storage not accessible" ;;
  218) echo "Proxmox: Template file corrupted or incomplete" ;;
  219) echo "Proxmox: CephFS does not support containers - use RBD" ;;
  220) echo "Proxmox: Unable to resolve template path" ;;
  221) echo "Proxmox: Template file not readable" ;;
  222) echo "Proxmox: Template download failed" ;;
  223) echo "Proxmox: Template not available after download" ;;
  224) echo "Proxmox: PBS storage is for backups only" ;;
  225) echo "Proxmox: No template available for OS/Version" ;;
  231) echo "Proxmox: LXC stack upgrade failed" ;;

  # --- Node.js / npm / pnpm / yarn (243-249) ---
  243) echo "Node.js: Out of memory (JavaScript heap out of memory)" ;;
  245) echo "Node.js: Invalid command-line option" ;;
  246) echo "Node.js: Internal JavaScript Parse Error" ;;
  247) echo "Node.js: Fatal internal error" ;;
  248) echo "Node.js: Invalid C++ addon / N-API failure" ;;
  249) echo "npm/pnpm/yarn: Unknown fatal error" ;;

  # --- DPKG ---
  255) echo "DPKG: Fatal internal error" ;;

  # --- Default ---
  *) echo "Unknown error" ;;
  esac
}

# ==============================================================================
# SECTION 2: TELEMETRY FUNCTIONS
# ==============================================================================

# ------------------------------------------------------------------------------
# post_to_api()
#
# - Sends LXC container creation statistics to Community-Scripts API
# - Only executes if:
#   * curl is available
#   * DIAGNOSTICS=yes
#   * RANDOM_UUID is set
# - Payload includes:
#   * Container type, disk size, CPU cores, RAM
#   * OS type and version
#   * IPv6 disable status
#   * Application name (NSAPP)
#   * Installation method
#   * PVE version
#   * Status: "installing"
#   * Random UUID for session tracking
# - Anonymous telemetry (no personal data)
# ------------------------------------------------------------------------------
post_to_api() {

  if ! command -v curl &>/dev/null; then
    return
  fi

  if [ "$DIAGNOSTICS" = "no" ]; then
    return
  fi

  if [ -z "$RANDOM_UUID" ]; then
    return
  fi

  local API_URL="http://api.community-scripts.org/dev/upload"
  local pve_version="not found"
  pve_version=$(pveversion | awk -F'[/ ]' '{print $2}')

  JSON_PAYLOAD=$(
    cat <<EOF
{
    "ct_type": $CT_TYPE,
    "type":"lxc",
    "disk_size": $DISK_SIZE,
    "core_count": $CORE_COUNT,
    "ram_size": $RAM_SIZE,
    "os_type": "$var_os",
    "os_version": "$var_version",
    "nsapp": "$NSAPP",
    "method": "$METHOD",
    "pve_version": "$pve_version",
    "status": "installing",
    "random_id": "$RANDOM_UUID"
}
EOF
  )
  if [[ "$DIAGNOSTICS" == "yes" ]]; then
    RESPONSE=$(curl -s -w "%{http_code}" -L -X POST "$API_URL" --post301 --post302 \
      -H "Content-Type: application/json" \
      -d "$JSON_PAYLOAD") || true
  fi

}

# ------------------------------------------------------------------------------
# post_to_api_vm()
#
# - Sends VM creation statistics to Community-Scripts API
# - Similar to post_to_api() but for virtual machines (not containers)
# - Reads DIAGNOSTICS from /usr/local/community-scripts/diagnostics file
# - Payload differences:
#   * ct_type=2 (VM instead of LXC)
#   * type="vm"
#   * Disk size without 'G' suffix (parsed from DISK_SIZE variable)
# - Only executes if DIAGNOSTICS=yes and RANDOM_UUID is set
# ------------------------------------------------------------------------------
post_to_api_vm() {

  if [[ ! -f /usr/local/community-scripts/diagnostics ]]; then
    return
  fi
  DIAGNOSTICS=$(grep -i "^DIAGNOSTICS=" /usr/local/community-scripts/diagnostics | awk -F'=' '{print $2}')
  if ! command -v curl &>/dev/null; then
    return
  fi

  if [ "$DIAGNOSTICS" = "no" ]; then
    return
  fi

  if [ -z "$RANDOM_UUID" ]; then
    return
  fi

  local API_URL="http://api.community-scripts.org/dev/upload"
  local pve_version="not found"
  pve_version=$(pveversion | awk -F'[/ ]' '{print $2}')

  DISK_SIZE_API=${DISK_SIZE%G}

  JSON_PAYLOAD=$(
    cat <<EOF
{
    "ct_type": 2,
    "type":"vm",
    "disk_size": $DISK_SIZE_API,
    "core_count": $CORE_COUNT,
    "ram_size": $RAM_SIZE,
    "os_type": "$var_os",
    "os_version": "$var_version",
    "nsapp": "$NSAPP",
    "method": "$METHOD",
    "pve_version": "$pve_version",
    "status": "installing",
    "random_id": "$RANDOM_UUID"
}
EOF
  )
  if [[ "$DIAGNOSTICS" == "yes" ]]; then
    RESPONSE=$(curl -s -w "%{http_code}" -L -X POST "$API_URL" --post301 --post302 \
      -H "Content-Type: application/json" \
      -d "$JSON_PAYLOAD") || true
  fi
}

# ------------------------------------------------------------------------------
# post_update_to_api()
#
# - Reports installation completion status to API
# - Prevents duplicate submissions via POST_UPDATE_DONE flag
# - Arguments:
#   * $1: status ("success" or "failed")
#   * $2: exit_code (default: 1 for failed, 0 for success)
# - Payload includes:
#   * Final status (success/failed)
#   * Error description via get_error_description()
#   * Random UUID for session correlation
# - Only executes once per session
# - Silently returns if:
#   * curl not available
#   * Already reported (POST_UPDATE_DONE=true)
#   * DIAGNOSTICS=no
# ------------------------------------------------------------------------------
post_update_to_api() {

  if ! command -v curl &>/dev/null; then
    return
  fi

  # Initialize flag if not set (prevents 'unbound variable' error with set -u)
  POST_UPDATE_DONE=${POST_UPDATE_DONE:-false}

  if [ "$POST_UPDATE_DONE" = true ]; then
    return 0
  fi
  exit_code=${2:-1}
  local API_URL="http://api.community-scripts.org/dev/upload/updatestatus"
  local status="${1:-failed}"
  if [[ "$status" == "failed" ]]; then
    local exit_code="${2:-1}"
  elif [[ "$status" == "success" ]]; then
    local exit_code="${2:-0}"
  fi

  if [[ -z "$exit_code" ]]; then
    exit_code=1
  fi

  error=$(explain_exit_code "$exit_code")

  if [ -z "$error" ]; then
    error="Unknown error"
  fi

  JSON_PAYLOAD=$(
    cat <<EOF
{
    "status": "$status",
    "error": "$error",
    "random_id": "$RANDOM_UUID"
}
EOF
  )
  if [[ "$DIAGNOSTICS" == "yes" ]]; then
    RESPONSE=$(curl -s -w "%{http_code}" -L -X POST "$API_URL" --post301 --post302 \
      -H "Content-Type: application/json" \
      -d "$JSON_PAYLOAD") || true
  fi

  POST_UPDATE_DONE=true
}
