# RescuePC Protected Module - DO NOT EDIT
# Source code is maintained separately in the development repository.

$script:ValidStatuses = @('Success', 'PartialSuccess', 'Failed', 'Skipped')
$script:ValidRiskLevels = @('Low', 'Medium', 'High')
$script:ValidSeverities = @('Info', 'Warning', 'Error', 'Critical')
$script:SchemaVersion = 2

function Remove-PII {
    param(
        [Parameter(Mandatory)]
        [AllowEmptyString()]
        [string]$Text,

        [switch]$PreserveMachineName
    )

    if ([string]::IsNullOrEmpty($Text)) {
        return $Text
    }

    $result = $Text

    $currentUser = $env:USERNAME
    if ($currentUser) {
        $result = $result -replace "\\Users\\$currentUser\\", '\Users\[REDACTED]\'
        $result = $result -replace "\\Users\\$currentUser(?=[\s\.\,\;\:\)]|$)", '\Users\[REDACTED]'
        $result = $result -replace "(?<=User[:\s]+)$currentUser(?=[\s\.\,\;\:\)]|$)", '[REDACTED]'
    }

    $result = $result -replace '(?i)C:\\Users\\[^\\]+\\', 'C:\Users\[REDACTED]\'

    if (-not $PreserveMachineName) {
        $machineName = $env:COMPUTERNAME
        if ($machineName) {
            $result = $result -replace "(?i)$machineName", '[MACHINE]'
        }
    }

    $result = $result -replace '[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}', '[EMAIL_REDACTED]'

    $result = $result -replace '(?<!127\.0\.0\.)(?<!192\.168\.)(?<!10\.)(?<!\d)\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}(?!\d)', '[IP_REDACTED]'

    return $result
}

$script:ScrubPII = $true

class RepairStep {
    [string]$Name
    [bool]$Attempted
    [bool]$Success
    [int]$ExitCode
    [string]$Message
    [string]$Severity
    [int]$DurationMs

    RepairStep([string]$name) {
        $this.Name = $name
        $this.Attempted = $false
        $this.Success = $false
        $this.ExitCode = -1
        $this.Message = ""
        $this.Severity = "Info"
        $this.DurationMs = 0
    }

    [hashtable]ToHashtable() {
        return @{
            name = $this.Name
            attempted = $this.Attempted
            success = $this.Success
            exitCode = $this.ExitCode
            message = $this.Message
            severity = $this.Severity
            durationMs = $this.DurationMs
        }
    }
}

class ScriptResult {
    [int]$SchemaVersion
    [string]$ScriptId
    [string]$ScriptVersion
    [datetime]$StartTime
    [datetime]$EndTime
    [int]$DurationMs
    [string]$Status
    [string]$RiskLevel
    [bool]$RequiresReboot
    [bool]$RollbackAvailable
    [int]$ExitCode
    [System.Collections.ArrayList]$Steps
    [System.Collections.ArrayList]$ChangesApplied
    [System.Collections.ArrayList]$Warnings
    [System.Collections.ArrayList]$Errors
    [System.Collections.ArrayList]$Artifacts
    [hashtable]$Metadata

    ScriptResult([string]$scriptId, [string]$scriptVersion) {
        $this.SchemaVersion = $script:SchemaVersion
        $this.ScriptId = $scriptId
        $this.ScriptVersion = $scriptVersion
        $this.StartTime = Get-Date
        $this.Status = "Success"
        $this.RiskLevel = "Low"
        $this.RequiresReboot = $false
        $this.RollbackAvailable = $false
        $this.ExitCode = 0
        $this.Steps = [System.Collections.ArrayList]::new()
        $this.ChangesApplied = [System.Collections.ArrayList]::new()
        $this.Warnings = [System.Collections.ArrayList]::new()
        $this.Errors = [System.Collections.ArrayList]::new()
        $this.Artifacts = [System.Collections.ArrayList]::new()
        $this.Metadata = @{}
    }

    [void]AddStep([string]$name, [bool]$success, [int]$exitCode, [string]$message) {
        $step = [RepairStep]::new($name)
        $step.Attempted = $true
        $step.Success = $success
        $step.ExitCode = $exitCode
        $step.Message = $message
        $step.Severity = if ($success) { "Info" } elseif ($exitCode -eq 0) { "Warning" } else { "Error" }
        $this.Steps.Add($step) | Out-Null
    }

    [void]AddStepWithDuration([string]$name, [bool]$success, [int]$exitCode, [string]$message, [int]$durationMs) {
        $step = [RepairStep]::new($name)
        $step.Attempted = $true
        $step.Success = $success
        $step.ExitCode = $exitCode
        $step.Message = $message
        $step.DurationMs = $durationMs
        $step.Severity = if ($success) { "Info" } elseif ($exitCode -eq 0) { "Warning" } else { "Error" }
        $this.Steps.Add($step) | Out-Null
    }

    [void]AddChange([string]$change) {
        $this.ChangesApplied.Add($change) | Out-Null
    }

    [void]AddWarning([string]$warning) {
        $this.Warnings.Add($warning) | Out-Null
    }

    [void]AddError([string]$errorMessage) {
        $this.Errors.Add($errorMessage) | Out-Null
    }

    [void]AddArtifact([string]$path, [string]$description) {
        $this.Artifacts.Add(@{ path = $path; description = $description }) | Out-Null
    }

    [void]SetRequiresReboot([bool]$value) {
        $this.RequiresReboot = $value
    }

    [void]SetRiskLevel([string]$level) {
        if ($level -in $script:ValidRiskLevels) {
            $this.RiskLevel = $level
        }
    }

    [void]SetRollbackAvailable([bool]$value) {
        $this.RollbackAvailable = $value
    }

    [void]AddMetadata([string]$key, $value) {
        $this.Metadata[$key] = $value
    }

    [void]Complete([int]$exitCode) {
        $this.EndTime = Get-Date
        $this.DurationMs = [int]($this.EndTime - $this.StartTime).TotalMilliseconds
        $this.ExitCode = $exitCode

        $failedSteps = @($this.Steps | Where-Object { $_.Attempted -and -not $_.Success })
        $attemptedSteps = @($this.Steps | Where-Object { $_.Attempted })

        if ($exitCode -ne 0 -or $this.Errors.Count -gt 0) {
            $this.Status = "Failed"
        }
        elseif ($failedSteps.Count -gt 0 -or $this.Warnings.Count -gt 0) {
            $this.Status = "PartialSuccess"
        }
        elseif ($attemptedSteps.Count -eq 0) {
            $this.Status = "Skipped"
        }
        else {
            $this.Status = "Success"
        }
    }

    [hashtable]ToHashtable() {
        return [ordered]@{
            schemaVersion = $this.SchemaVersion
            scriptId = $this.ScriptId
            scriptVersion = $this.ScriptVersion
            startTime = $this.StartTime.ToString("o")
            endTime = $this.EndTime.ToString("o")
            durationMs = $this.DurationMs
            status = $this.Status
            riskLevel = $this.RiskLevel
            requiresReboot = $this.RequiresReboot
            rollbackAvailable = $this.RollbackAvailable
            exitCode = $this.ExitCode
            steps = @($this.Steps | ForEach-Object { $_.ToHashtable() })
            changesApplied = @($this.ChangesApplied)
            warnings = @($this.Warnings)
            errors = @($this.Errors)
            artifacts = @($this.Artifacts)
            metadata = $this.Metadata
        }
    }

    [string]ToJson() {
        return $this.ToHashtable() | ConvertTo-Json -Depth 10 -Compress
    }

    [string]ToJsonPretty() {
        return $this.ToHashtable() | ConvertTo-Json -Depth 10
    }
}

function New-ScriptResult {
    param(
        [Parameter(Mandatory)]
        [string]$ScriptId,

        [Parameter(Mandatory=$false)]
        [string]$ScriptVersion = "1.0.0"
    )

    return [ScriptResult]::new($ScriptId, $ScriptVersion)
}

function Write-ScriptResult {
    param(
        [Parameter(Mandatory)]
        [ScriptResult]$Result,

        [string]$OutputPath
    )

    if (-not $OutputPath) {
        $logRoot = if ($global:ProjectRoot) {
            Join-Path $global:ProjectRoot "logs\repair_results"
        } else {
            Join-Path $PSScriptRoot "..\..\..\logs\repair_results"
        }

        if (-not (Test-Path $logRoot)) {
            New-Item -ItemType Directory -Path $logRoot -Force | Out-Null
        }

        $timestamp = Get-Date -Format "yyyyMMdd_HHmmss"
        $OutputPath = Join-Path $logRoot "$($Result.ScriptId)_$timestamp.json"
    }

    try {
        $Result.ToJsonPretty() | Out-File -FilePath $OutputPath -Encoding UTF8 -Force
        Write-Verbose "Script result saved to: $OutputPath"
    }
    catch {
        Write-Warning "Could not save script result to file: $_"
    }

    $summaryLine = "RESCUEPC_RESULT: $($Result.ToJson())"
    Write-Host $summaryLine -ForegroundColor DarkGray

    return $OutputPath
}

function Get-ScriptResultSummary {
    param(
        [Parameter(Mandatory)]
        [ScriptResult]$Result
    )

    $summary = @"
========================================
SCRIPT RESULT: $($Result.ScriptId)
========================================
Status: $($Result.Status)
Duration: $($Result.DurationMs)ms
Exit Code: $($Result.ExitCode)
Changes Applied: $($Result.ChangesApplied.Count)
Warnings: $($Result.Warnings.Count)
Errors: $($Result.Errors.Count)
Requires Reboot: $($Result.RequiresReboot)
Risk Level: $($Result.RiskLevel)
========================================
"@

    return $summary
}

function Export-HtmlReport {
    param(
        [Parameter(Mandatory)]
        $Results,

        [Parameter(Mandatory=$false)]
        [string]$OutputPath,

        [Parameter(Mandatory=$false)]
        [string]$Title = "RescuePC Repairs Report"
    )

    if ($Results -isnot [array]) {
        $Results = @($Results)
    }

    $parsedResults = @()
    foreach ($r in $Results) {
        if ($r -is [string] -and (Test-Path $r)) {
            $json = Get-Content $r -Raw | ConvertFrom-Json
            $parsedResults += $json
        }
        elseif ($r -is [ScriptResult]) {
            $parsedResults += $r.ToHashtable()
        }
        elseif ($r -is [hashtable]) {
            $parsedResults += $r
        }
    }

    $totalRepairs = $parsedResults.Count
    $successCount = @($parsedResults | Where-Object { $_.status -eq 'Success' }).Count
    $null = @($parsedResults | Where-Object { $_.status -eq 'PartialSuccess' }).Count
    $null = @($parsedResults | Where-Object { $_.status -eq 'Failed' }).Count
    $totalChanges = ($parsedResults | ForEach-Object { $_.changesApplied.Count } | Measure-Object -Sum).Sum
    $totalDuration = ($parsedResults | ForEach-Object { $_.durationMs } | Measure-Object -Sum).Sum
    $requiresReboot = ($parsedResults | Where-Object { $_.requiresReboot }).Count -gt 0

    $timestamp = Get-Date -Format "yyyy-MM-dd HH:mm:ss"
    $computerName = if ($script:ScrubPII) { "[MACHINE]" } else { $env:COMPUTERNAME }
    $userName = if ($script:ScrubPII) { "[REDACTED]" } else { $env:USERNAME }

    $html = @"
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>$Title</title>
    <style>
        * { margin: 0; padding: 0; box-sizing: border-box; }
        body { font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif; background:
        .container { max-width: 900px; margin: 0 auto; padding: 20px; }
        .header { background: linear-gradient(135deg,
        .header h1 { font-size: 28px; margin-bottom: 5px; }
        .header .subtitle { color:
        .header .logo { color:
        .summary-grid { display: grid; grid-template-columns: repeat(4, 1fr); gap: 15px; margin-bottom: 20px; }
        .summary-card { background: white; padding: 20px; border-radius: 10px; text-align: center; box-shadow: 0 1px 3px rgba(0,0,0,0.1); }
        .summary-card .number { font-size: 32px; font-weight: bold; }
        .summary-card .label { color:
        .success { color:
        .partial { color:
        .failed { color:
        .info { color:
        .repair-list { background: white; border-radius: 10px; box-shadow: 0 1px 3px rgba(0,0,0,0.1); overflow: hidden; }
        .repair-item { padding: 15px 20px; border-bottom: 1px solid
        .repair-item:last-child { border-bottom: none; }
        .repair-item .name { font-weight: 600; font-size: 16px; }
        .repair-item .meta { color:
        .repair-item .status-badge { display: inline-block; padding: 3px 10px; border-radius: 20px; font-size: 11px; font-weight: 600; text-transform: uppercase; }
        .status-success { background:
        .status-partial { background:
        .status-failed { background:
        .changes-list { margin-top: 10px; padding-left: 20px; }
        .changes-list li { color:
        .warnings-list li { color:
        .footer { text-align: center; padding: 20px; color:
        .reboot-warning { background:
        .reboot-warning .icon { font-size: 24px; margin-right: 15px; }
        @media print { body { background: white; } .container { max-width: 100%; } }
    </style>
</head>
<body>
    <div class="container">
        <div class="header">
            <span class="logo">R</span>
            <h1>$Title</h1>
            <div class="subtitle">Generated: $timestamp | Computer: $computerName | User: $userName</div>
        </div>

        $(if ($requiresReboot) { @"
        <div class="reboot-warning">
            <span class="icon">[!]</span>
            <div>
                <strong>Restart Required</strong><br>
                Some repairs require a system restart to take full effect.
            </div>
        </div>
"@ })

        <div class="summary-grid">
            <div class="summary-card">
                <div class="number info">$totalRepairs</div>
                <div class="label">Repairs Run</div>
            </div>
            <div class="summary-card">
                <div class="number success">$successCount</div>
                <div class="label">Successful</div>
            </div>
            <div class="summary-card">
                <div class="number">$totalChanges</div>
                <div class="label">Changes Made</div>
            </div>
            <div class="summary-card">
                <div class="number">$([math]::Round($totalDuration / 1000, 1))s</div>
                <div class="label">Total Time</div>
            </div>
        </div>

        <div class="repair-list">
"@

    foreach ($r in $parsedResults) {
        $statusClass = switch ($r.status) {
            'Success' { 'status-success' }
            'PartialSuccess' { 'status-partial' }
            'Failed' { 'status-failed' }
            default { 'status-partial' }
        }

        $durationSec = [math]::Round($r.durationMs / 1000, 1)
        $changesHtml = ""
        if ($r.changesApplied -and $r.changesApplied.Count -gt 0) {
            $changesHtml = "<ul class='changes-list'>"
            foreach ($change in $r.changesApplied) {
                $safeChange = if ($script:ScrubPII) { Remove-PII -Text $change -PreserveMachineName } else { $change }
                $changesHtml += "<li>[OK] $safeChange</li>"
            }
            $changesHtml += "</ul>"
        }

        $warningsHtml = ""
        if ($r.warnings -and $r.warnings.Count -gt 0) {
            $warningsHtml = "<ul class='warnings-list'>"
            foreach ($warning in $r.warnings) {
                $safeWarning = if ($script:ScrubPII) { Remove-PII -Text $warning -PreserveMachineName } else { $warning }
                $warningsHtml += "<li>[!] $safeWarning</li>"
            }
            $warningsHtml += "</ul>"
        }

        $html += @"
            <div class="repair-item">
                <div style="display: flex; justify-content: space-between; align-items: center;">
                    <span class="name">$($r.scriptId -replace '_', ' ' -replace '(?<=\w)([A-Z])', ' $1')</span>
                    <span class="status-badge $statusClass">$($r.status)</span>
                </div>
                <div class="meta">Duration: ${durationSec}s | Risk: $($r.riskLevel) | Exit Code: $($r.exitCode)</div>
                $changesHtml
                $warningsHtml
            </div>
"@
    }

    $html += @"
        </div>

        <div class="footer">
            <p>Generated by RescuePC Repairs Toolkit | www.rescuepcrepairs.com</p>
            <p>This report documents all repairs performed on this system.</p>
        </div>
    </div>
</body>
</html>
"@

    if (-not $OutputPath) {
        $reportRoot = if ($global:ProjectRoot) {
            Join-Path $global:ProjectRoot "logs\repair_reports"
        } else {
            Join-Path $PSScriptRoot "..\..\..\logs\repair_reports"
        }

        if (-not (Test-Path $reportRoot)) {
            New-Item -ItemType Directory -Path $reportRoot -Force | Out-Null
        }

        $timestamp = Get-Date -Format "yyyyMMdd_HHmmss"
        $OutputPath = Join-Path $reportRoot "RescuePC_Report_$timestamp.html"
    }

    try {
        $html | Out-File -FilePath $OutputPath -Encoding UTF8 -Force
        Write-Host "HTML report saved to: $OutputPath" -ForegroundColor Green
        return $OutputPath
    }
    catch {
        Write-Warning "Could not save HTML report: $_"
        return $null
    }
}

function Get-LatestRepairResults {
    param(
        [int]$Count = 10,
        [datetime]$Since
    )

    $logRoot = if ($global:ProjectRoot) {
        Join-Path $global:ProjectRoot "logs\repair_results"
    } else {
        Join-Path $PSScriptRoot "..\..\..\logs\repair_results"
    }

    if (-not (Test-Path $logRoot)) {
        return @()
    }

    $files = Get-ChildItem -Path $logRoot -Filter "*.json" -File |
        Sort-Object LastWriteTime -Descending

    if ($Since) {
        $files = $files | Where-Object { $_.LastWriteTime -ge $Since }
    }

    return $files | Select-Object -First $Count | ForEach-Object { $_.FullName }
}

function Remove-OldLogs {
    param (
        [int]$DaysToKeep = 30,
        [switch]$WhatIf
    )

    $cutoffDate = (Get-Date).AddDays(-$DaysToKeep)
    $deletedCount = 0
    $deletedSize = 0

    $cleanupPaths = @(
        @{ Path = "C:\ProgramData\RescuePC\logs\repair_logs"; Extensions = @("*.log", "*.txt") },
        @{ Path = "C:\ProgramData\RescuePC\logs\repair_results"; Extensions = @("*.json") },
        @{ Path = "$env:LOCALAPPDATA\RescuePC\reports"; Extensions = @("*.html") },
        @{ Path = "$env:LOCALAPPDATA\RescuePC\logs"; Extensions = @("*.log", "*.txt") }
    )

    if ($global:ProjectRoot) {
        $cleanupPaths += @{ Path = (Join-Path $global:ProjectRoot "logs\repair_logs"); Extensions = @("*.log", "*.txt") }
        $cleanupPaths += @{ Path = (Join-Path $global:ProjectRoot "logs\repair_results"); Extensions = @("*.json") }
        $cleanupPaths += @{ Path = (Join-Path $global:ProjectRoot "logs\reports"); Extensions = @("*.html") }
    }

    foreach ($config in $cleanupPaths) {
        $logPath = $config.Path
        $extensions = $config.Extensions

        if (-not (Test-Path $logPath)) { continue }

        try {
            foreach ($ext in $extensions) {
                $oldFiles = Get-ChildItem -Path $logPath -Filter $ext -File -ErrorAction SilentlyContinue |
                            Where-Object { $_.LastWriteTime -lt $cutoffDate }

                foreach ($file in $oldFiles) {
                    $safeName = $file.Name.ToLower()
                    if ($safeName -match '(config|manifest|signature|license|key|cert)') {
                        continue
                    }

                    if ($WhatIf) {
                        Write-Host "[WhatIf] Would delete: $($file.FullName) (Age: $([int]((Get-Date) - $file.LastWriteTime).TotalDays) days)" -ForegroundColor Yellow
                    } else {
                        try {
                            $fileSize = $file.Length
                            Remove-Item $file.FullName -Force -ErrorAction Stop
                            $deletedCount++
                            $deletedSize += $fileSize
                        } catch {
                        }
                    }
                }
            }
        } catch {
        }
    }

    return @{
        DeletedCount = $deletedCount
        DeletedSizeKB = [math]::Round($deletedSize / 1KB, 2)
        RetentionDays = $DaysToKeep
    }
}

function Test-RPCInteractiveConsole {
    try {
        if ([Console]::IsOutputRedirected) { return $false }
        if (-not $Host -or -not $Host.UI -or -not $Host.UI.RawUI) { return $false }
        return $true
    }
    catch {
        return $false
    }
}

function Test-RPCUnicodeSupport {
    try {
        $enc = [Console]::OutputEncoding
        if ($null -eq $enc) { return $false }
        return ($enc.WebName -match 'utf-8|utf-16')
    }
    catch {
        return $false
    }
}

function Get-RPCTheme {
    return @{
        Brand      = 'Cyan'
        Title      = 'White'
        Section    = 'Magenta'
        Success    = 'Green'
        Warning    = 'Yellow'
        Error      = 'Red'
        Info       = 'Gray'
        Accent     = 'Blue'
        Muted      = 'DarkGray'
        Step       = 'DarkCyan'
    }
}

function Get-RPCGlyphs {
    $unicode = Test-RPCUnicodeSupport
    if ($unicode) {
        return @{
            Ok      = [char]0x2714
            Warn    = [char]0x26A0
            Fail    = [char]0x2716
            Info    = [char]0x2022
            Arrow   = [char]0x25B6
            Skip    = [char]0x21B7
            H       = [char]0x2550
            Dash    = [char]0x2500
            Dot     = [char]0x00B7
        }
    }
    return @{
        Ok      = '[OK]'
        Warn    = '[!!]'
        Fail    = '[XX]'
        Info    = ' * '
        Arrow   = '>>>'
        Skip    = '[~]'
        H       = '='
        Dash    = '-'
        Dot     = '.'
    }
}

function Write-RPCConsoleLine {
    param(
        [Parameter(Mandatory=$true)][string]$Text,
        [string]$Color = 'White'
    )
    if (Test-RPCInteractiveConsole) {
        Write-Host $Text -ForegroundColor $Color
    }
}

function Write-RPCBlankLine {
    if (Test-RPCInteractiveConsole) { Write-Host "" }
}

function Write-RPCDivider {
    param(
        [int]$Width = 58,
        [string]$Color = 'DarkGray',
        [switch]$Heavy
    )
    $g = Get-RPCGlyphs
    $char = if ($Heavy) { $g.H } else { $g.Dash }
    Write-RPCConsoleLine -Text ($char * $Width) -Color $Color
}

function Write-RPCBanner {
    param(
        [Parameter(Mandatory=$true)][string]$Title,
        [string]$Subtitle = '',
        [string]$Version = ''
    )
    $t = Get-RPCTheme
    Write-RPCBlankLine
    Write-RPCDivider -Color $t.Brand -Heavy
    $brandLine = "  RescuePC Repairs"
    Write-RPCConsoleLine -Text $brandLine -Color $t.Brand
    $titleLine = "  $Title"
    if ($Version) { $titleLine += "  v$Version" }
    Write-RPCConsoleLine -Text $titleLine -Color $t.Title
    if ($Subtitle) {
        Write-RPCConsoleLine -Text "  $Subtitle" -Color $t.Info
    }
    Write-RPCDivider -Color $t.Brand -Heavy
}

function Write-RPCSection {
    param(
        [Parameter(Mandatory=$true)][string]$Text
    )
    $t = Get-RPCTheme
    $g = Get-RPCGlyphs
    Write-RPCBlankLine
    Write-RPCConsoleLine -Text "$($g.Arrow) $Text" -Color $t.Section
    Write-RPCDivider -Color $t.Muted
}

function Write-RPCStatus {
    param(
        [Parameter(Mandatory=$true)]
        [ValidateSet('Info','Success','Warning','Error','Skip')]
        [string]$Level,
        [Parameter(Mandatory=$true)][string]$Message
    )
    $t = Get-RPCTheme
    $g = Get-RPCGlyphs
    switch ($Level) {
        'Info'    { Write-RPCConsoleLine -Text "  $($g.Info) $Message" -Color $t.Info }
        'Success' { Write-RPCConsoleLine -Text "  $($g.Ok) $Message"  -Color $t.Success }
        'Warning' { Write-RPCConsoleLine -Text "  $($g.Warn) $Message" -Color $t.Warning }
        'Error'   { Write-RPCConsoleLine -Text "  $($g.Fail) $Message" -Color $t.Error }
        'Skip'    { Write-RPCConsoleLine -Text "  $($g.Skip) $Message" -Color $t.Muted }
    }
}

function Write-RPCStep {
    param(
        [Parameter(Mandatory=$true)][string]$Name,
        [string]$Detail = ''
    )
    $t = Get-RPCTheme
    $g = Get-RPCGlyphs
    if ($Detail) {
        Write-RPCConsoleLine -Text "  $($g.Dot) $Name  $($g.Dash) $Detail" -Color $t.Step
    } else {
        Write-RPCConsoleLine -Text "  $($g.Dot) $Name" -Color $t.Step
    }
}

function Write-RPCMetric {
    param(
        [Parameter(Mandatory=$true)][string]$Label,
        [Parameter(Mandatory=$true)][string]$Value
    )
    $t = Get-RPCTheme
    Write-RPCConsoleLine -Text ("  {0,-30} {1}" -f ($Label + ':'), $Value) -Color $t.Title
}

function Write-RPCWarningBlock {
    param(
        [Parameter(Mandatory=$true)][string]$Message
    )
    $t = Get-RPCTheme
    $g = Get-RPCGlyphs
    Write-RPCBlankLine
    Write-RPCConsoleLine -Text "  $($g.Warn)  $Message" -Color $t.Warning
    Write-RPCBlankLine
}

function Write-RPCSummaryBox {
    param(
        [Parameter(Mandatory=$true)][string]$Title,
        [Parameter(Mandatory=$true)][hashtable]$Items
    )
    $t = Get-RPCTheme
    Write-RPCBlankLine
    Write-RPCDivider -Color $t.Brand -Heavy
    Write-RPCConsoleLine -Text "  $Title" -Color $t.Brand
    Write-RPCDivider -Color $t.Muted
    foreach ($key in $Items.Keys) {
        Write-RPCMetric -Label $key -Value ([string]$Items[$key])
    }
    Write-RPCDivider -Color $t.Brand -Heavy
}

function Start-RPCScriptUI {
    param(
        [Parameter(Mandatory=$true)][string]$ToolName,
        [string]$Version = '',
        [string]$Description = ''
    )
    Write-RPCBanner -Title $ToolName -Subtitle $Description -Version $Version
}

function Complete-RPCScriptUI {
    param(
        [Parameter(Mandatory=$true)][string]$ToolName,
        [Parameter(Mandatory=$true)][hashtable]$Summary,
        [ValidateSet('Success','Warning','Error')]
        [string]$Result = 'Success'
    )
    Write-RPCSummaryBox -Title "$ToolName  $($Result.ToUpper())" -Items $Summary
    switch ($Result) {
        'Success' { Write-RPCStatus -Level Success -Message "$ToolName finished successfully." }
        'Warning' { Write-RPCStatus -Level Warning -Message "$ToolName finished with warnings." }
        'Error'   { Write-RPCStatus -Level Error   -Message "$ToolName failed." }
    }
    Write-RPCBlankLine
}

try {
    $null = Remove-OldLogs -ErrorAction SilentlyContinue
} catch {
}

Export-ModuleMember -Function New-ScriptResult, Write-ScriptResult, Get-ScriptResultSummary, Export-HtmlReport, Get-LatestRepairResults, Remove-PII, Remove-OldLogs, `
    Test-RPCInteractiveConsole, Test-RPCUnicodeSupport, Get-RPCTheme, Get-RPCGlyphs, `
    Write-RPCConsoleLine, Write-RPCBlankLine, Write-RPCDivider, Write-RPCBanner, Write-RPCSection, `
    Write-RPCStatus, Write-RPCStep, Write-RPCMetric, Write-RPCWarningBlock, Write-RPCSummaryBox, `
    Start-RPCScriptUI, Complete-RPCScriptUI