#!/usr/bin/env bash
#
# onx-mail-archive-run — Per-domain mailbox arşivleme (v85.3 MVP).
#
# `retention_days` öncesi gelen INBOX ve Sent mailleri Archive/<YYYY-MM>/
# klasörüne doveadm move ile taşır. Aynı `retention_days` süresinden eski
# Archive ay klasörlerini purge eder. İdempotent: aynı run iki kez çalışsa
# da ikinci run no-op olur (tarih kriteri aynı).
#
# Stdin (JSON):
#   {
#     "domain":         "leafport.com.tr",       // zorunlu
#     "retention_days": 90,                      // 1..36500 (default 90)
#     "archive_path":   "/var/vmail-archive"     // opsiyonel; default
#   }
#
# Stdout (JSON):
#   {
#     "ok": true,
#     "domain": "leafport.com.tr",
#     "retention_days": 90,
#     "before_date": "2026-02-24",
#     "archive_month": "2026-05",
#     "mailboxes_processed": 12,
#     "messages_moved": 1450,
#     "bytes_moved": 312500000,
#     "archive_dir_size": 8200000000,
#     "purged_months": ["2025-01","2025-02"],
#     "purged_bytes": 1200000000,
#     "errors": []
#   }
#
# Exit codes:
#   0 = ok
#   1 = invalid input
#   2 = preflight (doveadm yok, vmail dizini yok, jq yok)
#   3 = execution fail (doveadm move tüm mailbox'lar için fail)

set -u

INPUT=$(cat 2>/dev/null || echo '{}')

# ─── Preflight: jq + doveadm + vmail root ──────────────────────────────
if ! command -v jq >/dev/null 2>&1; then
    echo '{"error":"jq yok (preflight)"}' >&2
    exit 2
fi

DOMAIN=$(echo "$INPUT" | jq -r '.domain // ""' 2>/dev/null)
RETENTION_DAYS=$(echo "$INPUT" | jq -r '.retention_days // 90' 2>/dev/null)
ARCHIVE_PATH=$(echo "$INPUT" | jq -r '.archive_path // "/var/vmail-archive"' 2>/dev/null)

# ─── Input validation (defense-in-depth — Laravel-side de kontrol eder) ─
[[ -z "$DOMAIN" ]] && { echo '{"error":"domain gerekli"}' >&2; exit 1; }
if ! [[ "$DOMAIN" =~ ^[a-zA-Z0-9]([a-zA-Z0-9.-]*[a-zA-Z0-9])?\.[a-zA-Z]{2,63}$ ]]; then
    echo '{"error":"gecersiz domain formati"}' >&2
    exit 1
fi
if [[ ${#DOMAIN} -gt 253 ]]; then
    echo '{"error":"domain >253 char"}' >&2
    exit 1
fi
if ! [[ "$RETENTION_DAYS" =~ ^[0-9]+$ ]] || [[ "$RETENTION_DAYS" -lt 1 ]] || [[ "$RETENTION_DAYS" -gt 36500 ]]; then
    echo '{"error":"retention_days 1..36500 araliginda olmali"}' >&2
    exit 1
fi
# archive_path: sadece /var/vmail-archive[/...] veya /srv/mail-archive[/...] whitelist
# (path injection / Maildir overwrite önle)
if ! [[ "$ARCHIVE_PATH" =~ ^(/var/vmail-archive|/srv/mail-archive)(/[a-zA-Z0-9._-]+)*$ ]]; then
    echo '{"error":"archive_path whitelist disinda — /var/vmail-archive veya /srv/mail-archive altinda olmali"}' >&2
    exit 1
fi

if ! command -v doveadm >/dev/null 2>&1; then
    echo '{"error":"doveadm yok (preflight)"}' >&2
    exit 2
fi

VMAIL_ROOT="/var/vmail"
DOMAIN_DIR="${VMAIL_ROOT}/${DOMAIN}"

if [[ ! -d "$DOMAIN_DIR" ]]; then
    # Domain dizini yoksa skip — 0 mailbox, success
    cat <<EOF
{"ok":true,"domain":"${DOMAIN}","retention_days":${RETENTION_DAYS},"mailboxes_processed":0,"messages_moved":0,"bytes_moved":0,"archive_dir_size":0,"purged_months":[],"purged_bytes":0,"errors":[],"note":"domain_dir_not_found"}
EOF
    exit 0
fi

# ─── Tarih hesabı ──────────────────────────────────────────────────────
# doveadm 'before:YYYY-MM-DD' formatı — bu tarihten ÖNCE gelen mailler taşınır.
BEFORE_DATE=$(date -u -d "${RETENTION_DAYS} days ago" '+%Y-%m-%d' 2>/dev/null)
if [[ -z "$BEFORE_DATE" ]]; then
    echo '{"error":"date hesaplama hatasi"}' >&2
    exit 2
fi
ARCHIVE_MONTH=$(date -u '+%Y-%m')

# Archive root + domain klasörü hazırla
ARCHIVE_DOMAIN_DIR="${ARCHIVE_PATH}/${DOMAIN}"
mkdir -p "$ARCHIVE_DOMAIN_DIR" 2>/dev/null || {
    echo "{\"error\":\"archive dir olusturulamadi: ${ARCHIVE_DOMAIN_DIR}\"}" >&2
    exit 2
}

# ─── Mailbox tarama: Maildir bulunan tüm local user'lar ────────────────
MAILBOXES_PROCESSED=0
MESSAGES_MOVED=0
BYTES_MOVED=0
ERRORS=()

# Kullanıcıları bul (Maildir/cur olan dizinler)
while IFS= read -r maildir_path; do
    [[ -z "$maildir_path" ]] && continue
    user_home=$(dirname "$maildir_path")
    local_part=$(basename "$user_home")
    email="${local_part}@${DOMAIN}"

    [[ -z "$local_part" ]] && continue

    # Boyut snapshot — sonra delta hesaplamak için
    size_before=$(du -sb "$maildir_path" 2>/dev/null | awk '{print $1}')
    size_before="${size_before:-0}"

    # Archive klasörünü oluştur — doveadm move öncesi mailbox tree'sinde olmalı
    archive_mbox="Archive.${ARCHIVE_MONTH}"
    if ! doveadm mailbox create -u "$email" "$archive_mbox" >/dev/null 2>&1; then
        # Zaten varsa hata vermesin — code 68 (EX_NOUSER) veya benzeri normal
        :
    fi

    # INBOX + Sent → Archive/YYYY-MM (before:RETENTION_DAYS gun once)
    move_ok=1
    moved_inbox=0
    moved_sent=0

    # doveadm move -u <user> <destination> mailbox <source> savedbefore <days>
    # `savedbefore` Dovecot SEARCHRES'in OLDER eşdeğeri — INTERNALDATE bazlı
    if doveadm move -u "$email" "$archive_mbox" mailbox INBOX savedbefore "${RETENTION_DAYS}d" >/dev/null 2>&1; then
        moved_inbox=1
    else
        # Boş sonuç dönerse hata vermesin (exit 75 = EX_TEMPFAIL bazen INBOX boş)
        ec=$?
        if [[ $ec -ne 0 && $ec -ne 75 ]]; then
            ERRORS+=("doveadm move INBOX fail (${email}) exit=${ec}")
            move_ok=0
        fi
    fi

    if doveadm move -u "$email" "$archive_mbox" mailbox Sent savedbefore "${RETENTION_DAYS}d" >/dev/null 2>&1; then
        moved_sent=1
    else
        ec=$?
        if [[ $ec -ne 0 && $ec -ne 75 ]]; then
            ERRORS+=("doveadm move Sent fail (${email}) exit=${ec}")
        fi
    fi

    # Boyut delta — taşınan veri miktarı tahmini (doveadm count'tan daha ucuz)
    size_after=$(du -sb "$maildir_path" 2>/dev/null | awk '{print $1}')
    size_after="${size_after:-0}"
    delta=$((size_before - size_after))
    [[ $delta -lt 0 ]] && delta=0
    BYTES_MOVED=$((BYTES_MOVED + delta))

    # Mesaj sayisi — doveadm status messages mailbox Archive.YYYY-MM
    cnt=$(doveadm -f flow mailbox status -u "$email" messages "$archive_mbox" 2>/dev/null \
        | awk -F= '/^messages=/ {print $2}' | head -1)
    cnt="${cnt:-0}"
    # cnt = bu mailbox'taki TÜM Archive.YYYY-MM mesajları, sadece delta'yı bilemeyiz
    # Şimdilik moved estimate = floor(delta / 50KB) — daha doğru için doveadm fetch sayısı
    # gerekir ama bu MVP için yeterli
    if [[ $delta -gt 0 ]]; then
        estimated=$((delta / 51200))
        [[ $estimated -lt 1 ]] && estimated=1
        MESSAGES_MOVED=$((MESSAGES_MOVED + estimated))
    fi

    MAILBOXES_PROCESSED=$((MAILBOXES_PROCESSED + 1))

done < <(find "$DOMAIN_DIR" -mindepth 2 -maxdepth 2 -type d -name "Maildir" 2>/dev/null)

# ─── Retention purge: eski Archive.YYYY-MM klasörlerini sil ────────────
# Her mailbox için Archive.YYYY-MM klasörlerini tara, retention'dan eski
# olanları doveadm mailbox delete ile sil
PURGED_MONTHS=()
PURGED_BYTES=0
PURGE_BEFORE=$(date -u -d "${RETENTION_DAYS} days ago" '+%Y%m' 2>/dev/null)

while IFS= read -r maildir_path; do
    [[ -z "$maildir_path" ]] && continue
    user_home=$(dirname "$maildir_path")
    local_part=$(basename "$user_home")
    email="${local_part}@${DOMAIN}"

    # Archive.YYYY-MM Maildir++ klasörleri: .Archive.YYYY-MM/
    for archive_folder in "$maildir_path"/.Archive.*; do
        [[ ! -d "$archive_folder" ]] && continue
        folder_name=$(basename "$archive_folder")
        # .Archive.2026-05 → 202605
        month_id=$(echo "$folder_name" | sed -n 's/^\.Archive\.\([0-9]\{4\}\)-\([0-9]\{2\}\)$/\1\2/p')
        [[ -z "$month_id" ]] && continue

        if [[ "$month_id" -lt "$PURGE_BEFORE" ]]; then
            # Boyut snapshot
            purge_size=$(du -sb "$archive_folder" 2>/dev/null | awk '{print $1}')
            purge_size="${purge_size:-0}"

            # Mailbox name: Archive.YYYY-MM (dot prefix yok)
            mbox_name=$(echo "$folder_name" | sed 's/^\.//')
            if doveadm mailbox delete -u "$email" "$mbox_name" >/dev/null 2>&1; then
                PURGED_BYTES=$((PURGED_BYTES + purge_size))
                PURGED_MONTHS+=("${mbox_name}@${email}")
            else
                ERRORS+=("doveadm mailbox delete fail: ${mbox_name} (${email})")
            fi
        fi
    done

done < <(find "$DOMAIN_DIR" -mindepth 2 -maxdepth 2 -type d -name "Maildir" 2>/dev/null)

# ─── Archive root dir total size (UI istatistiği) ──────────────────────
ARCHIVE_DIR_SIZE=$(du -sb "$ARCHIVE_DOMAIN_DIR" 2>/dev/null | awk '{print $1}')
ARCHIVE_DIR_SIZE="${ARCHIVE_DIR_SIZE:-0}"

# ─── JSON output ───────────────────────────────────────────────────────
# Errors array JSON-encode
ERRORS_JSON="["
for i in "${!ERRORS[@]}"; do
    e_escaped=$(echo "${ERRORS[$i]}" | sed 's/\\/\\\\/g; s/"/\\"/g')
    [[ $i -gt 0 ]] && ERRORS_JSON+=","
    ERRORS_JSON+="\"${e_escaped}\""
done
ERRORS_JSON+="]"

# Purged months array
PURGED_JSON="["
for i in "${!PURGED_MONTHS[@]}"; do
    [[ $i -gt 0 ]] && PURGED_JSON+=","
    PURGED_JSON+="\"${PURGED_MONTHS[$i]}\""
done
PURGED_JSON+="]"

cat <<EOF
{"ok":true,"domain":"${DOMAIN}","retention_days":${RETENTION_DAYS},"before_date":"${BEFORE_DATE}","archive_month":"${ARCHIVE_MONTH}","mailboxes_processed":${MAILBOXES_PROCESSED},"messages_moved":${MESSAGES_MOVED},"bytes_moved":${BYTES_MOVED},"archive_dir_size":${ARCHIVE_DIR_SIZE},"purged_months":${PURGED_JSON},"purged_bytes":${PURGED_BYTES},"errors":${ERRORS_JSON}}
EOF

# Exit 3 = execution fail sadece TÜM mailbox fail ettiyse
# Burada partial fail OK kabul ediliyor — Laravel-side `errors[]` array'i denetler
if [[ $MAILBOXES_PROCESSED -gt 0 && ${#ERRORS[@]} -ge $MAILBOXES_PROCESSED ]]; then
    exit 3
fi
exit 0
