param(
    [string]$PackageRoot = 'C:\HyperTwist\packaged\xr',
    [string[]]$SmokeMaps = @(
        '/Game/HyperTwistTraining/Maps/L_HyperTwist_FollowAlongTraining',
        '/Game/HyperTwistTraining/Maps/L_HyperTwist_ClassicTraining'
    ),
    [double]$ObservationSeconds = 120.0,
    [double]$TimeoutSeconds = 150.0,
    [int]$ResX = 1600,
    [int]$ResY = 900,
    [string]$OpenXrRuntimePathOverride = '',
    [string]$ValidationDirectory = '',
    [string]$AggregateReportPath = '',
    [switch]$KeepRunning
)

$ErrorActionPreference = 'Stop'

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 Read-JsonFile {
    param(
        [Parameter(Mandatory = $true)]
        [string]$Path
    )

    return Get-Content -LiteralPath $Path -Raw | ConvertFrom-Json
}

function Convert-PathToken {
    param(
        [string]$Value
    )

    if ([string]::IsNullOrWhiteSpace($Value))
    {
        return 'unknown'
    }

    return ($Value -replace '[\\/:*?"<>| ?=&]', '_')
}

function Get-JsonPropertyValue {
    param(
        [Parameter(Mandatory = $true)]
        [object]$Object,
        [Parameter(Mandatory = $true)]
        [string[]]$Names,
        [object]$Default = $null
    )

    foreach ($Name in $Names)
    {
        $Property = $Object.PSObject.Properties[$Name]
        if ($null -ne $Property)
        {
            return $Property.Value
        }
    }

    return $Default
}

$ResolvedSmokeMaps =
    @($SmokeMaps) |
    Where-Object { -not [string]::IsNullOrWhiteSpace($_) } |
    Select-Object -Unique

if ($ResolvedSmokeMaps.Count -eq 0)
{
    throw 'Provide at least one XR smoke map.'
}

$LaunchScriptPath = Join-Path (Split-Path -Parent $PSCommandPath) 'Launch-HyperTwistXrPackage.ps1'
if (-not (Test-Path -LiteralPath $LaunchScriptPath))
{
    throw "Launch script was not found at '$LaunchScriptPath'."
}

if (-not (Test-Path -LiteralPath $PackageRoot))
{
    throw "Package root '$PackageRoot' was not found."
}

if ([string]::IsNullOrWhiteSpace($ValidationDirectory))
{
    $ValidationDirectory = Join-Path $PackageRoot 'validation\windowed-live'
}

if ([string]::IsNullOrWhiteSpace($AggregateReportPath))
{
    $AggregateReportPath = Join-Path $ValidationDirectory 'xr-windowed-live-observation-report.json'
}

$AggregateReport = [ordered]@{
    reportVersion = 'ht-xr-windowed-live-observation/v1'
    generatedAtUtc = [DateTime]::UtcNow.ToString('o')
    packageRoot = (Resolve-Path -LiteralPath $PackageRoot).Path
    smokeMaps = @($ResolvedSmokeMaps)
    observationSeconds = $ObservationSeconds
    timeoutSeconds = $TimeoutSeconds
    runtimeLaunchMode = 'windowed'
    resolution = [ordered]@{
        width = $ResX
        height = $ResY
    }
    openXrRuntimePathOverride = $OpenXrRuntimePathOverride
    smokeReports = @()
    preflight = $null
    packagedHeadMountedDisplaySessionObserved = $false
    packagedControllerInputObserved = $false
    remainingGateIds = @()
    result = 'failed'
    error = $null
}

try
{
    foreach ($SmokeMap in $ResolvedSmokeMaps)
    {
        $SmokeReportPath = Join-Path $ValidationDirectory (
            '{0}.json' -f (Convert-PathToken -Value $SmokeMap)
        )

        & $LaunchScriptPath `
            -PackageRoot $PackageRoot `
            -MapAssetPath $SmokeMap `
            -ObservationSeconds $ObservationSeconds `
            -TimeoutSeconds $TimeoutSeconds `
            -RuntimeLaunchMode 'windowed' `
            -ResX $ResX `
            -ResY $ResY `
            -OpenXrRuntimePathOverride $OpenXrRuntimePathOverride `
            -ReportPath $SmokeReportPath `
            -KeepRunning:$KeepRunning

        if (-not (Test-Path -LiteralPath $SmokeReportPath))
        {
            throw "Windowed XR smoke report '$SmokeReportPath' was not written for '$SmokeMap'."
        }

        $SmokeReport = Read-JsonFile -Path $SmokeReportPath
        if ($null -eq $SmokeReport)
        {
            throw "Windowed XR smoke report '$SmokeReportPath' could not be parsed."
        }

        $AggregateReport.smokeReports += $SmokeReport

        if ($null -eq $AggregateReport.preflight)
        {
            $AggregateReport.preflight = Get-JsonPropertyValue `
                -Object $SmokeReport `
                -Names @('preflight', 'Preflight') `
                -Default $null
        }

        $RuntimeReport = Get-JsonPropertyValue `
            -Object $SmokeReport `
            -Names @('runtimeReport', 'RuntimeReport') `
            -Default $null

        if ($null -ne $RuntimeReport)
        {
            $AggregateReport.packagedHeadMountedDisplaySessionObserved =
                $AggregateReport.packagedHeadMountedDisplaySessionObserved -or [bool](Get-JsonPropertyValue `
                    -Object $RuntimeReport `
                    -Names @('bObservedHeadMountedDisplaySession', 'ObservedHeadMountedDisplaySession') `
                    -Default $false)
            $AggregateReport.packagedControllerInputObserved =
                $AggregateReport.packagedControllerInputObserved -or [bool](Get-JsonPropertyValue `
                    -Object $RuntimeReport `
                    -Names @('bObservedControllerInputActivity', 'ObservedControllerInputActivity') `
                    -Default $false)

            foreach ($RemainingGateId in @((Get-JsonPropertyValue `
                -Object $RuntimeReport `
                -Names @('remainingGateIds', 'RemainingGateIds') `
                -Default @())))
            {
                if (-not [string]::IsNullOrWhiteSpace($RemainingGateId))
                {
                    $AggregateReport.remainingGateIds += $RemainingGateId
                }
            }
        }
    }

    $AggregateReport.remainingGateIds =
        @($AggregateReport.remainingGateIds | Select-Object -Unique)

    if ($AggregateReport.packagedHeadMountedDisplaySessionObserved `
        -and $AggregateReport.packagedControllerInputObserved)
    {
        $AggregateReport.result = 'passed'
    }
    else
    {
        $AggregateReport.result = 'pending-live-observation'
    }
}
catch
{
    if ($null -ne $SmokeReportPath -and (Test-Path -LiteralPath $SmokeReportPath))
    {
        $FailedSmokeReport = Read-JsonFile -Path $SmokeReportPath
        if ($null -ne $FailedSmokeReport)
        {
            $AggregateReport.smokeReports += $FailedSmokeReport
            if ($null -eq $AggregateReport.preflight)
            {
                $AggregateReport.preflight = Get-JsonPropertyValue `
                    -Object $FailedSmokeReport `
                    -Names @('preflight', 'Preflight') `
                    -Default $null
            }
        }
    }

    $AggregateReport.error = $_.Exception.Message
    if ($AggregateReport.remainingGateIds.Count -gt 0)
    {
        $AggregateReport.remainingGateIds =
            @($AggregateReport.remainingGateIds | Select-Object -Unique)
    }

    Write-Utf8JsonFile -Path $AggregateReportPath -Value $AggregateReport
    throw
}

Write-Utf8JsonFile -Path $AggregateReportPath -Value $AggregateReport
