<#
.SYNOPSIS
  PostgreSQL database backup with retention management and logging.
.DESCRIPTION
  Uses pg_dump (custom format) to back up the CRM database. Tracks the
  backup in the backup_logs table and auto-prunes files older than
  $RetentionDays. Reads DATABASE_URL from the environment or .env.local.
.PARAMETER BackupDir
  Directory where backup files are stored (default: ../backups).
.PARAMETER RetentionDays
  Number of days to retain backups (default: 30).
#>
param(
    [string]$BackupDir = "$PSScriptRoot\..\backups",
    [int]$RetentionDays = 30
)

# Fail fast on any error
$ErrorActionPreference = "Stop"
$startTime = Get-Date

# ── Ensure backup directory exists ────────────────────────────────
if (-not (Test-Path $BackupDir)) {
    New-Item -ItemType Directory -Path $BackupDir -Force | Out-Null
}

# ── Determine database URL from env or .env.local ─────────────────
$dbUrl = $env:DATABASE_URL
if (-not $dbUrl -and (Test-Path "$PSScriptRoot\..\.env.local")) {
    $envContent = Get-Content "$PSScriptRoot\..\.env.local" -Raw
    if ($envContent -match 'DATABASE_URL=(.+)') {
        $dbUrl = $Matches[1].Trim()
    }
}

if (-not $dbUrl) {
    Write-Error "DATABASE_URL not found. Set it as an environment variable or in .env.local"
    exit 1
}

# ── Parse the database URL into connection components ─────────────
$uri = [System.Uri]$dbUrl
$pgUser = $uri.UserInfo.Split(':')[0]
$pgPass = $uri.UserInfo.Split(':')[1]
$pgHost = $uri.Host
$pgPort = $uri.Port
$pgDb = $uri.AbsolutePath.TrimStart('/')

# pg_dump reads password from this env var (avoids interactive prompt)
$env:PGPASSWORD = $pgPass

$timestamp = Get-Date -Format "yyyyMMdd_HHmmss"
$filename = "crm_backup_$timestamp.sql"
$filepath = Join-Path $BackupDir $filename

Write-Output "Starting backup of database '$pgDb' to $filepath"
Write-Output "Database: $pgHost:$pgPort/$pgDb"

try {
    # ── Run pg_dump (custom format) ──────────────────────────────
    # Check PATH first, then fall back to the bundled psql location
    $pgDumpPath = if (Get-Command pg_dump -ErrorAction SilentlyContinue) {
        "pg_dump"
    } else {
        "$env:TEMP\pg\pgsql\bin\pg_dump.exe"
    }

    # Custom format (-Fc) for compressed, restorable output
    $output = & $pgDumpPath -h $pgHost -p $pgPort -U $pgUser -d $pgDb --format=custom --verbose --file $filename 2>&1
    $exitCode = $LASTEXITCODE

    if ($exitCode -ne 0 -or -not (Test-Path $filename)) {
        throw "pg_dump failed with exit code $exitCode"
    }

    # Move to final backup location (dg_dump writes to CWD first)
    Move-Item -Path $filename -Destination $filepath -Force
    $fileInfo = Get-Item $filepath
    $fileSize = $fileInfo.Length

    # ── Verify backup integrity ──────────────────────────────────
    # Run a schema-only dump against the same file to check it's valid custom format
    $verifyOutput = & $pgDumpPath -h $pgHost -p $pgPort -U $pgUser -d $pgDb --format=custom --schema-only --file "$filename.verify" 2>&1
    $verifyExitCode = $LASTEXITCODE
    if (Test-Path "$filename.verify") { Remove-Item "$filename.verify" -Force }

    $verificationStatus = if ($verifyExitCode -eq 0) { "verified" } else { "failed" }

    # Calculate duration
    $endTime = Get-Date
    $durationSeconds = [math]::Round(($endTime - $startTime).TotalSeconds)

    # ── Log backup to database ───────────────────────────────────
    # Records metadata in backup_logs table for audit trail
    $logQuery = @"
INSERT INTO backup_logs (backup_type, status, file_name, file_size_bytes, pg_dump_exit_code, verification_status, started_at, completed_at, duration_seconds, retention_days)
VALUES ('full', 'completed', '$filename', $fileSize, $exitCode, '$verificationStatus', '$($startTime.ToString("yyyy-MM-dd HH:mm:ss"))', '$($endTime.ToString("yyyy-MM-dd HH:mm:ss"))', $durationSeconds, $RetentionDays);
"@

    # Try to log to database (may fail if db is not reachable for logging, that's OK)
    try {
        $env:PGPASSWORD = $pgPass
        & $env:TEMP\pg\pgsql\bin\psql.exe -h $pgHost -p $pgPort -U $pgUser -d $pgDb -c $logQuery 2>&1 | Out-Null
    } catch {
        Write-Warning "Could not log backup to database: $_"
    }

    Write-Output "Backup completed successfully:"
    Write-Output "  File: $filename"
    Write-Output "  Size: $([math]::Round($fileSize / 1MB, 2)) MB"
    Write-Output "  Duration: ${durationSeconds}s"
    Write-Output "  Status: $verificationStatus"

    # ── Cleanup old backups beyond retention period ──────────────
    $cutoff = (Get-Date).AddDays(-$RetentionDays)
    $oldBackups = Get-ChildItem $BackupDir -Filter "crm_backup_*.sql" | Where-Object {
        $_.CreationTime -lt $cutoff
    }
    foreach ($old in $oldBackups) {
        Remove-Item $old.FullName -Force
        Write-Output "Removed old backup: $($old.Name)"
    }

} catch {
    Write-Error "Backup failed: $_"

    # Log failure to database for monitoring
    $endTime = Get-Date
    $durationSeconds = [math]::Round(($endTime - $startTime).TotalSeconds)
    $errorMsg = $_.ToString().Replace("'", "''")
    $failQuery = @"
INSERT INTO backup_logs (backup_type, status, file_name, error_message, pg_dump_exit_code, verification_status, started_at, completed_at, duration_seconds, retention_days)
VALUES ('full', 'failed', '$filename', '$errorMsg', $exitCode, 'failed', '$($startTime.ToString("yyyy-MM-dd HH:mm:ss"))', '$($endTime.ToString("yyyy-MM-dd HH:mm:ss"))', $durationSeconds, $RetentionDays);
"@
    try {
        $env:PGPASSWORD = $pgPass
        & $env:TEMP\pg\pgsql\bin\psql.exe -h $pgHost -p $pgPort -U $pgUser -d $pgDb -c $failQuery 2>&1 | Out-Null
    } catch {}

    exit 1
} finally {
    # Clean up the password environment variable for security
    Remove-Item "Env:PGPASSWORD" -ErrorAction SilentlyContinue
}
