#!/usr/bin/env bash
#
# Production deploy — atomik, geri alinabilir, bakim modunu kendisi yonetir.
#
# ⛔ NEDEN VAR (uctan uca denetim §1.2): deploy script'i, geri alma yolu, bakim
# modu akisi ve yedekleme yapilandirmasi YOKTU. `deployment/scripts/` altinda
# yalniz lokal docker ve CI yardimcilari vardi.
#
# ── Nasil calisir ──────────────────────────────────────────────────────────
#
# Release dizini deseni. Her deploy YENI bir dizine cikar, `current` sembolik
# baglantisi en son basarili surume bakar:
#
#   /var/www/html/3e-egitim/
#     releases/20260803-201500/     <- bu deploy
#     releases/20260803-183000/     <- bir onceki (geri donus hedefi)
#     shared/.env                   <- surumler arasi paylasilan
#     shared/storage/               <- yuklenen evrak, log
#     current -> releases/20260803-201500
#
# ⛔ Sembolik baglanti TEK atomik adimda degisir (`ln -sfn` + `mv`). Yarim
# gorunen bir surum yoktur: ya eski ya yeni.
#
# ⚠ `storage` ve `.env` PAYLASILIR, kopyalanmaz. Kopyalansaydi bir geri donus
# yuklenen evraki da geri alirdi — ki o evrak surumun degil musterinin.
#
# ── Kullanim ───────────────────────────────────────────────────────────────
#
#   deployment/scripts/deploy.sh                 # HEAD'i yayimla
#   deployment/scripts/deploy.sh v1.4.2          # bir etiketi yayimla
#   deployment/scripts/deploy.sh --rollback      # bir onceki surume don
#   deployment/scripts/deploy.sh --dry-run       # ne yapacagini yaz, yapma
#
set -euo pipefail

# ⛔ `/var/www/html/<proje>`, cunku makinelerin duzeni bu (Sets, 09.08.2026).
# puanbenim ve logfiks orada yasiyor ve nginx kokleri oraya bakiyor; kendi
# projesine ayri bir yer secmek, sunucuda bir sey arayan herkesin iki yere
# bakmasi demek. Surum dizini deseni degismiyor, yalnizca nerede durdugu.
APP_ROOT="${APP_ROOT:-/var/www/html/3e-egitim}"
REPO="${REPO:-git@github.com:Sets/3e-egitim.git}"
RELEASES="${APP_ROOT}/releases"
SHARED="${APP_ROOT}/shared"
CURRENT="${APP_ROOT}/current"
KEEP="${KEEP:-5}"

# ⛔ PHP SURUMU ADIYLA CAGRILIR, `php` ile degil (09.08.2026, 10.31.1.210).
#
# O makinede `/usr/bin/php` 8.3'tur — baska projeler oradadir ve oyle kalmasi
# gerekir. Ciplak `php` yazan bir deploy, `composer install` adiminda 8.3 icin
# bagimlilik cozer ve hata "platform requirements" diye cikar; ya da daha kotusu
# cozer, kurar ve uygulama ilk istekte patlar. Surum, ortamin varsayilanina
# birakilamayacak kadar belirleyici.
#
# ⚠ `NPM_BIN` ayni sebeple: 1.210'da node nvm altinda ve login olmayan bir
# kabugun PATH'inde yok.
#
# ⛔ SURUM TAHMIN EDILMEZ, BILDIRILIR (Sets, 11.08.2026: "statik olarak 8.5
# girmeliyiz"). Burada eskiden "kurulu olanlarin EN YENISI" araniyordu; bu iki
# sebeple yanlisti. Birincisi `preflight.sh` ayni soruya baska turlu cevap
# veriyordu (ciplak `php`) ve bir deploy tam bu ayrisma yuzunden durdu.
# Ikincisi ve kalicisi: makineye php8.6 kurulmasi, kimse istemeden ve kimse
# gormeden projenin yorumlayicisini degistirirdi.
#
# ⚠ Secim de dogrulama da `php-bin.sh` icinde: `.php-version` okunur, o surum
# bulunamazsa deploy baslamadan durur. Composer da ayni yorumlayiciya baglanir —
# bir phar olan `/usr/local/bin/composer`in hangi PHP ile kosacagina PATH karar
# veriyordu ve sunucuda PATH'teki php 8.3'tur.
# shellcheck source=deployment/scripts/php-bin.sh
. "$(dirname "${BASH_SOURCE[0]}")/php-bin.sh"

NPM_BIN="${NPM_BIN:-npm}"

# ⚠ Paylasilan yollar tek listede: bir tanesi unutulursa deploy sessizce bos
# bir storage ile acilir ve yuklenen evrak "kaybolur" gorunur.
SHARED_PATHS=(".env" "storage")

DRY_RUN=0
ROLLBACK=0
REF="${1:-HEAD}"

for arg in "$@"; do
    case "$arg" in
        --dry-run)  DRY_RUN=1; REF="${REF/--dry-run/HEAD}" ;;
        --rollback) ROLLBACK=1 ;;
    esac
done

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

run() {
    if [[ $DRY_RUN -eq 1 ]]; then
        printf '   \033[2m%s\033[0m\n' "$*"
    else
        "$@"
    fi
}

# ── nginx (EGT-259, Sets 21.10.2026) ───────────────────────────────────────
#
# ⛔ Sitenin nginx ayari release'in icindedir (`sites-enabled/3eegitim.conf` ->
# `current/deployment/nginx/...`) ve deploy onu YUKLEMIYORDU: EGT-257'nin
# `/_recordings/` konumu canliya cikti, nginx eski ayarla calismaya devam etti
# ve elle yuklendi. Ayar degisen her deploy ayni tuzaga duserdi.
#
# ⚠ Once `nginx -t`. Sunucu PAYLASIMLI: bozuk bir ayarla yapilan reload diger
# sitelere dokunmaz (nginx eski ayari tutar), ama hatayi da gostermez — test
# gecmezse deploy durur ve ne oldugu yazilir.
#
# ⚠ Izin yoksa (local prod'da sudoers bu iki satiri tasimayabilir) deploy
# durmaz; yuksek sesle uyarir ve root ile calistirilacak komutu yazar.
reload_nginx() {
    if [[ $DRY_RUN -eq 1 ]]; then
        run sudo nginx -t
        run sudo systemctl reload nginx
        return
    fi

    if ! sudo -n -l /usr/sbin/nginx -t >/dev/null 2>&1; then
        warn "nginx YUKLENMEDI: www-data'nin izni yok. Root ile: nginx -t && systemctl reload nginx"
        return
    fi

    local report
    report="$(sudo -n nginx -t 2>&1)" \
        || die "nginx ayari gecersiz — YUKLENMEDI, eski ayar calisiyor:"$'\n'"${report}"

    sudo -n systemctl reload nginx
    say "nginx ayari test edildi ve yuklendi"
}

# ── Bakim modu ─────────────────────────────────────────────────────────────
#
# ⚠ `--render` ile: Laravel bakim sayfasini ONCEDEN cizer ve dosyaya yazar.
# Onsuz bakim sayfasi da uygulamayi calistirmayi dener — yani migration
# sirasinda bakim sayfasinin kendisi 500 verebilir.
#
# ⛔ `errors.503` — BIZIM sayfamiz, `errors::503` degil (Sets, 11.08.2026:
# "duzgun bir maintenance sayfamiz var mi emin olalim"). Cift iki nokta,
# Laravel'in kendi paketindeki gorunumu adresler: bos beyaz bir sayfada
# Ingilizce "503 Service Unavailable". Bakim penceresine denk gelen bir firma
# yoneticisi icin bu, sistemin bozuldugu anlamina gelir.
#
# ⛔ `--retry` EKRANDA GORUNUR (Sets, 11.08.2026). Laravel'in bakim modunda
# baslangic-bitis penceresi yoktur, ama `--retry` saniye ya da tarih/saat alir
# ve onceden cizilen sayfaya gecirir — sayfa artik onu "Tahmini bitis: 18:40"
# diye yaziyor. Onceki deger 15 saniyeydi: deploy iki-uc dakika suruyor, yani
# hem `Retry-After` yanlis bir sey soyluyordu hem de ekranda gosterilecek kadar
# anlamli degildi.
#
# ⚠ Uzun bir bakim icin saat de verilebilir:
#     MAINTENANCE_RETRY="2026-08-11 21:00" deployment/scripts/deploy.sh
MAINTENANCE_RETRY="${MAINTENANCE_RETRY:-300}"

# ⚠ `--secret` yok. Bakim sirasinda "ama ben girebileyim" istegi, yarim
# migrate edilmis bir veritabanina birini sokmak demektir.
#
# ⚠ ILK KURULUMDA `current` HENUZ YOK. Onsuz bu iki fonksiyon "Could not open
# input file: …/current/artisan" yazip devam ediyordu — zararsiz ama yaniltici:
# bakim moduna girildigi saniliyor, oysa kapatilacak bir site yok.
maintenance_on() {
    [[ -L "$CURRENT" ]] || { warn "Ilk kurulum — kapatilacak calisan surum yok"; return 0; }

    say "Bakim modu aciliyor"
    run "$PHP_BIN" "${CURRENT}/artisan" down --render="errors.503" --retry="${MAINTENANCE_RETRY}" || true
}

maintenance_off() {
    [[ -L "$CURRENT" ]] || return 0

    say "Bakim modu kapaniyor"
    run "$PHP_BIN" "${CURRENT}/artisan" up || true
}

# ⛔ Ne olursa olsun bakim modundan cik. Deploy yarida kalirsa site bakim
# ekraninda ASILI KALMAMALI — kirik bir surum, erisilemeyen bir siteden iyidir
# ve hangisi oldugu operatore gorunur.
trap 'maintenance_off' ERR

# ── Geri alma ──────────────────────────────────────────────────────────────
if [[ $ROLLBACK -eq 1 ]]; then
    [[ -L "$CURRENT" ]] || die "current baglantisi yok — geri donulecek bir sey de yok"

    active="$(readlink "$CURRENT")"
    previous="$(ls -1dt "${RELEASES}"/*/ 2>/dev/null | grep -v "^${active}/\?$" | head -1 || true)"

    [[ -n "$previous" ]] || die "onceki surum bulunamadi"

    warn "GERI DONUS: $(basename "$active") → $(basename "$previous")"
    warn "⛔ Migration'lar GERI ALINMAZ. Bu surumde sema degisikligi varsa once"
    warn "   yedekten donmek gerekir — 'php artisan migrate:rollback' calisan"
    warn "   uretimde neredeyse her zaman yanlis cevaptir (AD-11)."

    maintenance_on
    run ln -sfn "${previous%/}" "${CURRENT}.tmp"
    run mv -Tf "${CURRENT}.tmp" "$CURRENT"
    run "$PHP_BIN" "${CURRENT}/artisan" optimize:clear
    run sudo systemctl reload php8.5-fpm
    run sudo systemctl restart 3eegitim-horizon
    maintenance_off

    # ⚠ Geri donulen surumun ayari: yeni surumle gelen bir konum geri alinir.
    reload_nginx

    say "Geri donuldu: $(basename "$previous")"
    exit 0
fi

# ── Yeni surum ─────────────────────────────────────────────────────────────
# ⛔ ON KOSULLAR, her seyden once (plan chunk9 §9.6).
#
# Buradaki maddelerin ortak ozelligi, atlandiklarinda sistemin SEBEBI
# GORUNMEYECEK sekilde bozulmasidir: eksik bir pgbouncer rolu "veritabanina
# baglanamiyor" diye cikar ve kodda aranir. Kontrol o teshisi onceden yapar.
#
# ⚠ Yedekten ve bakim modundan ONCE: on kosul eksikse hicbir sey degismemis
# olmali. Sets, 07.08.2026: kontrol hem local-prod hem prod dagitimindan once
# kosar — testler once 1.210'da yapilir, orada calistigi gorulunce prod'a
# cikilir, ve ikisi de ayni kapidan gecer.
#
# ⚠ PREFLIGHT_SKIP=1 bilerek atlamak icindir; sessizce atlanmaz, uyari yazar.
if [[ "${PREFLIGHT_SKIP:-0}" == "1" ]]; then
    warn "On kosul kontrolu ATLANDI (PREFLIGHT_SKIP=1)"
else
    # ⚠ SHARED'in yeri BURADA bilinir, preflight'ta tahmin edilmez: userlist
    # parola tasidigi icin depoda yoktur ve release dizininde asla bulunmaz.
    run env USERLIST="${SHARED}/infra/deployment/pgbouncer/userlist.txt" \
        "$(dirname "${BASH_SOURCE[0]}")/preflight.sh" \
        || die "On kosullar saglanmadi — dagitim yapilmadi."
fi

STAMP="$(date +%Y%m%d-%H%M%S)"
RELEASE="${RELEASES}/${STAMP}"

say "Surum ${STAMP} hazirlaniyor (${REF})"

run mkdir -p "$RELEASES" "$SHARED"
run git clone --depth 1 --branch "$REF" "$REPO" "$RELEASE" 2>/dev/null \
    || run git clone "$REPO" "$RELEASE"

# ⚠ Paylasilan yollar SIMLINK: release icindeki karsiliklari silinip
# baglanir. Once silinmezse `ln` dizinin ICINE baglar ve `.env` bulunamaz.
for path in "${SHARED_PATHS[@]}"; do
    run rm -rf "${RELEASE:?}/${path}"
    run ln -sfn "${SHARED}/${path}" "${RELEASE}/${path}"
done

# ⛔ SECILEN PHP DOGRU MU — composer'in KENDISINE sorulur (10.08.2026).
#
# Kisiti burada cozmeye calismak, composer'in isini ikinci kez yazmak olurdu:
# kok `composer.json` "^8.3" der ama gercek taban bir BAGIMLILIKTAN gelir
# (olculdu: >= 8.4.1), ve lock dosyasindaki kisitlari elle ayristirmak
# "^8.2|^8.3|^8.4|^8.5" gibi satirlarda yanlis cevap uretir. Soruyu sahibine
# sormak tek dogru yol.
#
# ⚠ `install`'DAN ONCE. Sonra sorulsaydi yanlis yorumlayiciyla cozulmus bir
# vendor agaci diske inmis olurdu — ve deploy "basarili" deyip uygulama ilk
# istekte patlardi, ki 09.08'de tam olarak bunun bir benzeri yasandi.
say "PHP surumu: $($PHP_BIN -r 'echo PHP_VERSION;' 2>/dev/null || echo bilinmiyor) (${PHP_BIN})"

if [[ $DRY_RUN -eq 0 ]] && ! "${COMPOSER_CMD[@]}" check-platform-reqs --lock --working-dir="$RELEASE" >/dev/null 2>&1; then
    printf '\033[1;31m✗\033[0m %s\n' "Bu makinedeki PHP uygulamanin istedigini karsilamiyor." >&2
    "${COMPOSER_CMD[@]}" check-platform-reqs --lock --working-dir="$RELEASE" 2>&1 | grep -vi success >&2 || true
    printf '\033[1;33m⚠\033[0m %s\n' "Kurulu surumler: $(compgen -c 'php8.' 2>/dev/null | sort -Vu | tr '\n' ' ')" >&2
    printf '\033[1;33m⚠\033[0m %s\n' "Dogrusunu adiyla verin: PHP_BIN=php8.5 COMPOSER_BIN='/usr/bin/php8.5 /usr/local/bin/composer' $0" >&2
    exit 1
fi

say "Bagimliliklar"
run "${COMPOSER_CMD[@]}" install --no-dev --optimize-autoloader --no-interaction --working-dir="$RELEASE"
run "$NPM_BIN" ci --prefix "$RELEASE"
run "$NPM_BIN" run build --prefix "$RELEASE"

# ⛔ SENTRY SURUMU VE KAYNAK HARITALARI (EGT-95).
#
# Vite `sourcemap: 'hidden'` ile derliyor: harita dosyalari uretilir ama
# bundle'larda `sourceMappingURL` yorumu BIRAKILMAZ. Yani tarayici kaynagimizi
# gormez, Sentry gorur — haritalar buradan yuklenip diskten silinir.
#
# ⚠ Surum adi (`STAMP`) sunucudaki `.env`'e yazilir, cunku `config:cache`
# degeri oradan okur ve onbellege gomer. Kaynak haritalari bir SURUME baglanir;
# surum yanlissa hatanin satiri de yanlis olur.
#
# ⚠ Token yoksa adim ATLANIR ve deploy devam eder. Bir kaynak haritasi
# yuklenemedi diye yayin durmaz — yalnizca o surumun JavaScript izleri okunmasi
# zor kalir.
sentry_release() {
    local env_file="${SHARED}/.env"
    local token org project

    [[ -f "$env_file" ]] || { say "Sentry: .env yok, atlaniyor"; return 0; }

    token="$(grep -E '^SENTRY_AUTH_TOKEN=' "$env_file" | cut -d= -f2- | tr -d '"' || true)"
    org="$(grep -E '^SENTRY_ORG=' "$env_file" | cut -d= -f2- | tr -d '"' || true)"
    project="$(grep -E '^SENTRY_PROJECT_JAVASCRIPT=' "$env_file" | cut -d= -f2- | tr -d '"' || true)"

    if [[ -z "$token" || -z "$org" || -z "$project" ]]; then
        say "Sentry: token/org/proje eksik, kaynak haritalari atlaniyor"
        return 0
    fi

    # ⚠ Idempotent: satir varsa degistirilir, yoksa eklenir. `.env` PAYLASILIR,
    # yani bu satir bir sonraki deploya kadar dogru kalmalidir.
    if grep -qE '^SENTRY_RELEASE=' "$env_file"; then
        run sed -i "s|^SENTRY_RELEASE=.*|SENTRY_RELEASE=${STAMP}|" "$env_file"
    else
        run bash -c "printf 'SENTRY_RELEASE=%s\n' '${STAMP}' >> '${env_file}'"
    fi

    local cli=("$RELEASE/node_modules/.bin/sentry-cli")

    if [[ ! -x "${cli[0]}" ]]; then
        say "Sentry: sentry-cli kurulu degil, kaynak haritalari atlaniyor"
        return 0
    fi

    say "Sentry surumu ${STAMP}"

    # ⚠ `|| true`: Sentry'ye ulasilamamasi deploy'u dusurmez.
    (
        export SENTRY_AUTH_TOKEN="$token" SENTRY_ORG="$org" SENTRY_PROJECT="$project"
        run "${cli[@]}" releases new "$STAMP" || true
        run "${cli[@]}" sourcemaps inject "${RELEASE}/public/build" || true
        run "${cli[@]}" sourcemaps upload --release="$STAMP" "${RELEASE}/public/build" || true
        run "${cli[@]}" releases finalize "$STAMP" || true
    )

    # ⛔ Haritalar diskten SILINIR. `hidden` yalnizca yorumu kaldirir; dosyanin
    # kendisi `public/` altinda tahmin edilebilir bir adla durur ve kaynagin
    # tamamini icerir.
    say "Kaynak haritalari siliniyor"
    run find "${RELEASE}/public/build" -name '*.map' -delete
}

sentry_release

# ⛔ FILAMENT VARLIKLARI, ve bu adim `npm run build` ile ayni is DEGILDIR
# (27.07.2026'da olculdu, `docs/infra/ortamlar.md`).
#
# Filament kendi CSS/JS'ini paketin icinde tasir ve `public/` altina bu komutla
# yayar; Vite yalnizca uygulamanin kendi bundle'ini uretir. Ikisi de
# `.gitignore`'da — paket surumuyle degisen uretilmis ciktilar. Adim atlandiginda
# backoffice paneli DUZ HTML olarak acilir: hata yok, log yok, yalnizca bicimsiz
# bir ekran.
say "Filament varliklari"
run "$PHP_BIN" "${RELEASE}/artisan" filament:assets

# ⛔ YEDEK, migration'dan ONCE ve her deployda. Geri donusun tek gercek yolu
# budur: sembolik baglanti kodu geri alir, veriyi almaz.
say "Veritabani yedegi"
run "${RELEASE}/deployment/scripts/backup.sh" "pre-deploy-${STAMP}"

maintenance_on

# ⛔ `--database=pgsql_admin`, ve bu opsiyonel degil (09.08.2026, 1.210).
#
# Ciplak `migrate` VARSAYILAN baglantiyi kullanir: PgBouncer uzerinden
# `3eegitim_app` rolu. Ama uygulama rollerini olusturan sey migration'in
# KENDISIDIR (`0001_01_01_000100_create_application_roles`) — yani temiz bir
# veritabaninda migrate, henuz var olmayan bir rolle baglanmayi deniyor:
#
#     FATAL: password authentication failed for user "3eegitim_app"
#
# Ilk kurulumda cikar ve "parolayi yanlis yazmisiz" gibi okunur; degildir, rol
# yoktur. Sema islerinin dogru yolu zaten dogrudan port ve yonetici rolu — CI ve
# gelistirme tarafi da migration'i boyle kosuyor (`docs/infra/ortamlar.md`).
say "Migration"
run "$PHP_BIN" "${RELEASE}/artisan" migrate --force --no-interaction --database=pgsql_admin

# ⛔ PGBOUNCER YENIDEN BASLATILIR — semayi degistiren her deployda (07.08.2026).
#
# PostgreSQL hazirlanmis sorgu planlarini onbelleklere alir ve PgBouncer bu
# baglantilari surumler arasinda paylastirir. Bir tabloya kolon eklendigi anda
# havuzdaki eski plan gecersiz olur ve sunucu su hatayi verir:
#
#     SQLSTATE[0A000]: cached plan must not change result type
#
# Olculdu: `users` tablosuna tek bir kolon eklendi ve GIRIS 500 donmeye basladi.
# Kod dogruydu, migration dogruydu; havuz eskiyi tutuyordu. Yeniden baslatmak
# saniyeler surer ve baglantiyi tazeler.
#
# ⚠ Migration'in HEMEN ardinda ve bakim modu HALA acikken: aradaki her istek
# eski planla karsilasabilir. Restart kosulsuz, cunku "sema degisti mi" sorusunu
# burada guvenilir sekilde cevaplayamayiz — ve degismediyse bedeli yalnizca
# birkac saniyelik bir baglanti tazelemesidir.
say "Baglanti havuzu tazeleniyor (sema degisikligi icin zorunlu)"
run docker restart 3eegitim_pgbouncer

# ⛔ IZIN SOZLUGU HER DEPLOYDA YAZILIR (28.07.2026, `docs/infra/ortamlar.md`).
#
# `config/authorization.php`'ye izin eklemek yetmez: satirlar veritabanina
# `AuthorizationSeeder` ile gider ve `super-admin` rolu onunla senkronlanir.
# Kosulmazsa Filament o kaynagi GIZLER — ekran vardir, menude yoktur, kimse
# sebebini anlamaz. Seeder kendi docblock'unda idempotent oldugunu soyluyor ve
# tam olarak bu senaryo icin yazilmis.
#
# ⚠ Testler bunu yakalamaz ve yakalayamaz: suite seeder'in DOGRU satirlari
# uretip uretmedigini sorar, bu veritabaninda kosulup kosulmadigini degil.
say "Izin sozlugu"
run "$PHP_BIN" "${RELEASE}/artisan" db:seed --force --no-interaction \
    --class='Database\Seeders\AuthorizationSeeder'

# ⛔ Bildirim metinleri de burada (13.08.2026, EGT-122). Metni tanimli olmayan
# bir bildirim SESSIZCE atlanir — production'da 183 e-posta bu yuzden hic
# gonderilmedi ve fark eden olmadi. Bos bir sablon tablosu notr bir baslangic
# degil, kesfedilene kadar calismayan bir urundur.
#
# ⚠ Var olan bir metne DOKUNMAZ (taslak dahil): birinin duzeltmesini bir
# sonraki deploy'da sessizce geri almak, bu adimi zararli hale getirirdi.
#
# ⚠ Metin yazmak gonderim ACMAZ. Gonderimi acan sey bildirim kuralidir.
say "Bildirim metinleri"
run "$PHP_BIN" "${RELEASE}/artisan" db:seed --force --no-interaction \
    --class='Database\Seeders\NotificationWordingSeeder'

# ⛔ YARDIM MAKALELERI DE BURADA (14.08.2026, EGT-137). Ayni sinifta bir eksik:
# makaleler `docs/yardim/` altinda yazilir ama ekran onlari TABLODAN okur, ve bu
# adim yoktu — dosya deploy edilir, prod'daki Yardim Merkezi eski halini gosterir
# ve kimse fark etmez, cunku ortada hata yoktur; yalnizca yeni makale yoktur.
#
# ⚠ Bir ekran degistiginde makalesi ayni commit'te guncelleniyor (CLAUDE.md);
# o kuralin karsiligi, guncellenen metnin canliya GITMESIDIR.
#
# ⚠ `updateOrCreate`: bu satirlari kimse ekrandan duzenlemiyor, dolayisiyla
# korunacak bir emek yok — dosyada duzeltilip panele ulasmayan bir metin ise
# kimsenin gormedigi bir duzeltmedir.
say "Yardim makaleleri"
run "$PHP_BIN" "${RELEASE}/artisan" db:seed --force --no-interaction \
    --class='Database\Seeders\HelpArticleSeeder'

say "Onbellekler"
run "$PHP_BIN" "${RELEASE}/artisan" config:cache
run "$PHP_BIN" "${RELEASE}/artisan" route:cache
run "$PHP_BIN" "${RELEASE}/artisan" view:cache
run "$PHP_BIN" "${RELEASE}/artisan" event:cache

# ⚠ YAZILABILIR DIZINLERIN SAHIPLIGI — deploy root olarak kosuyor, dolayisiyla
# yukaridaki cache komutlari bootstrap/cache ve storage/framework/views icine
# ROOT sahipli dosyalar birakir. Uygulama (php-fpm/horizon) www-data olarak
# kostugu icin sonradan `config:clear` SESSIZCE basarisiz olur: komut "cleared"
# der ama dosyayi silemez, uygulama eski onbellegi okumaya devam eder.
# 2026-08-12'de tam bu yasandi — .env'de port degistirildi, config:clear
# calistirildi, uygulama yine de ESKI portlari kullandi; Horizon acilamadi ve
# Reverb yanlis porta baglandi. Teshis yarim saat aldi.
#
# ⛔ 20.08.2026: iki dizin YETMIYOR. Deploy root olarak kosuldugunda release'in
# TAMAMI root sahipli kaliyordu — 20 Agustos'ta prod'da 28 bin boyle dosya
# sayildi (dort eski release ve `shared/infra`). Iki sonucu vardi: `--rollback`
# ile geri donulen bir surumde uygulama kendi bootstrap/cache'ine yazamaz
# (yukaridaki 12 Agustos kazasinin ta kendisi, bu sefer geri donuste), ve
# `shared/infra` deposunda www-data `git fetch` edemedigi icin deploy oncesi
# guncelleme root ile yapilmak zorunda kalir — yani her deploy bir sonrakinin
# sahipligini daha da bozar.
#
# ⚠ Sahiplik, izin degil: `chmod` yok. Script'lerin calistirma biti ve
# `.env`/`userlist.txt`in dar izinleri oldugu gibi kalir.
if [[ $DRY_RUN -eq 0 ]] && id -u www-data >/dev/null 2>&1; then
    say "Dosya sahipligi (www-data)"
    chown -R www-data:www-data "$RELEASE" 2>/dev/null || true
    [[ -d "${SHARED}/storage" ]] && chown -R www-data:www-data "${SHARED}/storage" 2>/dev/null || true

    # ⛔ VE SONUCU KONTROL EDILIR (OPS-25, 22.08.2026). `chown` yukarida
    # `2>/dev/null || true` ile kosuyor — dogru, cunku bir dosyanin sahipligini
    # degistirememek deploy'u durdurmamali; ama sessiz kalmasi baska bir sey.
    # 11.08'de birakilan root sahipli `bootstrap/cache` tam olarak boyle fark
    # edilmedi: uygulama ilk istekte "Permission denied" atti, hata Sentry'ye
    # dustu ve deploy ciktisinda hicbir iz yoktu.
    #
    # ⚠ Uyarir, durdurmaz. Surum zaten yayimlandi; burada patlamak calisan bir
    # deploy'u yarida birakmak olur. Ama ekranda bir satir kalir ve kalan dosya
    # ADIYLA yazilir — sayi tek basina nereye bakilacagini soylemez.
    strays="$(find "$RELEASE" ! -user www-data -print 2>/dev/null | head -5)"
    if [[ -n "$strays" ]]; then
        warn "Release'de www-data disi sahiplik kaldi — uygulama bu dosyalara yazamayabilir:"
        printf '%s\n' "$strays" | sed 's/^/    /'
    fi
fi

# ⚠ Depolama baglantisi her surumde yeniden kurulur: `public/storage` release
# dizinindedir ve yeni release'te henuz yoktur.
run "$PHP_BIN" "${RELEASE}/artisan" storage:link

say "Surum devraliniyor"
run ln -sfn "$RELEASE" "${CURRENT}.tmp"
run mv -Tf "${CURRENT}.tmp" "$CURRENT"

# ⚠ FPM reload, restart degil: acik istekler tamamlanir. Horizon RESTART ister —
# calisan worker'lar eski kodu bellekte tutar ve `horizon:terminate` onlara
# isini bitirip cikmalarini soyler.
run sudo systemctl reload php8.5-fpm
run "$PHP_BIN" "${CURRENT}/artisan" horizon:terminate

maintenance_off

# ⚠ Bakim modundan SONRA: ayar testi duserse site bakim sayfasinda kalmaz.
reload_nginx

say "Eski surumler temizleniyor (son ${KEEP} tutulur)"
if [[ $DRY_RUN -eq 0 ]]; then
    ls -1dt "${RELEASES}"/*/ | tail -n "+$((KEEP + 1))" | xargs -r rm -rf
fi

say "Yayimlandi: ${STAMP}"
