#!/bin/bash
###############################################################################
# vz_bw_audit.sh  (v2 - expert review pass)
#
# اسکریپت تشخیصی (فقط خواندنی / read-only) برای بررسی سلامت محاسبه ترافیک
# در Virtualizor (bw_collector.php) روی یک نود، با تمرکز ویژه روی اینکه
# فقط ترافیک "دانلود" (خروجی از VM به سمت اینترنت) شمرده و بیل می‌شه.
#
# این اسکریپت هیچ داده‌ای را در دیتابیس/سیستم تغییر نمی‌دهد.
#
# Usage:
#   sudo bash vz_bw_audit.sh [--sample-seconds 15] [--threshold-gb 500]
###############################################################################

set -u
LOG_DIR="/root/vz_bw_audit_logs"
TS="$(date +%Y%m%d_%H%M%S)"
LOG_FILE="${LOG_DIR}/audit_${TS}.log"
SAMPLE_SECONDS=15
THRESHOLD_GB=500
VZ_ROOT="/usr/local/virtualizor"
COLLECTOR="${VZ_ROOT}/scripts/bw_collector.php"

mkdir -p "$LOG_DIR"
log() { echo -e "$1" | tee -a "$LOG_FILE"; }
section() { log "\n================================================================\n$1\n================================================================"; }

while [[ $# -gt 0 ]]; do
  case "$1" in
    --sample-seconds) SAMPLE_SECONDS="$2"; shift 2;;
    --threshold-gb) THRESHOLD_GB="$2"; shift 2;;
    *) shift;;
  esac
done

section "شروع بررسی ($TS) - نتایج در $LOG_FILE ذخیره می‌شود"

if [[ $EUID -ne 0 ]]; then
  log "⚠️  با root اجرا کن."
  exit 1
fi

###############################################################################
# 1) بازرسی مستقیم خود bw_collector.php  (منبع حقیقت - نه حدس)
###############################################################################
section "1) بازرسی فایل واقعی $COLLECTOR"

if [[ ! -f "$COLLECTOR" ]]; then
  log "❌ فایل پیدا نشد در مسیر پیش‌فرض. مسیرهای مشابه:"
  find "$VZ_ROOT" -iname "bw_collector*" 2>/dev/null | tee -a "$LOG_FILE"
else
  log "✅ فایل پیدا شد."
  log "   mtime : $(stat -c '%y' "$COLLECTOR")"
  log "   owner : $(stat -c '%U:%G  mode=%a' "$COLLECTOR")"
  log "   md5   : $(md5sum "$COLLECTOR" | awk '{print $1}')"
  log "   (این md5 رو نگه دار؛ اگه بعداً Virtualizor آپدیت شد و مشکل حل شد، مقایسه‌اش کن.)"

  log "\n-- خطوطی که مشخص می‌کنن کدوم جهت (RX یا TX) به عنوان 'مصرف' حساب می‌شه:"
  log "   (این مهم‌ترین بخشه چون شما فقط دانلود/خروجی رو حساب می‌کنید، نه آپلود)"
  grep -n -iE "rx_bytes|tx_bytes|rx_byte|tx_byte|'rx'|'tx'|download|upload|net_dev|/proc/net/dev|getBWUsage|bw_used" "$COLLECTOR" 2>/dev/null | tee -a "$LOG_FILE"

  log "\n-- خطوطی که محل ذخیره «آخرین مقدار خوانده‌شده» (baseline قبلی) رو نشون می‌دن:"
  log "   (اگه این baseline با شمارنده واقعی اینترفیس ناهماهنگ بشه - مثلاً بعد از ری‌استارت VM"
  log "    یا بازسازی اینترفیس که شمارنده صفر می‌شه - دلتای محاسبه‌شده منفی/غول‌پیکر می‌شه)"
  grep -n -iE "last_bw|prev_bw|last_rx|last_tx|prev_rx|prev_tx|last_check|raw_bw|last_bytes" "$COLLECTOR" 2>/dev/null | tee -a "$LOG_FILE"

  log "\n-- آیا از vnstat استفاده می‌کنه یا مستقیم /proc/net/dev؟"
  grep -n -iE "vnstat" "$COLLECTOR" 2>/dev/null | tee -a "$LOG_FILE"
  if [[ -z "$(grep -iE 'vnstat' "$COLLECTOR" 2>/dev/null)" ]]; then
    log "   ردی از vnstat در فایل نبود => احتمالاً مستقیم از /proc/net/dev یا netlink می‌خونه."
  fi

  log "\n⚠️  اگه هیچ‌کدوم از الگوهای بالا پیدا نشد (خروجی خالیه)، یعنی فایل احتمالاً"
  log "   encode/obfuscate شده (ioncube/sourceguardian - رایج در نرم‌افزارهای Softaculous)."
  log "   در اون صورت نمی‌شه از روی سورس مطمئن شد و باید کاملاً empirical (بخش 6) قضاوت کرد."
fi

###############################################################################
# 2) پیدا کردن کانفیگ دیتابیس Virtualizor
###############################################################################
section "2) پیدا کردن اطلاعات اتصال دیتابیس Virtualizor"

DB_HOST=""; DB_USER=""; DB_PASS=""; DB_NAME=""
CANDIDATE_CONFIGS=(
  "${VZ_ROOT}/webroot/configuration/db.php"
  "${VZ_ROOT}/webroot/configuration.php"
  "${VZ_ROOT}/configuration.php"
  "${VZ_ROOT}/webroot/configuration/config.php"
  "${VZ_ROOT}/webroot/inc/db_conf.php"
)
for f in "${CANDIDATE_CONFIGS[@]}"; do
  if [[ -f "$f" ]]; then
    log "پیدا شد: $f"
    _host=$(grep -oP "(?<=mysql_host\s=\s')[^']+" "$f" 2>/dev/null | head -1)
    _user=$(grep -oP "(?<=mysql_user\s=\s')[^']+" "$f" 2>/dev/null | head -1)
    _pass=$(grep -oP "(?<=mysql_pass\s=\s')[^']+" "$f" 2>/dev/null | head -1)
    _name=$(grep -oP "(?<=mysql_db\s=\s')[^']+" "$f" 2>/dev/null | head -1)
    [[ -n "$_host" ]] && DB_HOST="$_host"
    [[ -n "$_user" ]] && DB_USER="$_user"
    [[ -n "$_pass" ]] && DB_PASS="$_pass"
    [[ -n "$_name" ]] && DB_NAME="$_name"
  fi
done
if [[ -z "$DB_PASS" && -d "${VZ_ROOT}/webroot" ]]; then
  MATCH=$(grep -rlP "mysql_pass\s*=" "${VZ_ROOT}/webroot" 2>/dev/null | head -1)
  if [[ -n "$MATCH" ]]; then
    log "fallback: کانفیگ احتمالی پیدا شد: $MATCH"
    DB_HOST=$(grep -oP "(?<=mysql_host\s=\s')[^']+" "$MATCH" 2>/dev/null | head -1)
    DB_USER=$(grep -oP "(?<=mysql_user\s=\s')[^']+" "$MATCH" 2>/dev/null | head -1)
    DB_PASS=$(grep -oP "(?<=mysql_pass\s=\s')[^']+" "$MATCH" 2>/dev/null | head -1)
    DB_NAME=$(grep -oP "(?<=mysql_db\s=\s')[^']+" "$MATCH" 2>/dev/null | head -1)
  fi
fi
DB_HOST="${DB_HOST:-localhost}"
DB_NAME="${DB_NAME:-virtualizor}"

if [[ -z "$DB_USER" || -z "$DB_PASS" ]]; then
  log "❌ نتونستم یوزر/پسورد دیتابیس رو خودکار پیدا کنم. دستی وارد کن (ذخیره نمی‌شه):"
  read -rp "DB_HOST [$DB_HOST]: " _in; DB_HOST="${_in:-$DB_HOST}"
  read -rp "DB_NAME [$DB_NAME]: " _in; DB_NAME="${_in:-$DB_NAME}"
  read -rp "DB_USER: " DB_USER
  read -rsp "DB_PASS: " DB_PASS; echo
fi

MYSQL="mysql -h $DB_HOST -u $DB_USER -p$DB_PASS $DB_NAME -N -B --connect-timeout=5"
if ! echo "SELECT 1;" | $MYSQL >/dev/null 2>&1; then
  log "❌ اتصال به دیتابیس برقرار نشد. بخش‌های DB رد می‌شن."
  DB_OK=0
else
  log "✅ اتصال به دیتابیس '$DB_NAME' برقرار شد."
  DB_OK=1
fi

###############################################################################
# 3) کشف اسکیمای دیتابیس
###############################################################################
VS_TABLE=""; BW_TABLE=""; VS_STATUS_COL=""; VS_IFACE_COL=""; VS_VIRT_COL=""

if [[ "$DB_OK" == "1" ]]; then
  section "3) کشف جدول‌ها و ستون‌های مرتبط با VM / bandwidth / baseline قبلی"

  VS_TABLE=$(echo "
    SELECT t.TABLE_NAME FROM information_schema.COLUMNS t
    WHERE t.TABLE_SCHEMA='$DB_NAME' AND t.COLUMN_NAME='vpsid'
    GROUP BY t.TABLE_NAME
    ORDER BY (SELECT COUNT(*) FROM information_schema.COLUMNS c2
              WHERE c2.TABLE_SCHEMA='$DB_NAME' AND c2.TABLE_NAME=t.TABLE_NAME) DESC
    LIMIT 5;" | $MYSQL 2>/dev/null)
  log "کاندیدهای جدول اصلی VM:\n$VS_TABLE"
  VS_TABLE=$(echo "$VS_TABLE" | head -1)
  log "==> انتخاب شد: '${VS_TABLE:-پیدا نشد}'"

  BW_CANDIDATES=$(echo "
    SELECT TABLE_NAME FROM information_schema.COLUMNS
    WHERE TABLE_SCHEMA='$DB_NAME' AND (COLUMN_NAME LIKE '%bandwidth%' OR COLUMN_NAME LIKE '%bw%')
    GROUP BY TABLE_NAME;" | $MYSQL 2>/dev/null)
  log "\nکاندیدهای جدول bandwidth:\n$BW_CANDIDATES"
  BW_TABLE=$(echo "$BW_CANDIDATES" | grep -iE 'bandwidth|bw' | head -1)
  log "==> انتخاب شد: '${BW_TABLE:-پیدا نشد}'"

  if [[ -n "$VS_TABLE" ]]; then
    COLS=$(echo "DESCRIBE $VS_TABLE;" | $MYSQL 2>/dev/null)
    log "\nستون‌های $VS_TABLE:\n$COLS"
    VS_STATUS_COL=$(echo "$COLS" | awk '{print $1}' | grep -iE '^(status|suspend|suspended)$' | head -1)
    VS_IFACE_COL=$(echo "$COLS" | awk '{print $1}' | grep -iE 'iface|interface|veth|vnet' | head -1)
    VS_VIRT_COL=$(echo "$COLS" | awk '{print $1}' | grep -iE '^virt$' | head -1)
    log "ستون suspend: ${VS_STATUS_COL:-پیدا نشد} | ستون interface: ${VS_IFACE_COL:-پیدا نشد} | ستون نوع مجازی‌سازی: ${VS_VIRT_COL:-پیدا نشد}"

    log "\n-- جستجوی ستون baseline/last-recorded (نامزد اصلی باگ desync شمارنده):"
    echo "$COLS" | grep -iE 'last_bw|prev_bw|last_rx|last_tx|prev_rx|prev_tx|last_check|raw_bw|last_bytes' | tee -a "$LOG_FILE"
  fi

  if [[ -n "$BW_TABLE" ]]; then
    log "\n-- نوع دیتای ستون‌های bandwidth (دنبال INT به‌جای BIGINT بگرد):"
    echo "SELECT COLUMN_NAME, DATA_TYPE, COLUMN_TYPE FROM information_schema.COLUMNS
          WHERE TABLE_SCHEMA='$DB_NAME' AND TABLE_NAME='$BW_TABLE'
          AND (COLUMN_NAME LIKE '%bw%' OR COLUMN_NAME LIKE '%bandwidth%' OR COLUMN_NAME LIKE '%used%'
               OR COLUMN_NAME LIKE '%download%' OR COLUMN_NAME LIKE '%upload%' OR COLUMN_NAME LIKE '%rx%' OR COLUMN_NAME LIKE '%tx%');" \
        | $MYSQL 2>/dev/null | tee -a "$LOG_FILE"
    log "\n⚠️  اگر DATA_TYPE برابر 'int'/'mediumint' بود (نه 'bigint')، این می‌تونه ریشه‌ی"
    log "    جهش‌های عجیب (مثل ۱۰۰ ترابایت) باشه: هر INT UNSIGNED فقط تا ~۴.۲۹GB (بایت) جا داره."

    log "\n-- آیا ستون جدا برای upload/download هست یا فقط یک ستون کلی 'bw'؟"
    log "   اگه فقط یک ستون کلی هست و بدون تفکیک RX/TX جمع می‌زنه، یعنی سمت DB داره"
    log "   upload+download رو با هم جمع می‌کنه ولی شما می‌خواید فقط download بیل بشه -"
    log "   این خودش یه mismatch جدا از باگ overflow است و باید توی منطق bw_collector.php"
    log "   (بخش 1) که کدوم ستون چطور پر می‌شه رو دقیق چک کنی."
  fi
fi

###############################################################################
# 4) لیست VM‌ها + تشخیص نوع مجازی‌سازی + اینترفیس متناظر
###############################################################################
section "4) لیست VM‌ها، وضعیت، نوع مجازی‌سازی و اینترفیس"

if [[ "$DB_OK" == "1" && -n "$VS_TABLE" ]]; then
  SEL_COLS="vpsid"
  [[ -n "$VS_STATUS_COL" ]] && SEL_COLS="$SEL_COLS, $VS_STATUS_COL"
  [[ -n "$VS_IFACE_COL" ]] && SEL_COLS="$SEL_COLS, $VS_IFACE_COL"
  [[ -n "$VS_VIRT_COL" ]] && SEL_COLS="$SEL_COLS, $VS_VIRT_COL"
  echo "SELECT $SEL_COLS FROM $VS_TABLE ORDER BY vpsid;" | $MYSQL 2>/dev/null | tee -a "$LOG_FILE"
else
  log "(رد شد - جدول VM از DB پیدا نشد)"
fi

log "\n-- اینترفیس‌های libvirt (اگه KVM/Xen باشه):"
if command -v virsh >/dev/null 2>&1; then
  virsh list --all 2>/dev/null | tee -a "$LOG_FILE"
  for d in $(virsh list --all --name 2>/dev/null | grep -v '^$'); do
    ifs=$(virsh domiflist "$d" 2>/dev/null | awk 'NR>2{print $1}')
    log "  $d  =>  ${ifs:-<no iface / stopped>}"
  done
fi

log "\n-- همه اینترفیس‌های veth/vnet/vif/tap و وضعیت up/down:"
ip -o link show 2>/dev/null | awk -F': ' '{print $2}' | grep -E '^(veth|vnet|vif|tap)' | while read -r ifc; do
  ifc_clean=$(echo "$ifc" | sed 's/@.*//')
  state=$(cat "/sys/class/net/${ifc_clean}/operstate" 2>/dev/null)
  log "  $ifc_clean  ->  operstate=$state"
done | tee -a "$LOG_FILE"

log "\nℹ️  نکته: نام‌گذاری اینترفیس بین انواع مجازی‌سازی فرق داره -"
log "   KVM/Xen معمولاً vnetX یا vifX.Y ، OpenVZ معمولاً venetX یا veth<CTID>.X ،"
log "   اگه چند نوع مجازی‌سازی روی این نود مخلوطه، حتماً ستون '$VS_VIRT_COL' رو نگاه کن"
log "   تا مطمئن بشی iface هر VM رو با پترن درست تطبیق می‌دی."

###############################################################################
# 5) اینترفیس تکراری بین چند VM
###############################################################################
if [[ "$DB_OK" == "1" && -n "$VS_TABLE" && -n "$VS_IFACE_COL" ]]; then
  section "5) اینترفیس تکراری بین چند VM (ترافیک یکی رو حساب دیگری می‌ره)"
  echo "SELECT $VS_IFACE_COL, COUNT(*) c, GROUP_CONCAT(vpsid) vpsids
        FROM $VS_TABLE WHERE $VS_IFACE_COL IS NOT NULL AND $VS_IFACE_COL != ''
        GROUP BY $VS_IFACE_COL HAVING c>1;" | $MYSQL 2>/dev/null > /tmp/vz_dup_iface.txt
  if [[ -s /tmp/vz_dup_iface.txt ]]; then
    log "❌ اینترفیس تکراری پیدا شد:"
    cat /tmp/vz_dup_iface.txt | tee -a "$LOG_FILE"
  else
    log "✅ اینترفیس تکراری پیدا نشد."
  fi
fi

###############################################################################
# 6) نمونه‌برداری زنده - RX و TX جدا از هم، با برچسب دقیق دانلود/آپلود
###############################################################################
section "6) نمونه‌برداری زنده (${SAMPLE_SECONDS}s) - تفکیک دانلود(egress) از آپلود(ingress)"

log "قرارداد رایج برای اینترفیس‌های tap/vnet/veth دیده‌شده از سمت هاست:"
log "  ΔRX روی این اینترفیس  = ترافیکی که VM به بیرون (اینترنت) فرستاده = «دانلود» مشتری از سرور شما"
log "  ΔTX روی این اینترفیس  = ترافیکی که از بیرون وارد VM شده = «آپلود» به سمت سرور شما"
log "⚠️  این قرارداد رایجه ولی garantee مطلق نیست - بستگی به driver/bridge/OVS داره."
log "   بهترین راه اثبات قطعی: از یک IP بیرونی یک دانلود با حجم مشخص (مثلاً 200MB) از"
log "   روی یکی از VMها بگیر (curl -o /dev/null http://VM_IP/testfile) دقیقاً همزمان با"
log "   اجرای این بخش، و ببین کدوم ستون (ΔRX یا ΔTX) همون VM حدود 200MB رشد کرد."

declare -A T1
IFACES=$(ip -o link show 2>/dev/null | awk -F': ' '{print $2}' | sed 's/@.*//' | grep -E '^(veth|vnet|vif|tap)')
for ifc in $IFACES; do
  rx=$(cat "/sys/class/net/${ifc}/statistics/rx_bytes" 2>/dev/null)
  tx=$(cat "/sys/class/net/${ifc}/statistics/tx_bytes" 2>/dev/null)
  T1[$ifc]="${rx:-0} ${tx:-0}"
done

log "\nدر حال نمونه‌برداری (${SAMPLE_SECONDS}s)..."
sleep "$SAMPLE_SECONDS"

log "\nاینترفیس | ΔRX=دانلود(احتمالی) bytes | ΔTX=آپلود(احتمالی) bytes"
for ifc in $IFACES; do
  rx2=$(cat "/sys/class/net/${ifc}/statistics/rx_bytes" 2>/dev/null)
  tx2=$(cat "/sys/class/net/${ifc}/statistics/tx_bytes" 2>/dev/null)
  read -r rx1 tx1 <<< "${T1[$ifc]}"
  drx=$(( ${rx2:-0} - ${rx1:-0} ))
  dtx=$(( ${tx2:-0} - ${tx1:-0} ))
  log "  $ifc | ΔRX=$drx | ΔTX=$dtx"
done | tee -a "$LOG_FILE"

if [[ "$DB_OK" == "1" && -n "$VS_TABLE" && -n "$VS_STATUS_COL" && -n "$VS_IFACE_COL" ]]; then
  log "\n-- تطبیق با VMهای suspended در دیتابیس:"
  echo "SELECT vpsid, $VS_STATUS_COL, $VS_IFACE_COL FROM $VS_TABLE
        WHERE $VS_STATUS_COL IN (1,'1','yes','suspended') AND $VS_IFACE_COL IS NOT NULL;" \
    | $MYSQL 2>/dev/null | while read -r vid st ifc; do
      ifc_clean=$(echo "$ifc" | sed 's/@.*//')
      log "  VM $vid (status=$st, iface=$ifc_clean): اگه ΔRX (دانلود) این iface در جدول بالا صفر نبود => مشکوک، چون یعنی VM ساسپندشده هنوز واقعاً از سرور شما داره ترافیک خروجی می‌فرسته."
  done | tee -a "$LOG_FILE"
fi

###############################################################################
# 7) مقایسه با ترافیک فیزیکی uplink (فقط جهت egress که مطابق download شماست)
###############################################################################
section "7) سنجش عقلانی: جمع دانلود(egress) همه VMها نباید از egress فیزیکی uplink بیشتر باشه"

UPLINK=$(ip route show default 2>/dev/null | awk '{print $5}' | head -1)
if [[ -n "$UPLINK" ]]; then
  log "uplink تشخیص داده شده: $UPLINK"
  utx1=$(cat "/sys/class/net/${UPLINK}/statistics/tx_bytes" 2>/dev/null)
  sleep 5
  utx2=$(cat "/sys/class/net/${UPLINK}/statistics/tx_bytes" 2>/dev/null)
  log "uplink ΔTX (خروجی فیزیکی واقعی سرور به اینترنت، 5s) = $(( ${utx2:-0} - ${utx1:-0} )) bytes"
  log "این عدد رو (به نسبت زمان) با مجموع ΔRX (بخش 6) همه VMها مقایسه کن."
  log "اگه مجموع VMها به‌طرز چشمگیری بیشتر بود => یه‌جا تکراری/اشتباه شمرده می‌شه، نه ترافیک واقعی."
else
  log "uplink خودکار پیدا نشد؛ با 'ip route show default' دستی چک کن."
fi

###############################################################################
# 8) vnstat (در صورت وجود) - کراس‌چک مستقل با اعداد Virtualizor
###############################################################################
if command -v vnstat >/dev/null 2>&1; then
  section "8) کراس‌چک با vnstat (منبع مستقل داده)"
  log "اینترفیس‌های تحت پوشش vnstat:"
  vnstat --iflist 2>/dev/null | tee -a "$LOG_FILE"
  log "\nاگه vnstat هم روی همون اینترفیس‌های VM فعاله، مصرف امروز رو با عدد پنل Virtualizor"
  log "مقایسه کن. اختلاف زیاد بین vnstat و Virtualizor یعنی مشکل مال منطق bw_collector.php"
  log "است، نه مشکل کرنل/شمارنده سیستم‌عامل."
fi

###############################################################################
# 9) جهش‌های غیرمنطقی در تاریخچه ثبت‌شده
###############################################################################
if [[ "$DB_OK" == "1" && -n "$BW_TABLE" ]]; then
  section "9) جهش‌های غیرمنطقی در تاریخچه bandwidth ثبت‌شده"
  echo "DESCRIBE $BW_TABLE;" | $MYSQL 2>/dev/null | tee -a "$LOG_FILE"
  log "\n(دستی: تاریخچه هر VM/روز رو بکش بیرون؛ دنبال مقادیری بگرد که >10x میانگین ۷ روز"
  log " اخیر همون VM باشن یا از ${THRESHOLD_GB}GB/روز رد بشن - کاندیدهای overflow/duplicate.)"
fi

###############################################################################
# 10) cron / اجرای هم‌زمان bw_collector.php
###############################################################################
section "10) زمان‌بندی و اجرای هم‌زمان bw_collector.php"

{ crontab -l 2>/dev/null; cat /etc/cron.d/* 2>/dev/null; } | grep -i "bw_collector" | tee -a "$LOG_FILE"
RUNNING=$(pgrep -fc "bw_collector.php")
log "تعداد نمونه‌ی در حال اجرا: ${RUNNING:-0}"
if [[ "${RUNNING:-0}" -gt 1 ]]; then
  log "❌ بیش از یک نمونه هم‌زمان در حال اجراست => احتمال race condition در محاسبه دلتا."
  log "   چک کن که lock file (مثلاً /tmp/bw_collector.lock یا مشابه) داره یا نه."
else
  log "✅ فقط یک نمونه (یا صفر) در حال اجراست."
fi

###############################################################################
# 11) جمع‌بندی
###############################################################################
section "جمع‌بندی و اولویت بررسی"
log "1. اول نتیجه بخش (1) رو نگاه کن: کدوم ستون (rx/tx) رو bw_collector.php واقعاً"
log "   به‌عنوان 'مصرف' ذخیره می‌کنه؟ اگه هر دو RX و TX رو جمع می‌زنه ولی قرار بوده فقط"
log "   دانلود(egress) حساب بشه، این خودش یه باگ منطقی مجزاست (نه overflow)."
log "2. اگه نوع ستون bandwidth در بخش (3) از نوع INT/MEDIUMINT بود (نه BIGINT)،"
log "   محتمل‌ترین علت «۱۰۰ ترابایت در روز» همینه."
log "3. اگه بخش (3) ستون last_bw/prev_bw/last_rx پیدا کرد ولی مقدارش با شمارنده واقعی"
log "   اینترفیس (بخش 6) به‌شدت فاصله داشت، یعنی baseline desync شده (مثلاً بعد از"
log "   ری‌بوت/migrate VM که شمارنده صفر شده ولی baseline قدیمی مونده) - این دقیقاً"
log "   می‌تونه دلتای منفی/غول‌پیکر تولید کنه."
log "4. اگه بخش (5) اینترفیس تکراری پیدا کرد، این توضیح می‌ده چرا بعضی VMها زیاد و"
log "   بعضی کم/صفر حساب می‌شن."
log "5. اگه بخش (6)/(7) نشون داد VM سوسپندشده هنوز egress واقعی داره، suspend فقط"
log "   با iptables انجام شده نه قطع واقعی اینترفیس - با 'iptables -L FORWARD -n -v' چک کن."
log "6. برای ریست فوری اعداد خراب یک VM خاص (بدون دست زدن به WHMCS):"
log "   https://HOST:4085/index.php?act=vs&bwreset=VPSID"
log "7. این کلاس باگ (اسپایک/محاسبه غلط، خصوصاً حول suspend) قبلاً هم در نسخه‌های"
log "   قدیمی‌تر Virtualizor دیده و فیکس شده؛ تکرارش روی 2.8.5 رو با همین لاگ"
log "   ($LOG_FILE) به‌عنوان ریگرشن به پشتیبانی Virtualizor گزارش بده."

log "\n✅ پایان بررسی. گزارش کامل: $LOG_FILE"