# ============================================================
#  NovaBulut - Yerel Veritabani Google Drive Yedekleme
#  GoogleDriveYedekForm'dan yazilan config.json'u okur, yerel SQL'den
#  BACKUP DATABASE alir, zip'ler, rclone ile Drive'a yukler, eski
#  yedekleri temizler. Windows Gorev Zamanlayici tarafindan tetiklenir
#  (NovaBulut-Yedekleme-<FirmaKodu>) - "Simdi Yedekle" ile de elle.
#
#  Kullanim: powershell -NoProfile -ExecutionPolicy Bypass -File db-yedekle.ps1 -KonfigDosya <yol>
# ============================================================
param(
    [Parameter(Mandatory = $true)]
    [string]$KonfigDosya
)

$ErrorActionPreference = 'Stop'

function Yaz([string]$Mesaj) {
    $satir = "{0}  {1}" -f (Get-Date -Format 'HH:mm:ss'), $Mesaj
    if ($script:Log) { Add-Content -Path $script:Log -Value $satir -Encoding UTF8 }
    Write-Output $satir
}

function DurumYaz([bool]$Basarili, [string]$Mesaj) {
    $durum = [ordered]@{
        Zaman     = (Get-Date -Format 'yyyy-MM-ddTHH:mm:ss')
        Basarili  = $Basarili
        Mesaj     = $Mesaj
    }
    try { $durum | ConvertTo-Json | Set-Content -Path $script:DurumDosya -Encoding UTF8 } catch { }
}

function Bitir([int]$Kod, [bool]$Basarili, [string]$Mesaj) {
    DurumYaz $Basarili $Mesaj
    Yaz $Mesaj
    exit $Kod
}

# ---------------- KONFIG OKU ----------------
if (-not (Test-Path $KonfigDosya)) {
    Write-Error "Konfig dosyasi bulunamadi: $KonfigDosya"
    exit 1
}
$cfg = Get-Content $KonfigDosya -Raw | ConvertFrom-Json

$KokDizin    = Split-Path $KonfigDosya -Parent
$script:Log        = Join-Path $KokDizin ("log\yedek_{0}.log" -f (Get-Date -Format 'yyyy-MM-dd_HHmm'))
$script:DurumDosya = Join-Path $KokDizin 'durum.json'
$Staging     = Join-Path $KokDizin 'staging'
$RcloneConf  = Join-Path $KokDizin 'rclone.conf'

New-Item -ItemType Directory -Force (Join-Path $KokDizin 'log'), $Staging | Out-Null

Yaz "===== Yedekleme basladi (firma: $($cfg.FirmaKodu)) ====="

if (-not (Test-Path $cfg.RclonePath)) {
    Bitir 1 $false "HATA: rclone.exe bulunamadi: $($cfg.RclonePath)"
}
if (-not (Test-Path $RcloneConf)) {
    Bitir 1 $false "HATA: rclone.conf yok - once NovaBulut icinden Google Drive'a baglanti yapilmali."
}

# ---------------- BAGLANTI COZ (DPAPI, KorumaliVeri.Koru ile AYNI: CurrentUser, entropy yok) ----------------
try {
    Add-Type -AssemblyName System.Security
    $sifreliBytes = [Convert]::FromBase64String($cfg.BaglantiDizeSifreli)
    $duzBytes = [System.Security.Cryptography.ProtectedData]::Unprotect(
        $sifreliBytes, $null, [System.Security.Cryptography.DataProtectionScope]::CurrentUser)
    $baglantiDize = [System.Text.Encoding]::UTF8.GetString($duzBytes)
}
catch {
    Bitir 1 $false ("HATA: Baglanti bilgisi cozulemedi (DPAPI - farkli Windows kullanicisi/makine olabilir): {0}" -f $_.Exception.Message)
}

# Microsoft.Data.SqlClient (NovaBulut.exe) baglanti dizesini "Trust Server Certificate=True"
# (bosluklu, SSMS-tarzi kanonik anahtar) olarak uretiyor; Windows PowerShell 5.1'in GAC'daki
# ESKI System.Data.SqlClient'i bu sozdizimini TANIMIYOR ("Anahtar sozcuk desteklenmiyor" hatasi -
# saha 2026-08-12). Bosluksuz esdegerine normalize edilir - davranis (self-signed sertifikaya
# guven) AYNI kalir, yalniz eski parser'in bildigi yazima cevrilir.
$baglantiDize = $baglantiDize -replace '(?i)Trust\s+Server\s+Certificate\s*=', 'TrustServerCertificate='

$builder = New-Object System.Data.SqlClient.SqlConnectionStringBuilder($baglantiDize)
$DbAdi = $builder['Initial Catalog']
if ([string]::IsNullOrWhiteSpace($DbAdi)) {
    Bitir 1 $false "HATA: Baglanti dizesinde veritabani adi (Initial Catalog) bulunamadi."
}

# Onceki calismadan yarim kalan zip'leri degil, staging'deki YUKLENEMEMIS zip'leri koru
# (bir sonraki rclone move'a onlar da dahil olur - basarisiz yuklemenin otomatik tekrar denemesi).
Remove-Item (Join-Path $Staging '*.bak') -Force -ErrorAction SilentlyContinue

# ---------------- BACKUP DATABASE (DB'nin KENDI veri dosyasinin bulundugu klasore) ----------------
# SERVERPROPERTY('InstanceDefaultBackupPath') KULLANILMIYOR - saha bulgusu 2026-08-12: bazi
# makinelerde NULL donup "SQL Server varsayilan yedek klasoru okunamadi" hatasi veriyordu (sa/sysadmin
# ile baglaninca bile - yani sebep yetki degil). Bu deger arka planda registry okur (xp_instance_regread,
# SQL 2012+'da eklendi) - eski bir SQL Server/Express surumunde bu SERVERPROPERTY hic yok, ya da o
# kurulumda BackupDirectory registry degeri hic ayarlanmamis olabilir; ikisinde de sysadmin dahi NULL
# alir. sys.database_files duz bir katalog view - registry'ye hic dokunmuyor, surumden/registry
# durumundan bagimsiz calisir; DB'nin .mdf'inin bulundugu klasor zaten SQL Server servis hesabinin
# yazma izni oldugu bir klasor (kendi veri dosyasini orada tutuyor).
$conn = New-Object System.Data.SqlClient.SqlConnection($baglantiDize)
try {
    $conn.Open()

    $cmdYol = $conn.CreateCommand()
    $cmdYol.CommandText = "SELECT TOP 1 physical_name FROM sys.database_files WHERE type = 0"
    $mdfYolu = $cmdYol.ExecuteScalar()
    if ([string]::IsNullOrWhiteSpace($mdfYolu)) {
        Bitir 1 $false "HATA: Veritabani veri dosyasinin yolu okunamadi."
    }
    $sqlYedekKlasoru = Split-Path $mdfYolu -Parent

    $damga = Get-Date -Format 'yyyy-MM-dd_HHmm'
    $bakAdi = "{0}_{1}.bak" -f $DbAdi, $damga
    $bakYolSql = Join-Path $sqlYedekKlasoru $bakAdi   # SQL Server SURECI bu yola yazar

    Yaz "BACKUP DATABASE [$DbAdi] -> $bakYolSql"
    $cmdYedek = $conn.CreateCommand()
    $cmdYedek.CommandTimeout = 0   # buyuk DB'lerde suresiz bekle
    $cmdYedek.CommandText = "BACKUP DATABASE [$DbAdi] TO DISK = N'$bakYolSql' WITH INIT"
    $cmdYedek.ExecuteNonQuery() | Out-Null
}
catch {
    Bitir 1 $false ("HATA: BACKUP DATABASE basarisiz: {0}" -f $_.Exception.Message)
}
finally {
    $conn.Close()
}

# ---------------- .bak'i KENDI STAGING'IMIZE TASI + ZIP'LE ----------------
# SQL Server servis hesabinin kendi yedek klasorunu doldurmamasi icin dosya
# BURAYA tasinir (Move-Item - kopyalayip silmek yerine); zip harici bir arac
# gerektirmeden Compress-Archive ile (SQL Express COMPRESSION desteklemez).
try {
    Move-Item -Path $bakYolSql -Destination $Staging -Force
    $bakYerel = Join-Path $Staging $bakAdi
    $zipYerel = Join-Path $Staging ($bakAdi -replace '\.bak$', '.zip')
    Compress-Archive -Path $bakYerel -DestinationPath $zipYerel -Force
    Remove-Item $bakYerel -Force
    $mb = (Get-Item $zipYerel).Length / 1MB
    Yaz ("Zip tamam: {0} ({1:N1} MB)" -f (Split-Path $zipYerel -Leaf), $mb)
}
catch {
    Bitir 1 $false ("HATA: .bak tasinamadi/zip'lenemedi: {0}" -f $_.Exception.Message)
}

# ---------------- DRIVE'A YUKLE ----------------
$zipler = @(Get-ChildItem (Join-Path $Staging '*.zip') -ErrorAction SilentlyContinue)
$toplamMB = ($zipler | Measure-Object Length -Sum).Sum / 1MB
$tarih = Get-Date -Format 'yyyy-MM-dd'
Yaz ("Drive'a yukleniyor: {0} dosya, {1:N0} MB -> {2}/{3}" -f $zipler.Count, $toplamMB, $cfg.DriveKlasor, $tarih)

& $cfg.RclonePath move $Staging ("{0}/{1}" -f $cfg.DriveKlasor, $tarih) `
    --config $RcloneConf --include '*.zip' --drive-chunk-size 64M --transfers 2 `
    --log-file $script:Log --log-level NOTICE
if ($LASTEXITCODE -ne 0) {
    Bitir 1 $false "HATA: Drive'a yukleme basarisiz (rclone kodu: $LASTEXITCODE). Zip staging'de birakildi, bir sonraki calismada tekrar denenecek."
}

# ---------------- ESKI YEDEKLERIN TEMIZLIGI ----------------
$saklamaGun = [int]$cfg.SaklamaGun
if ($saklamaGun -gt 0) {
    & $cfg.RclonePath delete $cfg.DriveKlasor --min-age ("{0}d" -f $saklamaGun) --config $RcloneConf --log-file $script:Log --log-level NOTICE
    & $cfg.RclonePath rmdirs $cfg.DriveKlasor --leave-root --config $RcloneConf --log-file $script:Log --log-level NOTICE
    Yaz "Drive'da $saklamaGun gunden eski yedekler temizlendi."
}

# 60 gunden eski yerel log dosyalarini sil
Get-ChildItem (Join-Path $KokDizin 'log') -Filter 'yedek_*.log' -ErrorAction SilentlyContinue |
    Where-Object { $_.LastWriteTime -lt (Get-Date).AddDays(-60) } |
    Remove-Item -Force -ErrorAction SilentlyContinue

Bitir 0 $true ("Basarili: {0} ({1:N1} MB) Drive'a yuklendi ({2}/{3})." -f $bakAdi, $toplamMB, $cfg.DriveKlasor, $tarih)
