# ================================================================
#  ARIA - Windows / WSL2 / Docker Desktop VHDX Cleanup
# ================================================================
#
#  Findet alle WSL2 + Docker Desktop ext4.vhdx Files unter
#  C:\Users\<USER>\AppData\Local\... und kompaktiert sie via diskpart.
#  Damit bekommst du Speicherplatz zurueck den du IN den Distros/
#  Containern geloescht hast (z.B. nach `docker system prune`),
#  der aber von der VHDX bisher nicht freigegeben wurde.
#
#  Nutzung (PowerShell als ADMIN, oder via cleanup-windows.bat):
#    .\cleanup-windows.ps1 stefan
#    .\cleanup-windows.ps1 -User stefan
#    .\cleanup-windows.ps1 -User stefan -SkipPrune    # nur compacten
#    .\cleanup-windows.ps1 -User stefan -PruneOnly    # nur prune
#
#  Was passiert:
#    1. Erst (optional): docker system prune + builder prune in WSL2
#    2. wsl --shutdown
#    3. Alle gefundenen .vhdx Files mit diskpart compact vdisk shrinken
#
#  Hinweis: diskpart braucht KEINE Hyper-V Tools (anders als Optimize-VHD).
#
#  ASCII-only damit Windows-PowerShell 5.1 das File ohne BOM korrekt
#  parsen kann (UTF-8-Sonderzeichen wuerden sonst als Windows-1252
#  fehlinterpretiert).
# ================================================================

[CmdletBinding()]
param(
    [Parameter(Mandatory=$true, Position=0,
               HelpMessage="Dein Windows-Benutzername (z.B. stefan)")]
    [string]$User,

    [Parameter(HelpMessage="Docker prune ueberspringen - nur compacten")]
    [switch]$SkipPrune,

    [Parameter(HelpMessage="Docker prune NUR machen, dann beenden")]
    [switch]$PruneOnly
)

# Defensive: Process-Scope ExecutionPolicy auf Bypass - verhindert dass
# Untersaetze (z.B. Module) blockiert werden. Harmless wenn Parent schon
# Bypass aufgerufen hat.
try { Set-ExecutionPolicy -Scope Process -ExecutionPolicy Bypass -Force | Out-Null } catch {}

# Admin-Check + Self-Elevation
# Wenn nicht als Admin gestartet -> einmal neu starten als Admin, mit
# ExecutionPolicy Bypass + den Original-Argumenten. User muss nur einmal
# UAC-Prompt bestaetigen.
$isAdmin = ([Security.Principal.WindowsPrincipal] `
    [Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole(
    [Security.Principal.WindowsBuiltInRole]::Administrator)
if (-not $isAdmin) {
    Write-Host "-> Starte neu als Administrator (mit ExecutionPolicy Bypass)..." -ForegroundColor Yellow
    $myPath = $MyInvocation.MyCommand.Path
    $forwardArgs = @("-NoProfile", "-ExecutionPolicy", "Bypass", "-File", "`"$myPath`"")
    if ($User)      { $forwardArgs += @("-User", $User) }
    if ($SkipPrune) { $forwardArgs += "-SkipPrune" }
    if ($PruneOnly) { $forwardArgs += "-PruneOnly" }
    try {
        Start-Process powershell.exe -Verb RunAs -ArgumentList $forwardArgs
    } catch {
        Write-Host "[FAIL] UAC-Elevation abgebrochen oder fehlgeschlagen." -ForegroundColor Red
        Write-Host "       Rechtsklick auf PowerShell -> 'Als Administrator ausfuehren'" -ForegroundColor Yellow
        exit 1
    }
    exit 0
}

$basePath = "C:\Users\$User\AppData\Local"
if (-not (Test-Path $basePath)) {
    Write-Host "[FAIL] Pfad existiert nicht: $basePath" -ForegroundColor Red
    Write-Host "       Pruefe den Benutzernamen." -ForegroundColor Yellow
    exit 1
}

Write-Host "================================================================" -ForegroundColor Cyan
Write-Host "  ARIA Cleanup fuer User: $User" -ForegroundColor Cyan
Write-Host "================================================================" -ForegroundColor Cyan
Write-Host ""

# -- 1. Docker prune (in WSL2) -----------------------------------
if (-not $SkipPrune) {
    Write-Host "[1/3] Docker Cleanup in WSL2..." -ForegroundColor Yellow
    Write-Host "      docker system prune -a --volumes -f" -ForegroundColor Gray
    Write-Host "      docker builder prune -a -f" -ForegroundColor Gray
    Write-Host ""
    try {
        wsl -e bash -c "docker system prune -a --volumes -f && docker builder prune -a -f"
        Write-Host "      [OK] fertig" -ForegroundColor Green
    } catch {
        Write-Host "      [WARN] Docker prune fehlgeschlagen (vielleicht laeuft Docker Desktop nicht?)" -ForegroundColor Yellow
        Write-Host "             $_" -ForegroundColor Gray
    }
    Write-Host ""
    if ($PruneOnly) {
        Write-Host "PruneOnly gesetzt - fertig." -ForegroundColor Cyan
        exit 0
    }
}

# -- 2. WSL2 shutdown --------------------------------------------
Write-Host "[2/3] WSL2 herunterfahren..." -ForegroundColor Yellow
wsl --shutdown
Start-Sleep -Seconds 3
Write-Host "      [OK] fertig" -ForegroundColor Green
Write-Host ""

# -- 3. VHDX-Files finden + compacten ----------------------------
Write-Host "[3/3] VHDX-Files suchen + compacten..." -ForegroundColor Yellow
Write-Host ""

$vhdxFiles = @()
$vhdxFiles += Get-ChildItem -Path "$basePath\Docker" -Recurse -Filter "*.vhdx" -ErrorAction SilentlyContinue
$vhdxFiles += Get-ChildItem -Path "$basePath\Packages" -Recurse -Filter "ext4.vhdx" -ErrorAction SilentlyContinue
$vhdxFiles = $vhdxFiles | Sort-Object FullName -Unique

if ($vhdxFiles.Count -eq 0) {
    Write-Host "      Keine .vhdx Files gefunden." -ForegroundColor Yellow
    exit 0
}

Write-Host "Gefundene Files (vorher):" -ForegroundColor Cyan
foreach ($f in $vhdxFiles) {
    $sizeGB = [math]::Round($f.Length / 1GB, 2)
    Write-Host ("  {0,8} GB  {1}" -f $sizeGB, $f.FullName) -ForegroundColor Gray
}
Write-Host ""

$totalBefore = ($vhdxFiles | Measure-Object Length -Sum).Sum

foreach ($f in $vhdxFiles) {
    Write-Host "-> Compact: $($f.FullName)" -ForegroundColor White
    $sizeBefore = [math]::Round($f.Length / 1GB, 2)

    # Temporaeres diskpart-Script schreiben
    $tmp = [System.IO.Path]::GetTempFileName()
    @"
select vdisk file="$($f.FullName)"
attach vdisk readonly
compact vdisk
detach vdisk
exit
"@ | Out-File -Encoding ASCII -FilePath $tmp

    try {
        $output = & diskpart /s $tmp 2>&1
        # Datei neu lesen - Length ist gecacht
        $newFile = Get-Item $f.FullName
        $sizeAfter = [math]::Round($newFile.Length / 1GB, 2)
        $saved = [math]::Round($sizeBefore - $sizeAfter, 2)
        if ($saved -gt 0) {
            Write-Host ("  [OK] {0} GB -> {1} GB  (gespart: {2} GB)" -f $sizeBefore, $sizeAfter, $saved) -ForegroundColor Green
        } else {
            Write-Host ("  --   {0} GB -> {1} GB  (nichts zu holen - File war schon optimal)" -f $sizeBefore, $sizeAfter) -ForegroundColor DarkGray
        }
    } catch {
        Write-Host "  [FAIL] Fehler: $_" -ForegroundColor Red
        Write-Host "         diskpart-Output:" -ForegroundColor DarkGray
        $output | ForEach-Object { Write-Host "         $_" -ForegroundColor DarkGray }
    } finally {
        Remove-Item $tmp -ErrorAction SilentlyContinue
    }
    Write-Host ""
}

# -- Zusammenfassung ---------------------------------------------
$vhdxFilesAfter = @()
$vhdxFilesAfter += Get-ChildItem -Path "$basePath\Docker" -Recurse -Filter "*.vhdx" -ErrorAction SilentlyContinue
$vhdxFilesAfter += Get-ChildItem -Path "$basePath\Packages" -Recurse -Filter "ext4.vhdx" -ErrorAction SilentlyContinue
$vhdxFilesAfter = $vhdxFilesAfter | Sort-Object FullName -Unique
$totalAfter = ($vhdxFilesAfter | Measure-Object Length -Sum).Sum

$savedTotal = [math]::Round(($totalBefore - $totalAfter) / 1GB, 2)

Write-Host "================================================================" -ForegroundColor Cyan
Write-Host ("  Gesamt: {0} GB -> {1} GB  (gespart: {2} GB)" -f `
    [math]::Round($totalBefore / 1GB, 2),
    [math]::Round($totalAfter / 1GB, 2),
    $savedTotal) -ForegroundColor Cyan
Write-Host "================================================================" -ForegroundColor Cyan
Write-Host ""
Write-Host "Fertig. Docker Desktop / WSL2 starten ja von alleine wieder beim naechsten Aufruf." -ForegroundColor Green
