[CmdletBinding()]
param(
    [ValidateSet("full", "binlog", "all")]
    [string]$Mode = "all",
    [string]$MysqlBin = "C:\xampp2\mysql\bin",
    [string]$BinlogSource = "C:\xampp2\mysql\binlogs",
    [string]$BackupRoot = "C:\FlutterProjetos\backups\mysql",
    [string]$Database = "bulo",
    [string]$HostName = "127.0.0.1",
    [string]$User = "root",
    [string]$Password = $env:BULO_DB_PASSWORD,
    [string]$OffsiteRoot = $env:BULO_BACKUP_OFFSITE,
    [int]$KeepFullDays = 30,
    [int]$KeepBinlogDays = 45
)

$ErrorActionPreference = "Stop"

function New-Dir([string]$Path) {
    if (-not (Test-Path -LiteralPath $Path)) {
        New-Item -ItemType Directory -Path $Path -Force | Out-Null
    }
}

function Write-BackupLog([string]$Message) {
    $logDir = Join-Path $BackupRoot "logs"
    New-Dir $logDir
    $line = "{0} {1}" -f (Get-Date -Format "yyyy-MM-dd HH:mm:ss"), $Message
    Add-Content -LiteralPath (Join-Path $logDir "backup.log") -Value $line -Encoding UTF8
    Write-Output $line
}

function Mysql-AuthArgs {
    $args = @("-h", $HostName, "-u", $User)
    if ($Password -ne $null -and $Password -ne "") {
        $args += "--password=$Password"
    }
    return $args
}

function Test-MysqlOnline([string]$MysqlAdmin) {
    $args = Mysql-AuthArgs
    $args += "ping"
    $previousErrorActionPreference = $ErrorActionPreference
    $ErrorActionPreference = "Continue"
    try {
        & $MysqlAdmin @args > $null 2>$null
        return $LASTEXITCODE -eq 0
    } catch {
        return $false
    } finally {
        $ErrorActionPreference = $previousErrorActionPreference
    }
}

function Copy-ToOffsite([string]$Path) {
    if ($OffsiteRoot -eq $null -or $OffsiteRoot.Trim() -eq "") {
        return
    }
    New-Dir $OffsiteRoot
    $relative = $Path.Substring($BackupRoot.Length).TrimStart("\", "/")
    $dest = Join-Path $OffsiteRoot $relative
    New-Dir (Split-Path -Parent $dest)
    Copy-Item -LiteralPath $Path -Destination $dest -Force
}

function Remove-OldFiles([string]$Path, [int]$Days) {
    if (-not (Test-Path -LiteralPath $Path)) {
        return
    }
    $limit = (Get-Date).AddDays(-$Days)
    Get-ChildItem -LiteralPath $Path -File -Recurse |
        Where-Object { $_.LastWriteTime -lt $limit } |
        Remove-Item -Force
}

function Backup-Full {
    $dump = Join-Path $MysqlBin "mysqldump.exe"
    if (-not (Test-Path -LiteralPath $dump)) {
        throw "mysqldump.exe nao encontrado em $dump"
    }

    $fullDir = Join-Path $BackupRoot "full"
    New-Dir $fullDir
    $stamp = Get-Date -Format "yyyyMMdd_HHmmss"
    $out = Join-Path $fullDir "${Database}_full_${stamp}.sql"

    $args = Mysql-AuthArgs
    $args += @(
        "--single-transaction",
        "--routines",
        "--triggers",
        "--events",
        "--hex-blob",
        "--default-character-set=utf8mb4",
        "--databases",
        $Database,
        "--result-file=$out"
    )

    Write-BackupLog "Iniciando backup completo em $out"
    & $dump @args
    if ($LASTEXITCODE -ne 0) {
        throw "mysqldump falhou com codigo $LASTEXITCODE"
    }

    $hashFile = "$out.sha256"
    $hash = Get-FileHash -LiteralPath $out -Algorithm SHA256
    Set-Content -LiteralPath $hashFile -Value "$($hash.Hash)  $([IO.Path]::GetFileName($out))" -Encoding ASCII
    Copy-ToOffsite $out
    Copy-ToOffsite $hashFile
    Remove-OldFiles $fullDir $KeepFullDays
    Write-BackupLog "Backup completo finalizado: $out ($((Get-Item -LiteralPath $out).Length) bytes)"
}

function Backup-Binlog {
    $mysqladmin = Join-Path $MysqlBin "mysqladmin.exe"
    if (-not (Test-Path -LiteralPath $mysqladmin)) {
        throw "mysqladmin.exe nao encontrado em $mysqladmin"
    }
    if (-not (Test-Path -LiteralPath $BinlogSource)) {
        throw "Pasta de binlogs nao encontrada: $BinlogSource"
    }

    if (-not (Test-MysqlOnline $mysqladmin)) {
        return
    }

    $binlogDir = Join-Path $BackupRoot "binlogs"
    New-Dir $binlogDir

    $args = Mysql-AuthArgs
    $args += "flush-logs"
    Write-BackupLog "Rotacionando binlog para backup incremental"
    & $mysqladmin @args
    if ($LASTEXITCODE -ne 0) {
        throw "mysqladmin flush-logs falhou com codigo $LASTEXITCODE"
    }

    Get-ChildItem -LiteralPath $BinlogSource -File -Filter "mysql-bin.*" |
        Where-Object { $_.Name -ne "mysql-bin.index" } |
        ForEach-Object {
            $dest = Join-Path $binlogDir $_.Name
            if (-not (Test-Path -LiteralPath $dest) -or (Get-Item -LiteralPath $dest).Length -ne $_.Length) {
                Copy-Item -LiteralPath $_.FullName -Destination $dest -Force
                Copy-ToOffsite $dest
            }
        }

    $index = Join-Path $BinlogSource "mysql-bin.index"
    if (Test-Path -LiteralPath $index) {
        $destIndex = Join-Path $binlogDir "mysql-bin.index"
        Copy-Item -LiteralPath $index -Destination $destIndex -Force
        Copy-ToOffsite $destIndex
    }

    Remove-OldFiles $binlogDir $KeepBinlogDays
    Write-BackupLog "Backup incremental/binlog finalizado"
}

try {
    New-Dir $BackupRoot
    if ($Mode -eq "full" -or $Mode -eq "all") {
        Backup-Full
    }
    if ($Mode -eq "binlog" -or $Mode -eq "all") {
        Backup-Binlog
    }
    exit 0
} catch {
    Write-BackupLog "ERRO: $($_.Exception.Message)"
    exit 1
}
