#!/usr/bin/env bash
# =============================================================================
# onx-user-terminate — Permanently remove a hosting account (v87 — idempotent)
#
# v87 changes:
#   - Cleanup steps run independently (no short-circuit on first failure).
#   - Apache / PHP-FPM reload failures = WARN logged but script exits 0
#     when userdel + file removal succeed.
#   - Returns JSON with both `warnings: []` (non-fatal) and `errors: []` (fatal).
#   - New input flags:
#       force: bool        — skip ALL service reloads (just userdel + files)
#       keep_backup: bool  — default true; set false to skip tar.gz
#   - Backwards compatible: legacy `backup_first` still honoured if present.
#
# Input (stdin JSON):
#   {
#     "username":     "onx_xxxx",   -- required
#     "delete_data":  true,         -- default true; false = keep /home/<user>
#     "backup_first": true,         -- legacy alias for keep_backup
#     "keep_backup":  true,         -- default true; create tar.gz before delete
#     "force":        false,        -- default false; skip all reloads
#     "home_base":    "/home/users" -- optional
#   }
#
# Output (stdout JSON):
#   {
#     "username":   "...",
#     "removed":    true,
#     "backup_path": "<path>" | null,
#     "user_removed": true,
#     "home_removed": true,
#     "warnings": ["httpd reload skipped — servis zaten down", ...],
#     "errors":   []
#   }
#
# Exit codes:
#   0 — success (with or without warnings)
#   1 — invalid input
#   2 — preflight fail
#   3 — fatal: userdel failed AND user existed
# =============================================================================

set -uo pipefail
# NOTE: `-e` intentionally OFF — we want every cleanup step to run even if
# an earlier step fails. We collect warnings and at the end decide exit.

SCRIPT_DIR="$(dirname "$(readlink -f "$0")")"
# shellcheck source=_lib/common.sh
source "${SCRIPT_DIR}/_lib/common.sh"

# ── Constants ─────────────────────────────────────────────────────────────────
TERM_BACKUP_DIR="/var/onox/term-backups"
VHOST_ACTIVE_DIR="/etc/httpd/conf.d/sites"
VHOST_SUSPENDED_DIR="/etc/httpd/suspended"
FPM_SOCK_DIR="/run/php-fpm"

# ── Warning / error accumulators ──────────────────────────────────────────────
declare -a WARNINGS=()
declare -a ERRORS=()

warn() {
    WARNINGS+=("$1")
    onx_log "WARN: $1"
}
err() {
    ERRORS+=("$1")
    onx_log "ERROR: $1"
}

# ── Dependencies ──────────────────────────────────────────────────────────────
command -v jq       >/dev/null 2>&1 || { printf '{"error":"jq required"}\n' >&2; exit 2; }
command -v userdel  >/dev/null 2>&1 || { printf '{"error":"userdel required"}\n' >&2; exit 2; }
command -v tar      >/dev/null 2>&1 || { printf '{"error":"tar required"}\n' >&2; exit 2; }
# mysql optional — DB cleanup is best-effort
require_root

# ── Read & parse stdin ────────────────────────────────────────────────────────
INPUT=$(cat)
onx_require_json "${INPUT}"

USERNAME=$(onx_json_get      "${INPUT}" "username")
DELETE_DATA=$(onx_json_get_bool "${INPUT}" "delete_data"  "true")

# v87: keep_backup is the new canonical flag, backup_first is the legacy alias.
# If keep_backup is set, it wins. Otherwise fall back to backup_first (default true).
KEEP_BACKUP_RAW=$(echo "${INPUT}" | jq -r 'if has("keep_backup") then (if .keep_backup then "true" else "false" end) else "" end')
if [[ -z "${KEEP_BACKUP_RAW}" ]]; then
    KEEP_BACKUP=$(onx_json_get_bool "${INPUT}" "backup_first" "true")
else
    KEEP_BACKUP="${KEEP_BACKUP_RAW}"
fi

# v87: force=true skips all service reloads — admin "Force Delete" path.
FORCE=$(onx_json_get_bool "${INPUT}" "force" "false")

# ── Input validation ──────────────────────────────────────────────────────────
onx_validate_username "${USERNAME}"

onx_log "terminate start: user=${USERNAME} delete_data=${DELETE_DATA} keep_backup=${KEEP_BACKUP} force=${FORCE}"

# ── Preflight: resolve user + home dir ────────────────────────────────────────
# IDEMPOTENT: user yoksa panel DB-level cleanup için yine de başarılı dönsün
USER_EXISTS=0
if id "${USERNAME}" &>/dev/null; then
    USER_EXISTS=1
fi
HOME_BASE_OPT=$(onx_json_get "${INPUT}" "home_base" "")
[[ -z "${HOME_BASE_OPT}" ]] && HOME_BASE_OPT="/home/users"

# Multi-path fallback — /home/users (yeni), /home (eski hesaplar)
HOME_DIR=""
for base in "${HOME_BASE_OPT}" "/home/users" "/home"; do
    if [[ -d "${base}/${USERNAME}" ]]; then
        HOME_DIR="${base}/${USERNAME}"
        break
    fi
done
[[ -z "${HOME_DIR}" ]] && HOME_DIR="${HOME_BASE_OPT}/${USERNAME}"

USER_REMOVED="false"
HOME_REMOVED="false"
BACKUP_PATH="null"

# Eğer hiçbiri yoksa: clean DB-only terminate — sysapi success döner
if [[ "${USER_EXISTS}" -eq 0 && ! -d "${HOME_DIR}" ]]; then
    onx_log "user does not exist AND no home dir — db-only terminate"
    # Emit JSON with empty warnings/errors arrays — note that this exits 0.
    printf '{"username":"%s","removed":true,"backup_path":null,"user_removed":false,"home_removed":false,"warnings":[],"errors":[],"note":"user_not_present_db_only_cleanup"}\n' \
        "${USERNAME}"
    exit 0
fi

# ── Step 1: optional backup ───────────────────────────────────────────────────
if [[ "${KEEP_BACKUP}" == "true" && -d "${HOME_DIR}" ]]; then
    if mkdir -p "${TERM_BACKUP_DIR}" 2>/dev/null; then
        TS=$(date -u +"%Y%m%d%H%M%S")
        CANDIDATE="${TERM_BACKUP_DIR}/${USERNAME}-${TS}.tar.gz"
        # tar to parent of HOME_DIR so the archive root is just "<user>/"
        HOME_PARENT=$(dirname "${HOME_DIR}")
        HOME_LEAF=$(basename "${HOME_DIR}")
        if tar -czf "${CANDIDATE}" -C "${HOME_PARENT}" "${HOME_LEAF}" 2>/dev/null; then
            BACKUP_PATH="${CANDIDATE}"
            onx_log "backup created: ${BACKUP_PATH}"
        else
            warn "tar backup başarısız (silme yine de devam)"
        fi
    else
        warn "yedek dizini oluşturulamadı: ${TERM_BACKUP_DIR}"
    fi
else
    onx_log "backup skipped (keep_backup=${KEEP_BACKUP}, home_dir=${HOME_DIR})"
fi

# ── Step 1.5: v87 — Deploy "terminated" placeholder BEFORE userdel ────────────
# v3.46 FIX: Soft-terminate mode'da (delete_data=false) home dir KORUNUR →
# terminated.html docroot'a deploy edilmeli + KALICI olmalı (cPanel davranışı).
# Hard mode'da (delete_data=true) zaten userdel -r home'u siler → deploy gereksiz
# (ama döneminde bir kez deploy olsa kalıntı bug çıkarmaz).
# Yani HER İKİ MODE'DA da deploy et — sadece home_dir/public_html mevcutsa.
if [[ -x "${SCRIPT_DIR}/onx-default-page-deploy" && -d "${HOME_DIR}/public_html" ]]; then
    TERM_AT=$(date -u +"%Y-%m-%dT%H:%M:%SZ")
    DEPLOY_PAYLOAD=$(jq -nc \
        --arg u "${USERNAME}" \
        --arg d "" \
        --arg r "${HOME_DIR}/public_html" \
        --arg t "${TERM_AT}" \
        '{
            username: $u,
            domain: $d,
            state: "terminated",
            docroot: $r,
            lang: "tr",
            terminated_at: $t,
            force: true
        }')
    echo "${DEPLOY_PAYLOAD}" | "${SCRIPT_DIR}/onx-default-page-deploy" >/dev/null 2>&1 || \
        onx_log "WARN: default-page-deploy terminated skipped (pre-userdel)"
fi

# ── Step 2: kill user processes ───────────────────────────────────────────────
if [[ "${USER_EXISTS}" -eq 1 ]]; then
    pkill -KILL -u "${USERNAME}" 2>/dev/null || true
    onx_log "pkill -KILL -u ${USERNAME} sent"
fi

# ── Step 3: remove Linux user (and home if delete_data=true) ──────────────────
if [[ "${USER_EXISTS}" -eq 1 ]]; then
    if [[ "${DELETE_DATA}" == "true" ]]; then
        if userdel -r "${USERNAME}" 2>/dev/null; then
            USER_REMOVED="true"
            HOME_REMOVED="true"
        elif userdel "${USERNAME}" 2>/dev/null; then
            USER_REMOVED="true"
            # Home dizini hâlâ varsa manuel sil
            if [[ -d "${HOME_DIR}" ]]; then
                if rm -rf "${HOME_DIR}" 2>/dev/null; then
                    HOME_REMOVED="true"
                else
                    warn "home dir silinemedi: ${HOME_DIR}"
                fi
            else
                HOME_REMOVED="true"
            fi
        else
            err "userdel başarısız: ${USERNAME}"
        fi
    else
        # delete_data=false: sadece Linux user'ı kaldır, /home/<user> dursun
        if userdel "${USERNAME}" 2>/dev/null; then
            USER_REMOVED="true"
        else
            err "userdel başarısız (data preserve): ${USERNAME}"
        fi
    fi
    onx_log "userdel done: user_removed=${USER_REMOVED} home_removed=${HOME_REMOVED}"
else
    # User yoktu ama home dir varsa (orphan) onu sil
    if [[ "${DELETE_DATA}" == "true" && -d "${HOME_DIR}" ]]; then
        if rm -rf "${HOME_DIR}" 2>/dev/null; then
            HOME_REMOVED="true"
            onx_log "orphan home dir removed: ${HOME_DIR}"
        else
            warn "orphan home dir silinemedi: ${HOME_DIR}"
        fi
    fi
fi

# ── Step 4: remove vhost configs (active + suspended) ─────────────────────────
# Apache directory + multi-webserver fallback dirs. Idempotent (glob = no match OK).
for dir in \
    "${VHOST_ACTIVE_DIR}" \
    "${VHOST_SUSPENDED_DIR}" \
    "/etc/httpd/conf.d/onox" \
    "/etc/nginx/conf.d/sites" \
    "/usr/local/lsws/conf/vhosts" \
    "/etc/caddy/sites"; do
    if [[ -d "${dir}" ]]; then
        rm -f "${dir}/${USERNAME}"-*.conf 2>/dev/null  || true
    fi
done
onx_log "vhost configs removed"

# ── v3.67: OLS-aware vhost handling ───────────────────────────────────────────
# Step 4 glob (${USERNAME}-*.conf) OLS'nin ALT-DİZİN yapısını (<user>-<domain>/
# vhconf.conf) kaçırıyor → terminate'te OLS vhost'u + listener map'i ASILI KALIYOR:
#   - hard (data silindi): docroot userdel -r ile gitti, map hâlâ route ediyor →
#     boş docroot → OLS DEFAULT sayfası (operatörün gördüğü bug).
#   - soft (data korundu): terminated.html docroot'ta ama OLS rewrite /index.php'ye
#     gidiyor → branded sayfa görünmüyor.
# Çözüm: soft → vhost'u KORU + .onx-suspended marker (rewrite branded index.html
# serve eder); hard → vhost dizini + listener map/blok temizle. Best-effort.
if [[ -d /usr/local/lsws/conf/vhosts ]]; then
    OLS_LISTENER="/usr/local/lsws/conf/httpd_config.conf"
    ols_term_changed=0
    ols_hard_term=0
    shopt -s nullglob
    for ols_vh in /usr/local/lsws/conf/vhosts/"${USERNAME}-"*/; do
        [[ -d "${ols_vh}" ]] || continue
        ols_vh_name=$(basename "${ols_vh}")
        if [[ "${DELETE_DATA}" == "true" ]]; then
            rm -rf "${ols_vh}" 2>/dev/null || true
            if [[ -f "${OLS_LISTENER}" ]]; then
                cp -a "${OLS_LISTENER}" "${OLS_LISTENER}.bak-term-$$" 2>/dev/null || true
                python3 - "${OLS_LISTENER}" "${ols_vh_name}" <<'PYEOF' 2>/dev/null || true
import sys, re
conf, vh = sys.argv[1], sys.argv[2]
s = open(conf).read()
s = re.sub(r'(?m)^[ \t]*map[ \t]+' + re.escape(vh) + r'[ \t].*$\n?', '', s)
s = re.sub(r'(?ms)^virtualhost[ \t]+' + re.escape(vh) + r'[ \t]*\{.*?^\}\n?', '', s)
open(conf, 'w').write(s)
PYEOF
            fi
            ols_term_changed=1
            ols_hard_term=1
        else
            # soft: vhost'u koru, marker koy → branded terminated.html serve edilir
            touch "${ols_vh}/.onx-suspended" 2>/dev/null || true
            chmod 644 "${ols_vh}/.onx-suspended" 2>/dev/null || true
            ols_term_changed=1
        fi
    done
    shopt -u nullglob

    # v3.68: hard-terminate'te domain'in map'i SİLİNDİ → artık eşleşmeyen Host.
    # Branded CATCH-ALL vhost'u garanti et (reload=false; restart aşağıda tek sefer).
    # Bu OLMADAN unmapped host ham OLS default sayfasını gösteriyordu (bisaglik bug).
    # Idempotent + best-effort — catch-all zaten kuruluysa no-op.
    if [[ ${ols_hard_term} -eq 1 && -x "${SCRIPT_DIR}/onx-ols-default-page" ]]; then
        echo '{"reload":false}' | "${SCRIPT_DIR}/onx-ols-default-page" >/dev/null 2>&1 \
            || onx_log "WARN: onx-ols-default-page ensure skipped (hard-terminate)"
    fi

    if [[ ${ols_term_changed} -eq 1 && -x /usr/local/lsws/bin/lswsctrl ]]; then
        /usr/local/lsws/bin/lswsctrl restart >/dev/null 2>&1 || true
    fi
    onx_log "OLS vhost terminate handling done (delete_data=${DELETE_DATA} hard=${ols_hard_term})"
fi

# ── Step 5: remove PHP-FPM sockets (all installed php versions) ───────────────
# /run/php-fpm/<user>-*.sock plus per-version /run/php-fpm/php{74,81,82,83,84}.d/
for sock_dir in "${FPM_SOCK_DIR}" /var/run/php-fpm /run/php*-fpm; do
    if [[ -d "${sock_dir}" ]]; then
        rm -f "${sock_dir}/${USERNAME}"-*.sock 2>/dev/null || true
    fi
done

# Pool conf dosyalarını sil (Remi modular layout)
for pool_dir in /etc/opt/remi/php*/php-fpm.d /etc/php-fpm.d; do
    if [[ -d "${pool_dir}" ]]; then
        rm -f "${pool_dir}/${USERNAME}.conf" 2>/dev/null || true
    fi
done
onx_log "php-fpm sockets + pool confs removed"

# ── Step 6: drop MariaDB users (onx_xxx_*) ────────────────────────────────────
# mysql optional — eğer komut yoksa skip + warn
if command -v mysql >/dev/null 2>&1; then
    DB_PREFIX="${USERNAME}_"
    DB_USERS=$(mysql -N -e \
        "SELECT CONCAT('\"', User, '\"@\"', Host, '\"') FROM mysql.user WHERE User LIKE '${DB_PREFIX}%';" \
        2>/dev/null || true)

    if [[ -n "${DB_USERS}" ]]; then
        while IFS= read -r db_user; do
            [[ -z "${db_user}" ]] && continue
            if mysql -e "DROP USER IF EXISTS ${db_user}; FLUSH PRIVILEGES;" 2>/dev/null; then
                onx_log "MariaDB user dropped: ${db_user}"
            else
                warn "MariaDB user drop başarısız: ${db_user}"
            fi
        done <<< "${DB_USERS}"
    fi
else
    warn "mysql komutu yok — DB user temizliği atlandı"
fi

# ── Step 7: cgroup slice cleanup (v86 isolation) ──────────────────────────────
# customer-<user>.slice + drop-in — best effort, slice yoksa devam
SLICE_NAME="customer-${USERNAME}.slice"
if systemctl status "${SLICE_NAME}" &>/dev/null; then
    systemctl stop "${SLICE_NAME}" 2>/dev/null || true
fi
rm -f "/etc/systemd/system/${SLICE_NAME}" 2>/dev/null || true
rm -rf "/etc/systemd/system/${SLICE_NAME}.d" 2>/dev/null || true

# ── Step 8: reload services (best-effort — WARN, not ERROR) ───────────────────
# v87 KEY CHANGE: reload başarısızlığı script'i fail ettirmez.
# `force=true` ise tüm reload'ları atla.
if [[ "${FORCE}" == "true" ]]; then
    onx_log "force=true — service reload skipped intentionally"
    WARNINGS+=("force modu: servis reload'ları atlandı")
else
    # Apache (httpd) — yalnızca aktifse reload
    if systemctl is-active --quiet httpd 2>/dev/null; then
        if ! systemctl reload httpd 2>/dev/null; then
            warn "httpd reload başarısız — servis zaten down veya config error"
        fi
    fi

    # PHP-FPM — her yüklü versiyon için ayrı reload (Remi modular)
    FPM_RELOADED=0
    for unit in php-fpm.service php74-php-fpm.service php81-php-fpm.service \
                php82-php-fpm.service php83-php-fpm.service php84-php-fpm.service; do
        if systemctl is-active --quiet "${unit}" 2>/dev/null; then
            if systemctl reload "${unit}" 2>/dev/null; then
                FPM_RELOADED=$((FPM_RELOADED + 1))
            else
                warn "${unit} reload başarısız"
            fi
        fi
    done
    onx_log "php-fpm reloaded units: ${FPM_RELOADED}"

    # Nginx — optional
    if systemctl is-active --quiet nginx 2>/dev/null; then
        if ! systemctl reload nginx 2>/dev/null; then
            warn "nginx reload başarısız"
        fi
    fi

    # OpenLiteSpeed — optional
    if systemctl is-active --quiet lsws 2>/dev/null; then
        if ! systemctl reload lsws 2>/dev/null; then
            warn "lsws reload başarısız"
        fi
    fi

    # Caddy — optional
    if systemctl is-active --quiet caddy 2>/dev/null; then
        if ! systemctl reload caddy 2>/dev/null; then
            warn "caddy reload başarısız"
        fi
    fi
fi

# systemd daemon-reload (slice cleanup için)
systemctl daemon-reload 2>/dev/null || true

onx_log "services reload phase complete"

# ── Determine outcome ─────────────────────────────────────────────────────────
# Fatal yalnızca userdel başarısızsa (user gerçekten varken).
if [[ ${#ERRORS[@]} -gt 0 ]]; then
    # Fatal error: still emit JSON, but exit 3
    ERR_JSON=$(printf '%s\n' "${ERRORS[@]}" | jq -R . | jq -s .)
    WARN_JSON=$(printf '%s\n' "${WARNINGS[@]}" | jq -R . | jq -s .)
    if [[ ${#WARNINGS[@]} -eq 0 ]]; then WARN_JSON='[]'; fi
    BACKUP_JSON="$( [[ "${BACKUP_PATH}" == "null" ]] && echo "null" || printf '"%s"' "${BACKUP_PATH}")"
    printf '{"username":"%s","removed":false,"backup_path":%s,"user_removed":%s,"home_removed":%s,"warnings":%s,"errors":%s}\n' \
        "${USERNAME}" \
        "${BACKUP_JSON}" \
        "${USER_REMOVED}" \
        "${HOME_REMOVED}" \
        "${WARN_JSON}" \
        "${ERR_JSON}"
    exit 3
fi

# ── Output (success — may have warnings) ──────────────────────────────────────
if [[ ${#WARNINGS[@]} -eq 0 ]]; then
    WARN_JSON='[]'
else
    WARN_JSON=$(printf '%s\n' "${WARNINGS[@]}" | jq -R . | jq -s .)
fi
BACKUP_JSON="$( [[ "${BACKUP_PATH}" == "null" ]] && echo "null" || printf '"%s"' "${BACKUP_PATH}")"

printf '{"username":"%s","removed":true,"backup_path":%s,"user_removed":%s,"home_removed":%s,"warnings":%s,"errors":[]}\n' \
    "${USERNAME}" \
    "${BACKUP_JSON}" \
    "${USER_REMOVED}" \
    "${HOME_REMOVED}" \
    "${WARN_JSON}"

exit 0
