#!/usr/bin/env bash
set -Eeuo pipefail

# proxmox-subscription-popup.sh
#
# Safe Proxmox VE 9 popup suppressor with:
# - specific patch only
# - no repair unless --repair is passed
# - colored output
# - backup before patch
# - automatic rollback if patch verification fails
#
# Usage:
#   sudo bash proxmox-subscription-popup.sh
#   sudo bash proxmox-subscription-popup.sh --undo
#   sudo bash proxmox-subscription-popup.sh --repair
#   sudo bash proxmox-subscription-popup.sh --status

SCRIPT_NAME="$(basename "$0")"
JS_FILE="/usr/share/javascript/proxmox-widget-toolkit/proxmoxlib.js"
STATE_DIR="/root/.proxmox-subscription-popup"
BACKUP_FILE="${STATE_DIR}/proxmoxlib.js.bak"
PATCH_FILE="${STATE_DIR}/subscription-popup.patch"

DO_REPAIR=0
DO_UNDO=0
DO_STATUS=0

for arg in "$@"; do
  case "$arg" in
    --repair) DO_REPAIR=1 ;;
    --undo)   DO_UNDO=1 ;;
    --status) DO_STATUS=1 ;;
    -h|--help)
      cat <<'EOF'
Usage:
  sudo bash proxmox-subscription-popup.sh           Apply the patch
  sudo bash proxmox-subscription-popup.sh --undo    Restore backup if present
  sudo bash proxmox-subscription-popup.sh --repair  Reinstall owning package, restart proxy
  sudo bash proxmox-subscription-popup.sh --status  Show current state
EOF
      exit 0
      ;;
    *)
      echo "Unknown argument: $arg" >&2
      exit 2
      ;;
  esac
done

if [[ "${EUID}" -ne 0 ]]; then
  echo "Please run as root." >&2
  exit 1
fi

mkdir -p "$STATE_DIR"

if [[ -t 1 ]]; then
  C_RESET=$'\033[0m'
  C_BOLD=$'\033[1m'
  C_DIM=$'\033[2m'
  C_RED=$'\033[1;31m'
  C_GREEN=$'\033[1;32m'
  C_YELLOW=$'\033[1;33m'
  C_BLUE=$'\033[1;34m'
  C_MAGENTA=$'\033[1;35m'
  C_CYAN=$'\033[1;36m'
else
  C_RESET=""
  C_BOLD=""
  C_DIM=""
  C_RED=""
  C_GREEN=""
  C_YELLOW=""
  C_BLUE=""
  C_MAGENTA=""
  C_CYAN=""
fi

say()    { printf "%b\n" "$*"; }
info()   { say "${C_CYAN}▶${C_RESET} $*"; }
ok()     { say "${C_GREEN}✔${C_RESET} $*"; }
warn()   { say "${C_YELLOW}⚠${C_RESET} $*"; }
fail()   { say "${C_RED}✖${C_RESET} $*" >&2; }
title()  { say "${C_BOLD}${C_MAGENTA}$*${C_RESET}"; }

restart_proxy() {
  info "Restarting pveproxy..."
  systemctl restart pveproxy
  ok "pveproxy restarted"
}

show_browser_hint() {
  say
  say "${C_BOLD}Browser refresh:${C_RESET} hard refresh the UI after this."
  say "Mac: ${C_DIM}Cmd+Shift+R${C_RESET}"
  say "Other: ${C_DIM}Ctrl+Shift+R${C_RESET}"
  say
}

detect_owner_pkg() {
  dpkg -S "$JS_FILE" 2>/dev/null | head -n1 | cut -d: -f1
}

is_proxmox() {
  command -v pveversion >/dev/null 2>&1
}

version_major() {
  if ! command -v pveversion >/dev/null 2>&1; then
    return 1
  fi
  pveversion | sed -n 's/^pve-manager\/\([0-9]\+\)\..*/\1/p' | head -n1
}

is_patched() {
  grep -Fq "orig_cmd(); return;" "$JS_FILE"
}

has_expected_original() {
  grep -Fq "if (data.status.toLowerCase() !== 'active')" "$JS_FILE" &&
  grep -Fq "Ext.Msg.show({" "$JS_FILE"
}

write_patch_file() {
  cat > "$PATCH_FILE" <<'EOF'
--- proxmoxlib.js.orig
+++ proxmoxlib.js
@@ -1,7 +1,8 @@
-    checked_command: function(orig_cmd) {
+    checked_command: function(orig_cmd) {
+        orig_cmd(); return;
 	Proxmox.Utils.API2Request({
 	    url: '/nodes/localhost/subscription',
 	    method: 'GET',
 	    failure: function(response, opts) {
 		Ext.Msg.alert(gettext('Error'), response.htmlStatus);
 	    },
EOF
}

status() {
  title "Proxmox subscription popup status"

  if ! is_proxmox; then
    fail "This does not look like a Proxmox host."
    exit 1
  fi

  local major
  major="$(version_major || true)"
  if [[ -n "$major" ]]; then
    info "Detected Proxmox major version: ${C_BOLD}${major}${C_RESET}"
  fi

  if [[ ! -f "$JS_FILE" ]]; then
    fail "JS file not found: $JS_FILE"
    exit 1
  fi

  local owner
  owner="$(detect_owner_pkg || true)"
  if [[ -n "$owner" ]]; then
    info "Owning package: ${C_BOLD}${owner}${C_RESET}"
  fi

  if is_patched; then
    ok "Popup suppression patch appears to be installed."
  else
    warn "Popup suppression patch does not appear to be installed."
  fi

  if [[ -f "$BACKUP_FILE" ]]; then
    info "Backup present: ${C_BOLD}${BACKUP_FILE}${C_RESET}"
  else
    warn "No backup found in ${STATE_DIR}"
  fi
}

repair() {
  title "Repairing Proxmox web UI file"

  [[ -f "$JS_FILE" ]] || { fail "Missing file: $JS_FILE"; exit 1; }

  local owner
  owner="$(detect_owner_pkg || true)"
  [[ -n "$owner" ]] || { fail "Could not determine owning package for $JS_FILE"; exit 1; }

  info "Reinstalling package: ${C_BOLD}${owner}${C_RESET}"
  apt-get update
  apt-get install --reinstall -y "$owner"

  [[ -f "$JS_FILE" ]] || { fail "File still missing after reinstall"; exit 1; }

  restart_proxy
  ok "Repair complete"
  show_browser_hint
}

undo() {
  title "Restoring original file"

  [[ -f "$BACKUP_FILE" ]] || { fail "No backup found at $BACKUP_FILE"; exit 1; }
  [[ -f "$JS_FILE" ]] || { fail "Target file missing: $JS_FILE"; exit 1; }

  cp -f "$BACKUP_FILE" "$JS_FILE"
  ok "Original file restored from backup"

  restart_proxy
  show_browser_hint
}

apply_patch() {
  title "Applying safe Proxmox popup patch"

  [[ -f "$JS_FILE" ]] || { fail "Missing file: $JS_FILE"; exit 1; }

  if ! is_proxmox; then
    fail "This does not look like a Proxmox host."
    exit 1
  fi

  local major
  major="$(version_major || true)"
  if [[ "$major" != "9" ]]; then
    warn "This script was requested for Proxmox 9. Detected major version: ${major:-unknown}"
    warn "Continuing only if the target code still matches exactly."
  else
    ok "Detected Proxmox VE 9"
  fi

  if is_patched; then
    ok "Patch is already installed. Nothing to do."
    show_browser_hint
    return 0
  fi

  if ! has_expected_original; then
    fail "The expected original code pattern was not found."
    fail "Refusing to patch, because the file layout is not what this script expects."
    fail "Use --repair first if the file is damaged, or inspect the current file manually."
    exit 1
  fi

  if [[ ! -f "$BACKUP_FILE" ]]; then
    info "Creating backup at ${C_BOLD}${BACKUP_FILE}${C_RESET}"
    cp -a "$JS_FILE" "$BACKUP_FILE"
    ok "Backup created"
  else
    warn "Backup already exists, leaving it untouched"
  fi

  local tmp
  tmp="$(mktemp)"
  cp -a "$JS_FILE" "$tmp"

  write_patch_file

  info "Checking whether patch applies cleanly..."
  if ! patch --dry-run "$tmp" "$PATCH_FILE" >/dev/null 2>&1; then
    rm -f "$tmp"
    fail "Patch dry-run failed. File is not an exact match for this patch."
    fail "No changes were made."
    exit 1
  fi
  ok "Patch dry-run succeeded"

  info "Applying patch..."
  patch "$JS_FILE" "$PATCH_FILE" >/dev/null
  ok "Patch applied"

  if ! is_patched; then
    warn "Verification failed after patch; restoring backup"
    cp -f "$BACKUP_FILE" "$JS_FILE"
    rm -f "$tmp"
    fail "Patch did not verify cleanly. Original file restored."
    exit 1
  fi

  rm -f "$tmp"

  restart_proxy
  ok "Subscription popup suppression is in place"
  show_browser_hint
}

case 1 in
  $DO_STATUS) status ;;
  $DO_UNDO) undo ;;
  $DO_REPAIR) repair ;;
  *) apply_patch ;;
esac