#!/usr/bin/env bash
# onx-db-orphan-purge — YETİM (silinmiş hesaplara ait) müşteri veritabanlarını
# GÜVENLE temizler: önce mysqldump yedeği, SONRA DROP DATABASE.
#
# Çok katmanlı güvenlik (bir DB ancak TÜM testleri geçerse "yetim" sayılır):
#   1) HARD denylist: sistem + panel + paylaşımlı DB'ler ASLA aday olmaz.
#   2) onoxsoft_panel.mysql_databases'te AKTİF hesaba bağlı satırı OLMAYACAK.
#   3) Adı, AKTİF bir hesabın username'i (ya da onx_-soyulmuş kökü) ile başlamayacak.
#   4) AKTİF hesapların home'larındaki config dosyalarında (wp-config.php/.env/
#      configuration.php/config.php) adı GEÇMEYECEK (canlı site kullanıyorsa KORU).
#   + Yedek-önce-sil: her DB DROP'tan önce sıkıştırılmış mysqldump alınır; yedek
#     boş/başarısızsa o DB DROP EDİLMEZ. → yanlış sınıflandırma bile GERİ DÖNÜLEBİLİR.
#   + Sanity guard: hesaplanan yetim sayısı MAX_ORPHANS'ı aşarsa komple ABORT.
#
# Kullanım:
#   onx-db-orphan-purge            # DRY-RUN (sadece listeler, hiçbir şey yapmaz)
#   onx-db-orphan-purge --execute  # yedekle + DROP (geri dönülebilir)
#
# root + MariaDB socket/.cnf erişimi gerektirir.
set -uo pipefail

EXECUTE=false
[[ "${1:-}" == "--execute" ]] && EXECUTE=true

PANEL_DB="onoxsoft_panel"
HOME_BASE="/home/users"
BACKUP_DIR="/var/backups/onoxsoft/orphan-db"
MAX_ORPHANS=40
# date kullanımı: tek seferlik script (resume gerektirmez) → güvenli.
TS="$(date +%Y%m%d-%H%M%S)"

# Asla dokunulmayacak DB'ler (sistem + panel + paylaşımlı servisler)
read -r -d '' PROTECTED_SQL <<'EOF' || true
'mysql','information_schema','performance_schema','sys','phpmyadmin',
'onoxsoft_panel','onoxsoft_pdns','roundcube','roundcubemail'
EOF
PROTECTED_SQL="$(echo "${PROTECTED_SQL}" | tr -d '\n')"

[[ $EUID -eq 0 ]] || { echo "[XX] root gerekli"; exit 1; }
command -v mysql >/dev/null 2>&1     || { echo "[XX] mysql yok"; exit 1; }
command -v mysqldump >/dev/null 2>&1 || { echo "[XX] mysqldump yok"; exit 1; }

# Root MySQL auth — sysapi mysql_exec_root ile AYNI yöntem. Bu sunucuda plain `mysql`
# kimlik doğrulayamıyor (root parolası /root/.onox-mysql-root.cnf'te). --defaults-extra-file
# İLK argüman olmalı; yoksa unix_socket (-u root) fallback.
MYSQL_AUTH=(-u root)
for cand in /root/.onox-mysql-root.cnf /root/.my.cnf; do
  if [[ -r "$cand" ]] && grep -qE '^[[:space:]]*(user|password)[[:space:]]*=' "$cand" 2>/dev/null; then
    MYSQL_AUTH=(--defaults-extra-file="$cand"); break
  fi
done

mysql_q()    { mysql "${MYSQL_AUTH[@]}" -N -B -e "$1" 2>/dev/null; }
mysql_exec() { mysql "${MYSQL_AUTH[@]}" -N -B -e "$1"; }

# panel DB var mı? (yanlış sunucuda koşmayı engelle)
if [[ "$(mysql_q "SELECT COUNT(*) FROM information_schema.SCHEMATA WHERE SCHEMA_NAME='${PANEL_DB}';")" != "1" ]]; then
  echo "[XX] ${PANEL_DB} bulunamadı — yanlış sunucu? ABORT"; exit 1
fi

# ── Katman 1+2+3: aday yetim listesini SQL ile hesapla ───────────────────────
read -r -d '' ORPHAN_SQL <<EOF || true
SELECT s.SCHEMA_NAME
FROM information_schema.SCHEMATA s
WHERE s.SCHEMA_NAME NOT IN (${PROTECTED_SQL})
  AND NOT EXISTS (
        SELECT 1 FROM ${PANEL_DB}.mysql_databases md
          JOIN ${PANEL_DB}.accounts a ON a.id = md.account_id
         WHERE md.real_db_name = s.SCHEMA_NAME
           AND a.status <> 'terminated' AND a.deleted_at IS NULL )
  AND NOT EXISTS (
        SELECT 1 FROM ${PANEL_DB}.accounts a2
         WHERE a2.status <> 'terminated' AND a2.deleted_at IS NULL
           AND ( s.SCHEMA_NAME = a2.username
              OR s.SCHEMA_NAME LIKE CONCAT(a2.username, '\\_%')
              OR s.SCHEMA_NAME LIKE CONCAT(REPLACE(a2.username,'onx_',''), '\\_%') ) )
ORDER BY s.SCHEMA_NAME;
EOF

mapfile -t CANDIDATES < <(mysql_q "${ORPHAN_SQL}")
# boşları ele
TMP=(); for d in "${CANDIDATES[@]}"; do [[ -n "$d" ]] && TMP+=("$d"); done; CANDIDATES=("${TMP[@]}")

if [[ "${#CANDIDATES[@]}" -eq 0 ]]; then
  echo "[OK] Yetim veritabanı bulunamadı — temiz."; exit 0
fi

if [[ "${#CANDIDATES[@]}" -gt "${MAX_ORPHANS}" ]]; then
  echo "[XX] ${#CANDIDATES[@]} aday > MAX_ORPHANS=${MAX_ORPHANS} — sınıflandırma şüpheli, ABORT (elle incele)."
  printf '   aday: %s\n' "${CANDIDATES[@]}"
  exit 1
fi

# ── Katman 4: aktif site config'lerinde adı geçenleri AYIKLA (canlı kullanımda) ─
in_use_by_live_site() {
  local db="$1"
  [[ -d "${HOME_BASE}" ]] || return 1
  grep -rqlI \
    --include="wp-config.php" --include=".env" \
    --include="configuration.php" --include="config.php" --include="config.inc.php" \
    -- "${db}" "${HOME_BASE}" 2>/dev/null
}

ORPHANS=(); SKIPPED_LIVE=()
for db in "${CANDIDATES[@]}"; do
  if in_use_by_live_site "${db}"; then
    SKIPPED_LIVE+=("${db}")
  else
    ORPHANS+=("${db}")
  fi
done

echo "==================================================================="
echo " onx-db-orphan-purge  —  $([[ $EXECUTE == true ]] && echo 'EXECUTE (yedekle+DROP)' || echo 'DRY-RUN (sadece liste)')"
echo " panel DB: ${PANEL_DB}   yedek: ${BACKUP_DIR}"
echo "==================================================================="

if [[ "${#SKIPPED_LIVE[@]}" -gt 0 ]]; then
  echo ""
  echo " ⚠ CANLI config'te adı geçtiği için KORUNAN (DROP edilmeyecek):"
  for d in "${SKIPPED_LIVE[@]}"; do echo "    KEEP  ${d}"; done
fi

echo ""
echo " Yetim (silinecek) — ${#ORPHANS[@]} adet:"
if [[ "${#ORPHANS[@]}" -eq 0 ]]; then echo "    (yok)"; fi

TOTAL_DROPPED=0
for db in "${ORPHANS[@]}"; do
  sz="$(mysql_q "SELECT COALESCE(ROUND(SUM(data_length+index_length)/1048576,1),0) FROM information_schema.tables WHERE table_schema='${db}';")"
  if [[ "${EXECUTE}" != "true" ]]; then
    echo "    [DRY] backup+DROP → ${db}  (${sz} MB)"
    continue
  fi

  mkdir -p "${BACKUP_DIR}"
  out="${BACKUP_DIR}/${db}-${TS}.sql.gz"
  if mysqldump "${MYSQL_AUTH[@]}" --single-transaction --quick --routines --triggers --events "${db}" 2>/dev/null | gzip > "${out}"; then
    if [[ -s "${out}" ]]; then
      if mysql_exec "DROP DATABASE IF EXISTS \`${db}\`;" >/dev/null 2>&1; then
        echo "    DROPPED ${db}  (${sz} MB)  yedek: ${out}"
        TOTAL_DROPPED=$((TOTAL_DROPPED+1))
      else
        echo "    [XX] ${db} DROP başarısız (yedek alındı: ${out}) — atlandı"
      fi
    else
      echo "    [XX] ${db} yedek BOŞ → DROP EDİLMEDİ"; rm -f "${out}"
    fi
  else
    echo "    [XX] ${db} mysqldump başarısız → DROP EDİLMEDİ"
  fi
done

echo ""
if [[ "${EXECUTE}" == "true" ]]; then
  echo " BİTTİ — ${TOTAL_DROPPED}/${#ORPHANS[@]} DB yedeklenip düşürüldü. Yedekler: ${BACKUP_DIR}"
  echo " Geri yükleme:  zcat ${BACKUP_DIR}/<db>-${TS}.sql.gz | mysql <yeni_db>"
else
  echo " DRY-RUN bitti. Gerçekten silmek için:  bash $0 --execute"
fi
echo "==================================================================="
