param(
    [Parameter(Mandatory = $true)]
    [string]$CompareCellMedianFile,

    [Parameter(Mandatory = $true)]
    [string]$AdaptCellMedianFile
)

$ErrorActionPreference = 'Stop'

function Convert-ToDouble([string]$Value, [string]$Column, [string]$Path) {
    $number = 0.0
    $style = [Globalization.NumberStyles]::Float
    if ([double]::TryParse($Value, $style,
            [Globalization.CultureInfo]::InvariantCulture, [ref]$number)) {
        return $number
    }
    if ([double]::TryParse($Value, $style,
            [Globalization.CultureInfo]::CurrentCulture, [ref]$number)) {
        return $number
    }
    throw "Cannot parse '$Value' as $Column in $Path"
}

function Test-SelectedDeferred([string]$Structure) {
    $base = ($Structure -split '@', 2)[0]
    return $base -eq 'adaptive_shape_deferred' `
        -or $base -eq 'adaptive_shape_default_deferred' `
        -or $base -eq 'adaptive_shape_moderate_deferred'
}

function Test-CellFile([string]$Path, [string]$Suite) {
    $resolved = (Resolve-Path -LiteralPath $Path).Path
    $rows = @(Import-Csv -LiteralPath $resolved | Where-Object {
        Test-SelectedDeferred $_.structure
    })
    if ($rows.Count -eq 0) {
        throw "No selected deferred/default/moderate adaptive rows in $resolved"
    }
    $requiredWorkloads = if ($Suite -eq 'adapt') {
        @('short_burst_negative_control', 'phase_10_uniform_10_local_80_read')
    } else {
        @('bursty_edit', 'phase_uniform_local_read')
    }
    foreach ($workload in $requiredWorkloads) {
        if (-not @($rows | Where-Object { $_.workload -eq $workload }).Count) {
            throw "Missing selected $Suite workload '$workload' in $resolved"
        }
    }

    $failures = @()
    foreach ($row in $rows) {
        $switches = Convert-ToDouble $row.median_switches 'median_switches' $resolved
        $leafRebuilds = Convert-ToDouble `
            $row.median_leaf_rebuilds 'median_leaf_rebuilds' $resolved
        $maxSwitches = 1.0
        $maxLeafRebuilds = 1.0
        $requireVector = $false
        $class = 'stationary'

        if ($row.workload -eq 'short_burst_negative_control') {
            $maxSwitches = 0.0
            $maxLeafRebuilds = 0.0
            $requireVector = $true
            $class = 'short-negative'
        } elseif ($Suite -eq 'adapt' `
                  -and $row.workload -eq 'phase_10_uniform_10_local_80_read') {
            $maxSwitches = 2.0
            # The trace deliberately contains two different edit phases.  One
            # stable shape choice per phase is useful adaptation, not churn.
            $maxLeafRebuilds = 2.0
            # Returning to vector is optional: for wide/non-trivial values the
            # measured O(N) conversion can cost more than the remaining read
            # phase.  The policy must avoid churn, not force an uneconomic
            # second transition.
            $class = 'adapt-exact-phase'
        } elseif ($Suite -eq 'compare' `
                  -and $row.workload -eq 'phase_uniform_local_read') {
            $maxSwitches = 2.0
            $maxLeafRebuilds = 2.0
            # The shorter compare phase, especially at smoke scale, may not
            # provide enough read evidence to amortize a return to vector.
            $class = 'compare-phase'
        } elseif ($Suite -eq 'compare' -and $row.workload -eq 'bursty_edit') {
            $maxSwitches = 2.0
            $maxLeafRebuilds = 1.0
            # As above, final tiered is valid when return conversion does not
            # amortize within the observed read tail.
            $class = 'bursty'
        }

        $modeFailure = $requireVector -and $row.final_mode -ne 'vector'
        if ($switches -gt $maxSwitches `
            -or $leafRebuilds -gt $maxLeafRebuilds `
            -or $modeFailure) {
            $failures += [pscustomobject]@{
                suite = $Suite
                class = $class
                profile = $row.profile
                type = $row.type
                workload = $row.workload
                n = $row.n
                structure = $row.structure
                switches = $switches
                switch_limit = $maxSwitches
                leaf_rebuilds = $leafRebuilds
                leaf_rebuild_limit = $maxLeafRebuilds
                final_mode = $row.final_mode
                required_mode = if ($requireVector) { 'vector' } else { '*' }
            }
        }
    }

    [pscustomobject]@{
        path = $resolved
        checked = $rows.Count
        failures = $failures
    }
}

$compare = Test-CellFile $CompareCellMedianFile 'compare'
$adapt = Test-CellFile $AdaptCellMedianFile 'adapt'
$failures = @($compare.failures) + @($adapt.failures)

if ($failures.Count -ne 0) {
    foreach ($failure in $failures) {
        $message = ("FAIL {0}/{1}: {2} {3}/{4}/N={5}; " +
            "switches={6} (limit {7}), leaf_rebuilds={8} (limit {9}), " +
            "final_mode={10} (required {11})") -f
            $failure.suite, $failure.class, $failure.structure,
            $failure.type, $failure.workload, $failure.n,
            $failure.switches, $failure.switch_limit,
            $failure.leaf_rebuilds, $failure.leaf_rebuild_limit,
            $failure.final_mode, $failure.required_mode
        Write-Host $message
    }
    throw "Adaptation validation failed in $($failures.Count) selected cell(s)"
}

Write-Host "Adaptation validation passed: $($compare.checked) compare and $($adapt.checked) adapt cells."
