Private/Invoke-SecureUninstall.ps1

#Requires -Version 5.1

function Invoke-SecureUninstall {
    <#
    .SYNOPSIS
    Executes secure uninstallation (Phase 5).
 
    .DESCRIPTION
    Performs actual removal with secure file deletion.
    - Stops bot process if running
    - Securely deletes config.json (3-pass SDelete)
    - Deletes all other files
    - Cleans AppData
    - Optionally uninstalls dependencies
    - Comprehensive logging
 
    .PARAMETER BotPath
    Installation directory to remove.
 
    .PARAMETER DependencyChoices
    Hashtable with user's dependency removal choices.
 
    .OUTPUTS
    Hashtable with removal status:
    @{
        Success = $true/$false
        DeletedFiles = @(list)
        Errors = @(list)
        LogPath = path
    }
    #>


    param(
        [string]$BotPath = "$env:USERPROFILE\AppData\Local\BATCRelayBot",
        [hashtable]$DependencyChoices = @{}
    )

    $logDir = "$env:APPDATA\BATCRelayBot-Uninstall"
    New-Item -ItemType Directory -Path $logDir -Force -ErrorAction SilentlyContinue | Out-Null
    $logPath = Join-Path $logDir "removal-$(Get-Date -Format 'yyyyMMdd-HHmmss').log"
    $deletedFiles = @()
    $errors = @()

    # Create removal log (saved to AppData so it survives directory deletion)
    "=== BATCRelayBot Removal Log ===" | Out-File $logPath -Force -Encoding UTF8
    "Started: $(Get-Date -Format 'yyyy-MM-dd HH:mm:ss')" | Add-Content $logPath -Encoding UTF8
    ""  | Add-Content $logPath -Encoding UTF8

    Write-Host ""
    Write-Host "Starting installation removal..." -ForegroundColor Green
    Write-Host ""

    # Step 1: Stop bot process
    Write-Host "[1/6] Stopping bot process..." -ForegroundColor Cyan
    try {
        $botProcess = Get-Process python -ErrorAction SilentlyContinue |
                      Where-Object { $_.CommandLine -match "bot\.py" }
        if ($botProcess) {
            Stop-Process $botProcess -Force -ErrorAction Stop
            "[OK] Bot process stopped" | Add-Content $logPath -Encoding UTF8
            Write-Host " DONE" -ForegroundColor Green
        } else {
            Write-Host " (Not running)" -ForegroundColor Gray
        }
    } catch {
        $msg = "Warning: Could not stop bot process: $_"
        $msg | Add-Content $logPath -Encoding UTF8
        Write-Host " WARNING: $_" -ForegroundColor Yellow
    }
    Write-Host ""

    # Step 2: Secure delete config.json
    Write-Host "[2/6] Securely deleting config.json..." -ForegroundColor Cyan
    $configPath = Join-Path $BotPath "config.json"
    if (Test-Path $configPath) {
        try {
            # Try SDelete first (bundled with module)
            $sdeleteExe = Join-Path $PSScriptRoot "..\..\tools\sdelete64.exe"
            if (Test-Path $sdeleteExe) {
                & $sdeleteExe -p 3 $configPath 2>$null
                "[OK] config.json securely deleted (3-pass SDelete)" | Add-Content $logPath -Encoding UTF8
                Write-Host " DONE (SDelete)" -ForegroundColor Green
            } else {
                # Fallback: Multi-pass PowerShell overwrite
                $bytes = [byte[]]@(0) * (Get-Item $configPath).Length
                $random = New-Object Random
                for ($i = 0; $i -lt 3; $i++) {
                    $random.NextBytes($bytes)
                    [System.IO.File]::WriteAllBytes($configPath, $bytes)
                }
                Remove-Item $configPath -Force
                "[OK] config.json deleted (multi-pass overwrite)" | Add-Content $logPath -Encoding UTF8
                Write-Host " DONE (Multi-pass)" -ForegroundColor Green
            }
            $deletedFiles += "config.json"
        } catch {
            $msg = "ERROR: Failed to delete config.json: $_"
            $msg | Add-Content $logPath -Encoding UTF8
            $errors += $msg
            Write-Host " FAILED: $_" -ForegroundColor Red
        }
    }
    Write-Host ""

    # Step 3: Delete other files
    Write-Host "[3/6] Deleting installation files..." -ForegroundColor Cyan
    try {
        if (Test-Path $BotPath) {
            $files = Get-ChildItem -Path $BotPath -File -ErrorAction SilentlyContinue
            $count = 0
            foreach ($file in $files) {
                try {
                    Remove-Item $file.FullName -Force -ErrorAction Stop
                    $deletedFiles += $file.Name
                    $count++
                } catch {
                    $errors += "Could not delete $($file.Name): $_"
                }
            }
            "[OK] Deleted $count files from $BotPath" | Add-Content $logPath -Encoding UTF8
            Write-Host " DONE ($count files)" -ForegroundColor Green
        }
    } catch {
        $msg = "ERROR: Failed to delete files: $_"
        $msg | Add-Content $logPath -Encoding UTF8
        $errors += $msg
        Write-Host " FAILED: $_" -ForegroundColor Red
    }
    Write-Host ""

    # Step 4: Clean AppData
    Write-Host "[4/6] Cleaning AppData..." -ForegroundColor Cyan
    try {
        $appDataPath = "$env:APPDATA\BATCRelayBot"
        if (Test-Path $appDataPath) {
            Remove-Item $appDataPath -Recurse -Force -ErrorAction SilentlyContinue
            "[OK] Cleaned AppData\BATCRelayBot" | Add-Content $logPath -Encoding UTF8
            Write-Host " DONE" -ForegroundColor Green
        } else {
            Write-Host " (No AppData folder)" -ForegroundColor Gray
        }

        $pipCache = "$env:APPDATA\pip\cache"
        if ((Test-Path $pipCache) -and $DependencyChoices.RemovePython) {
            Get-ChildItem -Path $pipCache -Recurse -ErrorAction SilentlyContinue | Remove-Item -Force -Recurse -ErrorAction SilentlyContinue
            "[OK] Cleaned pip cache" | Add-Content $logPath -Encoding UTF8
        }
    } catch {
        Write-Host " WARNING: $_" -ForegroundColor Yellow
    }
    Write-Host ""

    # Step 5: Uninstall optional dependencies
    Write-Host "[5/6] Uninstalling optional dependencies..." -ForegroundColor Cyan
    $depsRemoved = 0

    if ($DependencyChoices.RemovePython) {
        try {
            Write-Host " Uninstalling Python..." -ForegroundColor Gray
            winget uninstall "Python.Python" --silent 2>$null
            "[OK] Python uninstalled" | Add-Content $logPath -Encoding UTF8
            $depsRemoved++
        } catch {
            Write-Host " WARNING: Python uninstall failed" -ForegroundColor Yellow
        }
    }

    if ($DependencyChoices.RemoveFFmpeg) {
        try {
            Write-Host " Uninstalling FFmpeg..." -ForegroundColor Gray
            winget uninstall "Gyan.FFmpeg" --silent 2>$null
            "[OK] FFmpeg uninstalled" | Add-Content $logPath -Encoding UTF8
            $depsRemoved++
        } catch {
            Write-Host " WARNING: FFmpeg uninstall failed" -ForegroundColor Yellow
        }
    }

    if ($DependencyChoices.RemoveModule) {
        try {
            Write-Host " Uninstalling PowerShell module..." -ForegroundColor Gray
            Uninstall-Module BATCRelayBot -Force -ErrorAction Stop
            "[OK] PowerShell module uninstalled" | Add-Content $logPath -Encoding UTF8
            $depsRemoved++
        } catch {
            Write-Host " WARNING: Module uninstall failed" -ForegroundColor Yellow
        }
    }

    if ($depsRemoved -eq 0) {
        Write-Host " (No dependencies selected)" -ForegroundColor Gray
    } else {
        Write-Host " DONE ($depsRemoved items removed)" -ForegroundColor Green
    }
    Write-Host ""

    # Write final verification BEFORE deleting directory (log is inside it)
    $success = -not (Test-Path $BotPath)
    if (-not $success) {
        "[OK] Pre-deletion verification: Installation directory exists, proceeding with removal" | Add-Content $logPath -Encoding UTF8
    }

    # Step 6: Delete installation directory
    Write-Host "[6/6] Removing installation directory..." -ForegroundColor Cyan
    try {
        if (Test-Path $BotPath) {
            Remove-Item $BotPath -Recurse -Force -ErrorAction SilentlyContinue
            Write-Host " DONE" -ForegroundColor Green
        }
    } catch {
        $msg = "ERROR: Could not remove directory: $_"
        $errors += $msg
        Write-Host " FAILED: $_" -ForegroundColor Red
    }
    Write-Host ""

    # Verify removal (log file will be gone after directory deletion)
    $success = -not (Test-Path $BotPath)

    return @{
        Success = $success
        DeletedFiles = $deletedFiles
        Errors = $errors
        LogPath = $logPath
    }
}

Export-ModuleMember -Function Invoke-SecureUninstall