param(
    [string]$PayloadRoot = '',
    [string]$TargetInstallRoot = '',
    [string]$ExpectedPayloadManifestPath = '',
    [string]$ReportPath = '',
    [Int64]$MinimumMemoryBytes = 8589934592,
    [Int64]$MinimumFreeSpaceBytes = 4294967296,
    [int]$MinimumWindowsBuild = 19045,
    [switch]$VerifyPayloadHashes,
    [switch]$WaitForUser,
    [switch]$PassThru
)

$ErrorActionPreference = 'Stop'

$ExpectedSolverTableSizeBytes = 676207080
$ExpectedSolverTableSha256 = 'dc6de3d909a37afe2ba7f1f978543a13eae215bd20d954d66813524a8ca20034'
$RequiredVcRuntimeVersion = [version]'14.44.35211.0'

function Write-Utf8JsonFile {
    param(
        [Parameter(Mandatory = $true)]
        [string]$Path,
        [Parameter(Mandatory = $true)]
        [object]$Value
    )

    $ParentPath = Split-Path -Parent $Path
    if (-not [string]::IsNullOrWhiteSpace($ParentPath))
    {
        New-Item -ItemType Directory -Force -Path $ParentPath | Out-Null
    }

    $Json = $Value | ConvertTo-Json -Depth 12
    $Utf8NoBom = New-Object System.Text.UTF8Encoding($false)
    [System.IO.File]::WriteAllText($Path, $Json, $Utf8NoBom)
}

function Get-NormalizedRelativePath {
    param(
        [Parameter(Mandatory = $true)]
        [string]$RootPath,
        [Parameter(Mandatory = $true)]
        [string]$FilePath
    )

    $RootPrefix = $RootPath.TrimEnd('\', '/') + [System.IO.Path]::DirectorySeparatorChar
    $ResolvedFilePath = [System.IO.Path]::GetFullPath($FilePath)
    if (-not $ResolvedFilePath.StartsWith(
        $RootPrefix,
        [System.StringComparison]::OrdinalIgnoreCase))
    {
        throw "File '$ResolvedFilePath' is outside payload root '$RootPath'."
    }

    return $ResolvedFilePath.Substring($RootPrefix.Length).Replace('\', '/')
}

function Resolve-PayloadRelativeFilePath {
    param(
        [Parameter(Mandatory = $true)]
        [string]$RootPath,
        [Parameter(Mandatory = $true)]
        [string]$RelativePath
    )

    if ([string]::IsNullOrWhiteSpace($RelativePath))
    {
        throw 'A payload-relative path cannot be empty.'
    }

    $WindowsRelativePath = $RelativePath.Replace('/', '\')
    if ([System.IO.Path]::IsPathRooted($WindowsRelativePath))
    {
        throw "Payload-relative path '$RelativePath' must not be rooted."
    }

    $ResolvedRootPath = [System.IO.Path]::GetFullPath($RootPath)
    $RootPrefix = $ResolvedRootPath.TrimEnd('\', '/') + [System.IO.Path]::DirectorySeparatorChar
    $ResolvedFilePath = [System.IO.Path]::GetFullPath(
        (Join-Path $ResolvedRootPath $WindowsRelativePath)
    )
    if (-not $ResolvedFilePath.StartsWith(
        $RootPrefix,
        [System.StringComparison]::OrdinalIgnoreCase))
    {
        throw "Payload-relative path '$RelativePath' escapes root '$RootPath'."
    }

    return $ResolvedFilePath
}

function Get-ExistingPathRoot {
    param(
        [Parameter(Mandatory = $true)]
        [string]$Path
    )

    $CandidatePath = [System.IO.Path]::GetFullPath($Path)
    while (-not (Test-Path -LiteralPath $CandidatePath))
    {
        $ParentPath = Split-Path -Parent $CandidatePath
        if ([string]::IsNullOrWhiteSpace($ParentPath) -or $ParentPath -eq $CandidatePath)
        {
            throw "No existing parent could be resolved for '$Path'."
        }

        $CandidatePath = $ParentPath
    }

    return $CandidatePath
}

function Get-VcRuntimeEvidence {
    $RegistryPaths = @(
        'HKLM:\SOFTWARE\Microsoft\VisualStudio\14.0\VC\Runtimes\x64',
        'HKLM:\SOFTWARE\WOW6432Node\Microsoft\VisualStudio\14.0\VC\Runtimes\x64'
    )

    foreach ($RegistryPath in $RegistryPaths)
    {
        if (-not (Test-Path -LiteralPath $RegistryPath))
        {
            continue
        }

        $Runtime = Get-ItemProperty -LiteralPath $RegistryPath
        $VersionText = ([string]$Runtime.Version).TrimStart('v')
        try
        {
            $ParsedVersion = [version]$VersionText
        }
        catch
        {
            continue
        }

        return [pscustomobject][ordered]@{
            registryPath = $RegistryPath
            installed = [int]$Runtime.Installed -eq 1
            version = $ParsedVersion.ToString()
        }
    }

    return $null
}

if ([string]::IsNullOrWhiteSpace($TargetInstallRoot))
{
    $TargetInstallRoot = Join-Path ([Environment]::GetFolderPath('ProgramFiles')) 'HyperTwist'
}

if ([string]::IsNullOrWhiteSpace($ReportPath))
{
    $ReportPath = Join-Path $env:TEMP 'HyperTwist-readiness.json'
}

$Checks = New-Object System.Collections.ArrayList
function Add-ReadinessCheck {
    param(
        [Parameter(Mandatory = $true)]
        [string]$Id,
        [Parameter(Mandatory = $true)]
        [ValidateSet('required', 'optional', 'informational')]
        [string]$Class,
        [Parameter(Mandatory = $true)]
        [bool]$Passed,
        [Parameter(Mandatory = $true)]
        [string]$Summary,
        [object]$Evidence = $null
    )

    [void]$Checks.Add([pscustomobject][ordered]@{
        id = $Id
        class = $Class
        passed = $Passed
        summary = $Summary
        evidence = $Evidence
    })
}

$OperatingSystem = Get-CimInstance Win32_OperatingSystem
$ComputerSystem = Get-CimInstance Win32_ComputerSystem
$WindowsBuild = [int]$OperatingSystem.BuildNumber
$Is64BitOperatingSystem = [Environment]::Is64BitOperatingSystem

Add-ReadinessCheck `
    -Id 'windows-x64' `
    -Class 'required' `
    -Passed $Is64BitOperatingSystem `
    -Summary 'HyperTwist requires a 64-bit Windows operating system.' `
    -Evidence ([pscustomobject][ordered]@{
        architecture = [string]$OperatingSystem.OSArchitecture
        processArchitecture = [string]$env:PROCESSOR_ARCHITECTURE
    })

Add-ReadinessCheck `
    -Id 'windows-supported-build' `
    -Class 'required' `
    -Passed ($WindowsBuild -ge $MinimumWindowsBuild) `
    -Summary "HyperTwist requires Windows build $MinimumWindowsBuild or newer." `
    -Evidence ([pscustomobject][ordered]@{
        caption = [string]$OperatingSystem.Caption
        version = [string]$OperatingSystem.Version
        build = $WindowsBuild
        minimumBuild = $MinimumWindowsBuild
    })

$TotalMemoryBytes = [Int64]$ComputerSystem.TotalPhysicalMemory
Add-ReadinessCheck `
    -Id 'system-memory' `
    -Class 'required' `
    -Passed ($TotalMemoryBytes -ge $MinimumMemoryBytes) `
    -Summary 'At least 8 GiB of physical memory is required for the desktop Alpha.' `
    -Evidence ([pscustomobject][ordered]@{
        totalBytes = $TotalMemoryBytes
        minimumBytes = $MinimumMemoryBytes
    })

$ExistingInstallPathRoot = Get-ExistingPathRoot -Path $TargetInstallRoot
$TargetDriveName = [System.IO.Path]::GetPathRoot($ExistingInstallPathRoot).TrimEnd('\').TrimEnd(':')
$TargetDrive = Get-PSDrive -Name $TargetDriveName
$AvailableFreeSpaceBytes = [Int64]$TargetDrive.Free
Add-ReadinessCheck `
    -Id 'install-volume-free-space' `
    -Class 'required' `
    -Passed ($AvailableFreeSpaceBytes -ge $MinimumFreeSpaceBytes) `
    -Summary 'The target volume must have enough free space for installation and first-run caches.' `
    -Evidence ([pscustomobject][ordered]@{
        targetInstallRoot = [System.IO.Path]::GetFullPath($TargetInstallRoot)
        availableBytes = $AvailableFreeSpaceBytes
        minimumBytes = $MinimumFreeSpaceBytes
    })

$VcRuntime = Get-VcRuntimeEvidence
$VcRuntimeReady = $null -ne $VcRuntime `
    -and $VcRuntime.installed `
    -and [version]$VcRuntime.version -ge $RequiredVcRuntimeVersion
Add-ReadinessCheck `
    -Id 'vc-runtime-x64' `
    -Class 'required' `
    -Passed $VcRuntimeReady `
    -Summary "Microsoft Visual C++ 2015-2022 x64 runtime $RequiredVcRuntimeVersion or newer is required and is bundled by the installer." `
    -Evidence $VcRuntime

$SystemDirectory = [Environment]::SystemDirectory
$RequiredSystemLibraries = @(
    'd3d11.dll',
    'd3d12.dll',
    'dxgi.dll',
    'mf.dll',
    'mfplat.dll',
    'mfreadwrite.dll',
    'xinput1_4.dll'
)
$MissingSystemLibraries = @(
    $RequiredSystemLibraries |
        Where-Object { -not (Test-Path -LiteralPath (Join-Path $SystemDirectory $_)) }
)
Add-ReadinessCheck `
    -Id 'windows-media-and-graphics-libraries' `
    -Class 'required' `
    -Passed ($MissingSystemLibraries.Count -eq 0) `
    -Summary 'Windows DirectX, XInput, and Media Foundation runtime libraries must be available.' `
    -Evidence ([pscustomobject][ordered]@{
        systemDirectory = $SystemDirectory
        requiredLibraries = $RequiredSystemLibraries
        missingLibraries = $MissingSystemLibraries
    })

$VideoControllers = @(
    Get-CimInstance Win32_VideoController -ErrorAction SilentlyContinue |
        ForEach-Object {
            [pscustomobject][ordered]@{
                name = [string]$_.Name
                driverVersion = [string]$_.DriverVersion
                driverDate = if ($null -eq $_.DriverDate) { $null } else { $_.DriverDate.ToString('o') }
                adapterRamBytes = [Int64]$_.AdapterRAM
                status = [string]$_.Status
            }
        }
)
Add-ReadinessCheck `
    -Id 'graphics-adapter' `
    -Class 'required' `
    -Passed ($VideoControllers.Count -gt 0) `
    -Summary 'At least one Windows graphics adapter must be visible.' `
    -Evidence $VideoControllers

$SoundDevices = @(
    Get-CimInstance Win32_SoundDevice -ErrorAction SilentlyContinue |
        ForEach-Object {
            [pscustomobject][ordered]@{
                name = [string]$_.Name
                status = [string]$_.Status
            }
        }
)
Add-ReadinessCheck `
    -Id 'audio-device' `
    -Class 'optional' `
    -Passed ($SoundDevices.Count -gt 0) `
    -Summary 'An audio device is optional for puzzle play and recommended for narration and speech features.' `
    -Evidence $SoundDevices

$OpenXrRegistryPaths = @(
    'HKLM:\SOFTWARE\Khronos\OpenXR\1',
    'HKLM:\SOFTWARE\WOW6432Node\Khronos\OpenXR\1'
)
$OpenXrRuntime = $null
foreach ($OpenXrRegistryPath in $OpenXrRegistryPaths)
{
    if (Test-Path -LiteralPath $OpenXrRegistryPath)
    {
        $OpenXrRuntime = Get-ItemProperty -LiteralPath $OpenXrRegistryPath
        break
    }
}
Add-ReadinessCheck `
    -Id 'openxr-runtime' `
    -Class 'optional' `
    -Passed ($null -ne $OpenXrRuntime -and -not [string]::IsNullOrWhiteSpace([string]$OpenXrRuntime.ActiveRuntime)) `
    -Summary 'An active OpenXR runtime is optional for keyboard/mouse play and required only for live headset sessions.' `
    -Evidence $(if ($null -eq $OpenXrRuntime) {
        $null
    } else {
        [pscustomobject][ordered]@{ activeRuntime = [string]$OpenXrRuntime.ActiveRuntime }
    })

$ResolvedPayloadRoot = $null
if (-not [string]::IsNullOrWhiteSpace($PayloadRoot))
{
    if (-not (Test-Path -LiteralPath $PayloadRoot -PathType Container))
    {
        throw "Payload root '$PayloadRoot' was not found."
    }

    $ResolvedPayloadRoot = (Resolve-Path -LiteralPath $PayloadRoot).Path
    $RequiredPayloadFiles = @(
        [pscustomobject]@{ path = 'UnrealHyperTwist.exe'; size = -1; sha256 = '' },
        [pscustomobject]@{ path = 'UnrealHyperTwist\Binaries\Win64\UnrealHyperTwist-Win64-Shipping.exe'; size = -1; sha256 = '' },
        [pscustomobject]@{ path = 'UnrealHyperTwist\Binaries\Win64\tbb12.dll'; size = -1; sha256 = '' },
        [pscustomobject]@{ path = 'UnrealHyperTwist\Binaries\Win64\tbbmalloc.dll'; size = -1; sha256 = '' },
        [pscustomobject]@{ path = 'UnrealHyperTwist\Content\Paks\UnrealHyperTwist-Windows.pak'; size = -1; sha256 = '' },
        [pscustomobject]@{ path = 'UnrealHyperTwist\Content\Paks\UnrealHyperTwist-Windows.utoc'; size = -1; sha256 = '' },
        [pscustomobject]@{ path = 'UnrealHyperTwist\Content\Paks\UnrealHyperTwist-Windows.ucas'; size = -1; sha256 = '' },
        [pscustomobject]@{ path = 'UnrealHyperTwist\Saved\twophase-ht.tbl'; size = $ExpectedSolverTableSizeBytes; sha256 = $ExpectedSolverTableSha256 },
        [pscustomobject]@{ path = 'Content\Browser\index.html'; size = -1; sha256 = '' },
        [pscustomobject]@{ path = 'Content\Browser\src\browser-runtime-bootstrap.js'; size = -1; sha256 = '' },
        [pscustomobject]@{ path = 'Content\Browser\src\browser-spatial-runtime-fallback.js'; size = -1; sha256 = '' },
        [pscustomobject]@{ path = 'Content\Browser\dist\browser-spatial-runtime.js'; size = -1; sha256 = '' },
        [pscustomobject]@{ path = 'Engine\Binaries\ThirdParty\Windows\XAudio2_9\x64\XAudio2_9Redist.dll'; size = -1; sha256 = '' },
        [pscustomobject]@{ path = 'Engine\Binaries\ThirdParty\OpenXR\win64\openxr_loader.dll'; size = -1; sha256 = '' }
    )

    $PayloadFailures = New-Object System.Collections.ArrayList
    foreach ($RequiredPayloadFile in $RequiredPayloadFiles)
    {
        $RequiredPath = Join-Path $ResolvedPayloadRoot $RequiredPayloadFile.path
        if (-not (Test-Path -LiteralPath $RequiredPath -PathType Leaf))
        {
            [void]$PayloadFailures.Add("missing:$($RequiredPayloadFile.path)")
            continue
        }

        $RequiredItem = Get-Item -LiteralPath $RequiredPath
        if ($RequiredItem.Length -le 0)
        {
            [void]$PayloadFailures.Add("empty:$($RequiredPayloadFile.path)")
            continue
        }

        if ([Int64]$RequiredPayloadFile.size -ge 0 `
            -and $RequiredItem.Length -ne [Int64]$RequiredPayloadFile.size)
        {
            [void]$PayloadFailures.Add("size:$($RequiredPayloadFile.path)")
            continue
        }

        if (-not [string]::IsNullOrWhiteSpace([string]$RequiredPayloadFile.sha256))
        {
            $ActualHash = (Get-FileHash -LiteralPath $RequiredPath -Algorithm SHA256).Hash.ToLowerInvariant()
            if ($ActualHash -ne ([string]$RequiredPayloadFile.sha256).ToLowerInvariant())
            {
                [void]$PayloadFailures.Add("sha256:$($RequiredPayloadFile.path)")
            }
        }
    }

    $CefRuntimeFiles = @(
        Get-ChildItem `
            -LiteralPath (Join-Path $ResolvedPayloadRoot 'Engine\Binaries\ThirdParty\CEF3\Win64') `
            -Filter 'libcef.dll' `
            -File `
            -Recurse `
            -ErrorAction SilentlyContinue
    )
    if ($CefRuntimeFiles.Count -eq 0)
    {
        [void]$PayloadFailures.Add('missing:Engine/Binaries/ThirdParty/CEF3/Win64/*/libcef.dll')
    }

    Add-ReadinessCheck `
        -Id 'required-payload-files' `
        -Class 'required' `
        -Passed ($PayloadFailures.Count -eq 0) `
        -Summary 'The installed payload must contain every required executable, runtime, cooked-data, browser, solver, and OpenXR file.' `
        -Evidence ([pscustomobject][ordered]@{
            payloadRoot = $ResolvedPayloadRoot
            requiredFileCount = $RequiredPayloadFiles.Count + 1
            cefRuntimeFiles = @(
                $CefRuntimeFiles |
                    ForEach-Object {
                        Get-NormalizedRelativePath `
                            -RootPath $ResolvedPayloadRoot `
                            -FilePath $_.FullName
                    }
            )
            failures = @($PayloadFailures)
        })

    $OuterExecutablePath = Join-Path $ResolvedPayloadRoot 'UnrealHyperTwist.exe'
    if (Test-Path -LiteralPath $OuterExecutablePath -PathType Leaf)
    {
        $Signature = Get-AuthenticodeSignature -LiteralPath $OuterExecutablePath
        Add-ReadinessCheck `
            -Id 'desktop-code-signature' `
            -Class 'optional' `
            -Passed ($Signature.Status -eq [System.Management.Automation.SignatureStatus]::Valid) `
            -Summary 'The Alpha payload is currently unsigned; production distribution requires Authenticode signing.' `
            -Evidence ([pscustomobject][ordered]@{
                status = $Signature.Status.ToString()
                signer = if ($null -eq $Signature.SignerCertificate) {
                    $null
                } else {
                    [string]$Signature.SignerCertificate.Subject
                }
            })
    }
}

if (-not [string]::IsNullOrWhiteSpace($ExpectedPayloadManifestPath))
{
    if ($null -eq $ResolvedPayloadRoot)
    {
        throw 'ExpectedPayloadManifestPath requires PayloadRoot.'
    }
    if (-not (Test-Path -LiteralPath $ExpectedPayloadManifestPath -PathType Leaf))
    {
        throw "Payload manifest '$ExpectedPayloadManifestPath' was not found."
    }

    $Manifest = Get-Content -LiteralPath $ExpectedPayloadManifestPath -Raw | ConvertFrom-Json
    $ManifestFailures = New-Object System.Collections.ArrayList
    $SeenManifestPaths = @{}
    $ManifestEntries = @($Manifest.files)
    if ([string]$Manifest.manifestVersion -ne 'ht-windows-installer-payload/v1')
    {
        [void]$ManifestFailures.Add("manifest-version:$($Manifest.manifestVersion)")
    }

    $DeclaredFileCount = 0
    if (-not [int]::TryParse([string]$Manifest.fileCount, [ref]$DeclaredFileCount) `
        -or $DeclaredFileCount -ne $ManifestEntries.Count)
    {
        [void]$ManifestFailures.Add("manifest-file-count:$($Manifest.fileCount)")
    }

    $DeclaredTotalSizeBytes = 0L
    if (-not [Int64]::TryParse(
        [string]$Manifest.totalSizeBytes,
        [ref]$DeclaredTotalSizeBytes
    ) -or $DeclaredTotalSizeBytes -lt 0)
    {
        [void]$ManifestFailures.Add("manifest-total-size:$($Manifest.totalSizeBytes)")
    }

    $ComputedTotalSizeBytes = 0L
    foreach ($ManifestEntry in $ManifestEntries)
    {
        $ManifestRelativePath = [string]$ManifestEntry.relativePath
        $ManifestPathKey = $ManifestRelativePath.Replace('\', '/').ToLowerInvariant()
        if ($SeenManifestPaths.ContainsKey($ManifestPathKey))
        {
            [void]$ManifestFailures.Add("duplicate:$ManifestRelativePath")
            continue
        }
        $SeenManifestPaths[$ManifestPathKey] = $true

        $ManifestEntrySizeBytes = 0L
        if (-not [Int64]::TryParse(
            [string]$ManifestEntry.sizeBytes,
            [ref]$ManifestEntrySizeBytes
        ) -or $ManifestEntrySizeBytes -lt 0)
        {
            [void]$ManifestFailures.Add("invalid-size:$ManifestRelativePath")
            continue
        }
        $ComputedTotalSizeBytes += $ManifestEntrySizeBytes

        $ExpectedManifestHash = ([string]$ManifestEntry.sha256).ToLowerInvariant()
        if ($ExpectedManifestHash -notmatch '^[0-9a-f]{64}$')
        {
            [void]$ManifestFailures.Add("invalid-sha256:$ManifestRelativePath")
            continue
        }

        try
        {
            $ManifestFilePath = Resolve-PayloadRelativeFilePath `
                -RootPath $ResolvedPayloadRoot `
                -RelativePath $ManifestRelativePath
        }
        catch
        {
            [void]$ManifestFailures.Add("invalid-path:$ManifestRelativePath")
            continue
        }

        if (-not (Test-Path -LiteralPath $ManifestFilePath -PathType Leaf))
        {
            [void]$ManifestFailures.Add("missing:$ManifestRelativePath")
            continue
        }

        $ManifestItem = Get-Item -LiteralPath $ManifestFilePath
        if ($ManifestItem.Length -ne $ManifestEntrySizeBytes)
        {
            [void]$ManifestFailures.Add("size:$ManifestRelativePath")
            continue
        }

        if ($VerifyPayloadHashes)
        {
            $ManifestHash = (Get-FileHash -LiteralPath $ManifestFilePath -Algorithm SHA256).Hash.ToLowerInvariant()
            if ($ManifestHash -ne $ExpectedManifestHash)
            {
                [void]$ManifestFailures.Add("sha256:$ManifestRelativePath")
            }
        }
    }
    if ($DeclaredTotalSizeBytes -ne $ComputedTotalSizeBytes)
    {
        [void]$ManifestFailures.Add(
            "manifest-total-size-parity:$DeclaredTotalSizeBytes/$ComputedTotalSizeBytes"
        )
    }

    Add-ReadinessCheck `
        -Id 'payload-manifest-parity' `
        -Class 'required' `
        -Passed ($ManifestFailures.Count -eq 0) `
        -Summary 'The installed payload must match the release manifest without missing, truncated, or altered files.' `
        -Evidence ([pscustomobject][ordered]@{
            manifestPath = (Resolve-Path -LiteralPath $ExpectedPayloadManifestPath).Path
            manifestVersion = [string]$Manifest.manifestVersion
            manifestEntryCount = $ManifestEntries.Count
            declaredFileCount = $DeclaredFileCount
            declaredTotalSizeBytes = $DeclaredTotalSizeBytes
            computedTotalSizeBytes = $ComputedTotalSizeBytes
            hashesVerified = [bool]$VerifyPayloadHashes
            failures = @($ManifestFailures)
        })
}

$RequiredFailures = @(
    $Checks |
        Where-Object { $_.class -eq 'required' -and -not $_.passed }
)
$OptionalWarnings = @(
    $Checks |
        Where-Object { $_.class -eq 'optional' -and -not $_.passed }
)
$Result = if ($RequiredFailures.Count -gt 0) {
    'blocked'
} elseif ($OptionalWarnings.Count -gt 0) {
    'ready-with-warnings'
} else {
    'ready'
}

$Report = [pscustomobject][ordered]@{
    reportVersion = 'ht-windows-readiness/v1'
    generatedAtUtc = [DateTime]::UtcNow.ToString('o')
    result = $Result
    payloadRoot = $ResolvedPayloadRoot
    targetInstallRoot = [System.IO.Path]::GetFullPath($TargetInstallRoot)
    requiredFailureCount = $RequiredFailures.Count
    optionalWarningCount = $OptionalWarnings.Count
    checks = @($Checks)
}

Write-Utf8JsonFile -Path $ReportPath -Value $Report
[Console]::Out.WriteLine((
    "HyperTwist Windows readiness: {0} ({1} required failure(s), {2} optional warning(s))." -f
    $Result,
    $RequiredFailures.Count,
    $OptionalWarnings.Count
))
[Console]::Out.WriteLine("Report: $([System.IO.Path]::GetFullPath($ReportPath))")

if ($PassThru)
{
    return $Report
}

if ($WaitForUser)
{
    [void](Read-Host 'Press Enter to close the HyperTwist readiness report')
}

if ($Result -eq 'blocked')
{
    exit 2
}

exit 0
