#!/usr/bin/env bash
#
# Veritabani yedegi — deploy oncesi ve gunluk.
#
# ⛔ NEDEN VAR (uctan uca denetim §1.2): yedekleme yapilandirmasi yoktu. Bir
# deploy'u geri almak kolaydir; bir migration'i geri almak degildir. Kod geri
# donusunun anlamli olmasi icin verinin o ana ait bir kopyasi gerekir.
#
# ── Kullanim ───────────────────────────────────────────────────────────────
#
#   deployment/scripts/backup.sh                 # gunluk yedek
#   deployment/scripts/backup.sh pre-deploy-x    # etiketli yedek
#   deployment/scripts/backup.sh --verify <dosya>  # yedegi geri yuklenebilir mi
#
# ── Neler var ──────────────────────────────────────────────────────────────
#
# ⚠ `--format=custom`: `pg_restore` ile SECMELI geri yukleme yapilabilir ve
# paralel calisir. Duz SQL dokumu tek parcadir ve buyuk bir veritabaninda
# saatler surer.
#
# ⛔ `--no-owner --no-acl`: dokum baska bir kullanici adiyla geri yuklenebilsin.
# Onsuz geri yukleme "role 3eegitim_app does not exist" ile duser — ve bu tam
# olarak yedege en cok ihtiyac duyulan anda, temiz bir makinede olur.
#
# ── Belgeler de yedeklenir ─────────────────────────────────────────────────
#
# ⛔ Ilk fazda dokuman diski YEREL (Sets, 05.08.2026): baytlar sunucunun
# diskinde, `storage/app/private` altinda duruyor. Yalniz veritabanini
# yedeklemek, geri yukledikten sonra var olmayan dosyalari isaret eden satirlar
# birakir — imzali yoklama, sertifika, lisansli dokuman. Kayit durur, belge
# gider.
#
# ⚠ Arsivdeki baytlar ZATEN sifreli (`SecureFileStore` yazmadan once sifreler).
# Yine de 0600: sifreleme anahtari `.env`'de ve ikisi ayni makinede.
#
# ⚠ Disk `local` DEGILSE arsiv alinmaz. R2/S3'e gecildigi gun baytlar orada
# olur; bos bir tar uretmek "yedegim var" diye okunur ve yanlistir.
#
set -euo pipefail

ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)"
[[ -f "${ROOT}/.env" ]] && set -a && source "${ROOT}/.env" && set +a

DEST="${BACKUP_DIR:-/var/backups/3eegitim}"
KEEP_DAYS="${BACKUP_KEEP_DAYS:-30}"
LABEL="${1:-daily}"

say() { printf '\033[1;36m→\033[0m %s\n' "$1"; }
die() { printf '\033[1;31m✗\033[0m %s\n' "$1" >&2; exit 1; }

# ── Dogrulama modu ─────────────────────────────────────────────────────────
#
# ⛔ Denenmemis yedek, yedek degildir. Bu mod dokumu GECICI bir veritabanina
# geri yukler ve satir sayar; cron'dan haftada bir cagrilmasi beklenir.
if [[ "$LABEL" == "--verify" ]]; then
    file="${2:?dogrulanacak dosya}"
    probe="3eegitim_verify_$$"

    say "Gecici veritabani: ${probe}"
    PGPASSWORD="${DB_ADMIN_PASSWORD}" createdb -h 127.0.0.1 -p "${DB_DIRECT_PORT:-20200}" \
        -U "${DB_ADMIN_USERNAME:-postgres}" "$probe"

    trap 'PGPASSWORD="${DB_ADMIN_PASSWORD}" dropdb -h 127.0.0.1 -p "${DB_DIRECT_PORT:-20200}" -U "${DB_ADMIN_USERNAME:-postgres}" "$probe" 2>/dev/null || true' EXIT

    # ⛔ `|| true`: `pg_restore` cikis kodunu ATLANAN hatalar icin de dondurur ve
    # TimescaleDB'li bir dokumde bunlar kacinilmaz — hypertable'lar `ALTER TABLE
    # ONLY` kabul etmez. `set -e` altinda betik tam burada oluyordu, yani
    # "denenmemis yedek, yedek degildir" adimi HIC tamamlanmiyordu (05.08.2026).
    #
    # ⚠ Kararı cikis kodu degil, ASAGIDAKI sayim verir: geri yuklenen sey
    # calisan bir sema mi. Bir uyari yigini gecerli bir yedegi gecersiz kilmaz;
    # bos bir veritabani kilar.
    PGPASSWORD="${DB_ADMIN_PASSWORD}" pg_restore -h 127.0.0.1 -p "${DB_DIRECT_PORT:-20200}" \
        -U "${DB_ADMIN_USERNAME:-postgres}" -d "$probe" --no-owner --no-acl -j 4 "$file" \
        2>/dev/null || true

    tables="$(PGPASSWORD="${DB_ADMIN_PASSWORD}" psql -h 127.0.0.1 -p "${DB_DIRECT_PORT:-20200}" \
        -U "${DB_ADMIN_USERNAME:-postgres}" -d "$probe" -tAc \
        "select count(*) from information_schema.tables where table_schema = 'public'")"

    [[ "$tables" -gt 20 ]] || die "yedek sifirdan farkli degil: yalniz ${tables} tablo"

    # ⛔ Tablo sayisi semanin geldigini soyler, VERININ geldigini soylemez. Bos
    # bir sema da "basariyla" geri yuklenir; bir yedegin ise yaramasi icin
    # icinde kayit olmasi gerekir.
    users="$(PGPASSWORD="${DB_ADMIN_PASSWORD}" psql -h 127.0.0.1 -p "${DB_DIRECT_PORT:-20200}" \
        -U "${DB_ADMIN_USERNAME:-postgres}" -d "$probe" -tAc "select count(*) from users")"

    [[ "$users" -gt 0 ]] || die "yedek bos: sema geldi, kayit gelmedi"

    say "Yedek geri yuklenebilir: ${tables} tablo, ${users} kullanici"

    # ⛔ Belge arsivi de denenir. Okunamayan bir tar, ihtiyac aninda okunamayan
    # bir tardir; `-t` acmadan icindekileri listeler.
    documents="${file%.dump}-belgeler.tar.gz"

    if [[ -f "$documents" ]]; then
        count="$(tar -tzf "$documents" | wc -l | tr -d ' ')"
        [[ "$count" -gt 0 ]] || die "belge arsivi bos: ${documents}"
        say "Belge arsivi okunabilir: ${count} girdi"
    else
        printf '\033[1;33m⚠\033[0m Belge arsivi yok: %s\n' "$documents"
    fi

    exit 0
fi

# ── Yedek al ───────────────────────────────────────────────────────────────
mkdir -p "$DEST"

# ⛔ SUNUCUYLA AYNI SURUMDEN pg_dump (09.08.2026, ilk production kurulumu).
#
# Debian'in `pg_wrapper`'i `pg_dump`i yerel cluster surumune gore secer ve
# production makinesinde kurulu olan 17'ydi; veritabani ise 18.4. Sonuc:
#
#     pg_dump: error: aborting because of server version mismatch
#
# Deploy tam yedek adiminda durdu — dogru yerde durdu, ama sebep koda degil
# makineye aitti. `pg_dump` her zaman sunucuya esit ya da ondan YENI olmalidir.
#
# ⚠ Once sunucuya sorulur, sonra o surumun ikilisi aranir. Bulunamazsa PATH'teki
# kullanilir ve uyumsuzsa zaten yuksek sesle duser — sessizce yarim bir dokum
# almaktansa hic almamak dogrudur.
pg_dump_binary() {
    local server major candidate

    server="$(PGPASSWORD="${DB_ADMIN_PASSWORD}" psql -h 127.0.0.1 -p "${DB_DIRECT_PORT:-20200}" \
        -U "${DB_ADMIN_USERNAME:-postgres}" -d "${DB_DATABASE:-3eegitim}" \
        -tAc 'show server_version' 2>/dev/null || true)"

    major="${server%%.*}"
    candidate="/usr/lib/postgresql/${major}/bin/pg_dump"

    if [[ -n "$major" && -x "$candidate" ]]; then
        printf '%s' "$candidate"
        return
    fi

    printf 'pg_dump'
}

stamp="$(date +%Y%m%d-%H%M%S)"
file="${DEST}/${LABEL}-${stamp}.dump"

say "Yedek aliniyor → ${file}"

PGPASSWORD="${DB_ADMIN_PASSWORD:?.env icinde tanimli olmali}" "$(pg_dump_binary)" \
    -h 127.0.0.1 \
    -p "${DB_DIRECT_PORT:-20200}" \
    -U "${DB_ADMIN_USERNAME:-postgres}" \
    -d "${DB_DATABASE:-3eegitim}" \
    --format=custom \
    --compress=9 \
    --no-owner \
    --no-acl \
    -f "$file"

# ⛔ 0600: dokum HER SEYI tasir — sifreli kolonlar dahil. Sifreleme anahtari
# `.env`'de duruyor ve ikisi ayni makinede; dosya izni son savunma.
chmod 600 "$file"

size="$(du -h "$file" | cut -f1)"
say "Alindi (${size})"

# ── Belgeler ───────────────────────────────────────────────────────────────
#
# ⚠ Paylasilan dizinden okunur, release'ten degil: `deploy.sh` `storage`'i
# surumler arasi paylasir ve yuklenen evrak orada durur.
DOCUMENTS_DIR="${DOCUMENT_ROOT:-${ROOT}/storage/app/private}"

if [[ "${DOCUMENT_DISK:-local}" == "local" ]]; then
    if [[ -d "$DOCUMENTS_DIR" ]]; then
        archive="${file%.dump}-belgeler.tar.gz"

        say "Belgeler yedekleniyor → ${archive}"

        # ⚠ `-C` ile dizinin ICINDEN arsivlenir: mutlak yol tasiyan bir tar,
        # geri yuklerken baska bir makinenin dizin agacini yeniden uretir.
        tar -czf "$archive" -C "$DOCUMENTS_DIR" .
        chmod 600 "$archive"

        say "Belgeler alindi ($(du -h "$archive" | cut -f1))"
    else
        printf '\033[1;33m⚠\033[0m Belge dizini yok: %s\n' "$DOCUMENTS_DIR"
    fi
else
    say "Dokuman diski '${DOCUMENT_DISK}' — baytlar bu makinede degil, arsiv alinmadi."
fi

# ⚠ Once yeni yedek alinir, SONRA eskiler silinir. Ters sirada, alma adimi
# duserse elde hicbir sey kalmayabilir.
say "Eski yedekler temizleniyor (${KEEP_DAYS} gunden eski)"
find "$DEST" -name '*.dump' -type f -mtime "+${KEEP_DAYS}" -delete
find "$DEST" -name '*-belgeler.tar.gz' -type f -mtime "+${KEEP_DAYS}" -delete

# ⚠ Uzak kopya. Ayni diskteki yedek, disk arizasina karsi yedek degildir.
if [[ -n "${BACKUP_REMOTE:-}" ]]; then
    say "Uzak kopya → ${BACKUP_REMOTE}"
    rclone copy "$file" "${BACKUP_REMOTE}" --config "${RCLONE_CONFIG:-/etc/rclone.conf}"

    # ⚠ Belgeler de gider. Dokumu uzaga tasiyip belgeleri makinede birakmak,
    # disk arizasinda kayitlari kurtarip dosyalari kaybetmektir.
    [[ -f "${file%.dump}-belgeler.tar.gz" ]] \
        && rclone copy "${file%.dump}-belgeler.tar.gz" "${BACKUP_REMOTE}" --config "${RCLONE_CONFIG:-/etc/rclone.conf}"
else
    printf '\033[1;33m⚠\033[0m BACKUP_REMOTE tanimli degil — yedek yalniz bu makinede.\n'
fi
