#!/bin/bash
#
# XUI.ONE 1.5.13 Installer (bootstrap)
# -----------------------------------
# Downloads the pre-patched XUI.ONE 1.5.13 release, extracts it, and runs the
# bundled installer. Modeled on the midesi.net installxui.sh wrapper, but pulls
# the package directly from the XUIPatch GitHub release.
#
# Usage (as root):
#   bash <(wget -qO- https://raw.githubusercontent.com/<you>/xui_installer/main/install_xui.sh)
#   -- or --
#   chmod +x install_xui.sh && ./install_xui.sh
#
set -euo pipefail

# ---------------------------------------------------------------------------
# Config
# ---------------------------------------------------------------------------
RELEASE_NAME="XUI_1.5.13-r7.zip"

# Primary download source: self-hosted Gitea generic-package mirror (fast, under
# our control). Override with the XUI_URL env var to point elsewhere.
DEFAULT_URL="https://git.ops01.hprx.zip/api/packages/seed/generic/xui/1.5.13-r7/${RELEASE_NAME}"
URL="${XUI_URL:-$DEFAULT_URL}"

# Fallback source: the upstream XUIPatch GitHub release. Tried automatically if
# the primary mirror is unreachable. Override with XUI_FALLBACK_URL.
FALLBACK_URL="${XUI_FALLBACK_URL:-https://github.com/xuione/XUIPatch/releases/download/main/${RELEASE_NAME}}"

# Integrity check: known-good SHA-256 of XUI_1.5.13.zip. Verified before extract.
# Override/clear with the XUI_SHA256 env var (set to "" to skip).
EXPECTED_SHA256="${XUI_SHA256-f6222964d31daf062b15d3db4378d3339e6131ee75f91c1897a895a9638fc108}"

WORKDIR="${XUI_WORKDIR:-/opt/xui_install}"

# License key. XUI 1.5.13 is the post-shutdown, license-free build: the patched
# extensions removed all license verification and the GTAXUI license servers are
# offline. The bundled installer only checks that the key is exactly 16 hex chars
# (isValidLicense: len==16 and all hex) — it never phones home. So any valid hex
# works; we auto-generate one unless XUI_LICENSE is provided.
XUI_LICENSE="${XUI_LICENSE:-}"

# Answer to the installer's "Overwrite sysctl configuration?" prompt (Y/N).
# Y is recommended (sets XUI's TCP tuning + open-file limits).
XUI_SYSCTL="${XUI_SYSCTL:-Y}"

# Set XUI_INTERACTIVE=1 to answer the installer's prompts yourself instead of
# letting this wrapper drive them non-interactively.
XUI_INTERACTIVE="${XUI_INTERACTIVE:-0}"

# ---------------------------------------------------------------------------
# Helpers
# ---------------------------------------------------------------------------
c_reset=$'\033[0m'; c_blue=$'\033[1;34m'; c_green=$'\033[1;32m'
c_yellow=$'\033[1;33m'; c_red=$'\033[1;31m'

info()  { printf '%s[*]%s %s\n' "$c_blue"   "$c_reset" "$*"; }
ok()    { printf '%s[+]%s %s\n' "$c_green"  "$c_reset" "$*"; }
warn()  { printf '%s[!]%s %s\n' "$c_yellow" "$c_reset" "$*"; }
die()   { printf '%s[x]%s %s\n' "$c_red"    "$c_reset" "$*" >&2; exit 1; }

# Return a valid 16-char hex license: use XUI_LICENSE if set (validated), else
# generate a random one.
resolve_license() {
  if [ -n "$XUI_LICENSE" ]; then
    if printf '%s' "$XUI_LICENSE" | grep -Eq '^[0-9a-fA-F]{16}$'; then
      printf '%s' "$XUI_LICENSE"; return 0
    fi
    die "XUI_LICENSE must be exactly 16 hex chars (got: '$XUI_LICENSE')."
  fi
  if command -v openssl >/dev/null 2>&1; then
    openssl rand -hex 8
  else
    head -c 8 /dev/urandom | od -An -tx1 | tr -d ' \n'
  fi
}

banner() {
  cat <<'EOF'

===============================================
            XUI.ONE 1.5.13 Installer
===============================================

EOF
}

# ---------------------------------------------------------------------------
# Pre-flight checks
# ---------------------------------------------------------------------------
preflight() {
  # Must be root
  if [ "$(id -u)" -ne 0 ]; then
    die "This installer must be run as root. Try: sudo su -  then re-run."
  fi

  # OS / version check (XUI 1.5.13 is tested on Ubuntu 20.04 / 22.04 / 24.04)
  if [ -r /etc/os-release ]; then
    # shellcheck disable=SC1091
    . /etc/os-release
    if [ "${ID:-}" != "ubuntu" ]; then
      warn "This package is tested on Ubuntu only (detected: ${PRETTY_NAME:-unknown})."
      warn "Continuing anyway, but the install may fail."
    else
      case "${VERSION_ID:-}" in
        20.04|22.04|24.04|26.04) ok "Ubuntu ${VERSION_ID} detected (supported)." ;;
        *) warn "Ubuntu ${VERSION_ID:-?} is untested for this build (supported: 20.04 / 22.04 / 24.04 / 26.04)." ;;
      esac
    fi
  else
    warn "Could not detect OS (no /etc/os-release). Proceeding cautiously."
  fi

  # Architecture
  arch="$(uname -m)"
  [ "$arch" = "x86_64" ] || warn "Architecture is '$arch'; XUI expects x86_64."

  # Resource warnings (non-fatal)
  mem_gb=$(awk '/MemTotal/ {printf "%d", $2/1024/1024}' /proc/meminfo 2>/dev/null || echo 0)
  [ "${mem_gb:-0}" -ge 8 ] || warn "Only ${mem_gb}GB RAM detected; 16GB+ recommended for production."
  disk_gb=$(df -BG --output=avail / 2>/dev/null | tail -1 | tr -dc '0-9' || echo 0)
  [ "${disk_gb:-0}" -ge 20 ] || warn "Only ${disk_gb}GB free on /; 40GB+ recommended."

  # Re-install guard
  if [ -d /home/xui ]; then
    warn "/home/xui already exists — XUI may already be installed."
    printf 'Continue and re-run the installer anyway? (y/N): '
    read -r ans
    case "$ans" in y|Y) ;; *) die "Aborted by user." ;; esac
  fi
}

# ---------------------------------------------------------------------------
# Dependencies
# ---------------------------------------------------------------------------
ensure_deps() {
  local missing=()
  for bin in wget unzip tar; do
    command -v "$bin" >/dev/null 2>&1 || missing+=("$bin")
  done
  if [ "${#missing[@]}" -gt 0 ]; then
    info "Installing missing dependencies: ${missing[*]}"
    export DEBIAN_FRONTEND=noninteractive
    apt-get update -qq
    apt-get install -y -qq "${missing[@]}"
  fi
}

# ---------------------------------------------------------------------------
# Download + verify
# ---------------------------------------------------------------------------
fetch() {
  mkdir -p "$WORKDIR"
  cd "$WORKDIR"

  info "Downloading XUI release package..."
  info "Primary source: $URL"
  if ! wget --tries=3 --timeout=60 --quiet --show-progress \
        --progress=bar:force:noscroll "$URL" -O "$RELEASE_NAME"; then
    warn "Primary source failed; trying fallback: $FALLBACK_URL"
    rm -f "$RELEASE_NAME"
    if ! wget --tries=3 --timeout=60 --quiet --show-progress \
          --progress=bar:force:noscroll "$FALLBACK_URL" -O "$RELEASE_NAME"; then
      die "Download failed from both primary and fallback sources."
    fi
  fi
  [ -s "$RELEASE_NAME" ] || die "Downloaded file is empty: $RELEASE_NAME"

  if [ -n "$EXPECTED_SHA256" ]; then
    info "Verifying checksum..."
    actual=$(sha256sum "$RELEASE_NAME" | awk '{print $1}')
    [ "$actual" = "$EXPECTED_SHA256" ] \
      || die "Checksum mismatch! expected=$EXPECTED_SHA256 actual=$actual"
    ok "Checksum verified."
  fi
}

# ---------------------------------------------------------------------------
# Patch the bundled installer for stale external dependencies
# ---------------------------------------------------------------------------
# The 2025-era bundled `install` references two sources that are now broken on
# fresh boxes:
#   1. MariaDB 10.6 repo at ams2.mirrors.digitalocean.com — DigitalOcean retired
#      their distro mirrors, so the host no longer resolves (DNS failure).
#   2. `add-apt-repository ppa:maxmind/ppa` — queries the launchpad.net API,
#      which hangs indefinitely on networks that can't reach launchpad.net. The
#      packages it provides (libmaxminddb0/-dev, mmdb-bin) ship in Ubuntu's own
#      repos anyway, so the PPA is unnecessary.
# These patches are best-effort: each only fires if its pattern is present, so a
# future fixed package is left untouched.
patch_installer() {
  [ -f install ] || return 0
  local changed=0

  if grep -q 'ams2\.mirrors\.digitalocean\.com' install; then
    sed -i 's|ams2\.mirrors\.digitalocean\.com/mariadb/repo/10\.6/ubuntu|mirror.rackspace.com/mariadb/repo/10.6/ubuntu|g' install
    info "Patched: MariaDB mirror -> mirror.rackspace.com (DigitalOcean mirror is dead)."
    changed=1
  fi

  if grep -q 'add-apt-repository -y ppa:maxmind/ppa' install; then
    sed -i 's|sudo add-apt-repository -y ppa:maxmind/ppa|true|g' install
    info "Patched: dropped maxmind PPA (launchpad.net can hang; pkgs are in Ubuntu repos)."
    changed=1
  fi

  # Add MariaDB READ-COMMITTED isolation to the panel's my.cnf. Avoids the
  # lines_divergence cleanup-cron gap-lock deadlocks (SQLSTATE 40001 / 1213).
  # Proven + persisted on the live 10.6 main 2026-08-14. Idempotent.
  if grep -q 'rMySQLCnf' install && ! grep -q 'transaction-isolation' install; then
    sed -i 's|\[mysqld\]\\n|[mysqld]\\ntransaction-isolation = READ-COMMITTED\\n|' install
    info "Patched: my.cnf transaction-isolation = READ-COMMITTED (deadlock fix)."
    changed=1
  fi

  [ "$changed" -eq 0 ] && info "No installer patches needed (upstream looks current)."
  return 0
}

# ---------------------------------------------------------------------------
# Extract + run
# ---------------------------------------------------------------------------
extract_and_run() {
  cd "$WORKDIR"
  info "Extracting files..."
  unzip -o -q "$RELEASE_NAME" || die "Extraction failed."

  [ -f install ] || die "Expected 'install' script not found in package."

  info "Cleaning up archive..."
  rm -f "$RELEASE_NAME"

  info "Setting permissions..."
  chmod +x install

  patch_installer

  if [ "$XUI_INTERACTIVE" = "1" ]; then
    cat <<'EOF'

-----------------------------------------------
 Handing over to the XUI installer (interactive).
 You will be prompted for:
   - your license key (any 16-char hex works)
   - sysctl overwrite (answer Y — recommended)
-----------------------------------------------

EOF
    info "Starting XUI install..."
    ./install
    return
  fi

  LICENSE="$(resolve_license)"
  local log="$WORKDIR/install.log"
  cat <<EOF

-----------------------------------------------
 Running the XUI installer non-interactively.
   License key : $LICENSE
   sysctl       : answering '$XUI_SYSCTL'
 (license servers are offline; any valid 16-hex
  key works — this build never phones home)
-----------------------------------------------

EOF
  info "Starting XUI install... (full log: $log)"
  # Feed: license key, then the sysctl answer. The extra answers cover any
  # conditional Y/N prompt without blocking. pipefail propagates install errors.
  printf '%s\n%s\nY\nY\n' "$LICENSE" "$XUI_SYSCTL" | ./install 2>&1 | tee "$log"

  # Surface the panel setup URL the installer prints at the end.
  SETUP_URL="$(grep -aoE 'http://[0-9.]+/[A-Za-z0-9]+' "$log" | tail -1 || true)"
}

# ---------------------------------------------------------------------------
# Main
# ---------------------------------------------------------------------------
main() {
  banner
  preflight
  ensure_deps
  fetch
  extract_and_run

  echo
  ok "XUI installation finished."
  if [ "$XUI_INTERACTIVE" != "1" ]; then
    printf '%s\n' "-----------------------------------------------"
    [ -n "${SETUP_URL:-}" ] && printf '  Panel setup URL : %s\n' "$SETUP_URL"
    printf '  License key     : %s\n' "${LICENSE:-<entered interactively>}"
    printf '  MySQL creds     : %s/credentials.txt\n' "$WORKDIR"
    printf '  Full log        : %s/install.log\n' "$WORKDIR"
    printf '%s\n' "-----------------------------------------------"
    warn "Save credentials.txt somewhere safe, then open the setup URL to finish."
  else
    info "Check ${WORKDIR}/credentials.txt and the 'Continue Setup' URL above."
  fi
}

main "$@"
