param(
    [Parameter(Mandatory=$true)][ValidateSet('configure','build','test','versions')][string] $Mode,
    [ValidateSet('Debug','Release')][string] $Configuration = 'Release',
    [string] $BuildDir = 'build',
    [Parameter(Mandatory=$true)][string] $EvidenceRoot,
    [string] $BenchmarkReference = '',
    [string[]] $Targets = @(),
    [switch] $Fresh,
    [switch] $PathValidationOnly
)
$ErrorActionPreference = 'Stop'
$projectRoot = [IO.Path]::GetFullPath((Join-Path $PSScriptRoot '..'))
Set-Location -LiteralPath $projectRoot
$buildPath = [IO.Path]::GetFullPath((Join-Path $projectRoot $BuildDir))
$evidencePath = [IO.Path]::GetFullPath($EvidenceRoot)
if (-not $buildPath.StartsWith($projectRoot + '\', [StringComparison]::OrdinalIgnoreCase)) { throw 'Build tree must remain below the repository.' }
function Test-AllowedEvidencePath([string]$Path) {
    foreach ($version in @('V0.12.0','V0.12.5','V0.12.75','V0.13.0')) {
        $root = [IO.Path]::GetFullPath((Join-Path $projectRoot (Join-Path 'Artifacts' $version))) + '\'
        if ($Path.StartsWith($root, [StringComparison]::OrdinalIgnoreCase)) { return $true }
    }
    return $false
}
if (-not (Test-AllowedEvidencePath $evidencePath)) { throw 'Evidence must remain below repository Artifacts/V0.12.0, Artifacts/V0.12.5, Artifacts/V0.12.75, or Artifacts/V0.13.0.' }
if ($PathValidationOnly) { Write-Host 'PASS: bounded evidence path accepted.'; exit 0 }
New-Item -ItemType Directory -Path $evidencePath -Force | Out-Null
$runName = $Mode + '-' + $Configuration + '-' + [DateTime]::UtcNow.ToString('yyyyMMddTHHmmssfffZ')
$logPath = Join-Path $evidencePath ($runName + '.log')
$vswhere = 'C:/Program Files (x86)/Microsoft Visual Studio/Installer/vswhere.exe'
if (-not (Test-Path -LiteralPath $vswhere)) { throw 'The confirmed VS locator is missing; do not install a substitute.' }
$installationJson = @(& $vswhere -products '*' -requires Microsoft.VisualStudio.Component.VC.Tools.x86.x64 -format json)
if ($LASTEXITCODE -ne 0) { throw 'vswhere failed.' }
# Windows PowerShell 5.1 can emit a JSON array as one pipeline object. Assign
# the parsed array directly before filtering so installations stay separate.
$installations = ConvertFrom-Json -InputObject ($installationJson -join "`n")
$installation = @($installations | Where-Object {$_.catalog.productLineVersion -eq '18'})
if ($installation.Count -ne 1) { throw 'Expected exactly one confirmed Visual Studio 2026 installation.' }
$launcher = Join-Path $installation[0].installationPath 'Common7/Tools/Launch-VsDevShell.ps1'
& $launcher -Arch amd64 -HostArch amd64 -SkipAutomaticLocation -NoLogo
# VS can set only CMAKE_GENERATOR_PLATFORM. Set the matching generator in this
# child process too, so CMake's nested build helpers do not warn on every target.
$env:CMAKE_GENERATOR='Visual Studio 18 2026'
$env:CMAKE_GENERATOR_PLATFORM='x64'
if ($env:VSCMD_ARG_TGT_ARCH -ne 'x64') { throw 'x64 Developer Environment was not initialized.' }
$compiler = @(& where.exe cl)
if ($LASTEXITCODE -ne 0) { throw 'cl missing from initialized environment.' }
# cl writes its informational banner to stderr and exits nonzero without a
# source file. Merge inside the native shell so PowerShell 5.1 does not turn
# that expected banner into a terminating NativeCommandError.
$compilerBanner = (& cmd.exe /d /c 'cl 2>&1' | Out-String)
if ($compilerBanner -notmatch '19\.51\.36256') { throw "Unconfirmed compiler: $compilerBanner" }
$cmakeVersion = (& cmake --version | Out-String)
if ($LASTEXITCODE -ne 0 -or $cmakeVersion -notmatch '4\.4\.3') { throw 'Unconfirmed CMake version.' }
if ((& cmake --help | Out-String) -notmatch 'Visual Studio 18 2026') { throw 'Confirmed generator missing.' }
if ($env:WindowsSDKVersion.TrimEnd('\') -ne '10.0.26100.0') { throw 'Unconfirmed Windows SDK.' }
if ($env:VCToolsVersion.TrimEnd('\') -ne '14.51.36231') { throw 'Unconfirmed VCTools.' }
$sourceVersion = [regex]::Match((Get-Content -Raw -LiteralPath 'CMakeLists.txt'), 'project\(DeepPluck VERSION ([0-9.]+)').Groups[1].Value
$cachePath = Join-Path $buildPath 'CMakeCache.txt'
$referenceLine = @()
if (Test-Path -LiteralPath $cachePath) {
    $referenceLine = @(Get-Content -LiteralPath $cachePath |
        Where-Object {$_ -like 'DEEPPLUCK_BENCHMARK_REFERENCE:PATH=*'})
}
if ($referenceLine.Count -eq 1) {
    $reference = $referenceLine[0].Substring($referenceLine[0].IndexOf('=') + 1)
} elseif (-not [string]::IsNullOrWhiteSpace($BenchmarkReference)) {
    $reference = [IO.Path]::GetFullPath($BenchmarkReference)
} else {
    throw 'Read and preserve the established CPU audio reference before configuring.'
}
if (-not (Test-Path -LiteralPath $reference -PathType Container)) { throw 'Preserved CPU audio reference is missing.' }
$toolchain = [ordered]@{mode=$Mode;configuration=$Configuration;sourceVersion=$sourceVersion;vs=$installation[0].installationVersion;compiler=$compiler;banner=$compilerBanner.Trim();cmake=$cmakeVersion.Trim();sdk=$env:WindowsSDKVersion;tools=$env:VCToolsVersion;reference=$reference;build=$buildPath}
[IO.File]::WriteAllText((Join-Path $evidencePath ($runName + '-environment.json')), ($toolchain | ConvertTo-Json -Depth 5), [Text.UTF8Encoding]::new($false))
$stopwatch = [Diagnostics.Stopwatch]::StartNew()
switch ($Mode) {
    'configure' {
        $arguments = @('-S','.', '-B',$buildPath,'-G','Visual Studio 18 2026','-A','x64',"-DDEEPPLUCK_BENCHMARK_REFERENCE:PATH=$reference",'-DDEEPPLUCK_PROFILE_SYMBOLS=OFF')
        if ($Fresh) { $arguments = @('--fresh') + $arguments }
        & cmake @arguments 2>&1 | Tee-Object -FilePath $logPath
        $nativeExit = $LASTEXITCODE
    }
    'build' {
        $arguments = @('--build',$buildPath,'--config',$Configuration,'--parallel')
        if ($Targets.Count -gt 0) { $arguments += @('--target') + $Targets }
        & cmake @arguments 2>&1 | Tee-Object -FilePath $logPath
        $nativeExit = $LASTEXITCODE
    }
    'test' {
        & ctest --test-dir $buildPath -C $Configuration --output-on-failure 2>&1 | Tee-Object -FilePath $logPath
        $nativeExit = $LASTEXITCODE
    }
    'versions' {
        & powershell.exe -NoProfile -ExecutionPolicy Bypass -File Tests/VerifyArtifacts.ps1 -ExpectedVersion $sourceVersion -SourceDir $projectRoot -BuildDir $buildPath -Configuration $Configuration 2>&1 | Tee-Object -FilePath $logPath
        $nativeExit = $LASTEXITCODE
        if($nativeExit -eq 0){
            & powershell.exe -NoProfile -ExecutionPolicy Bypass -File Tests/VerifyArtifacts.ps1 -ExpectedVersion $sourceVersion -SourceDir $projectRoot -BuildDir $buildPath -Configuration $Configuration -SelfTest 2>&1 | Tee-Object -FilePath $logPath -Append
            $nativeExit = $LASTEXITCODE
        }
    }
}
$stopwatch.Stop()
[IO.File]::WriteAllText((Join-Path $evidencePath ($runName + '-result.json')), ([ordered]@{nativeExit=$nativeExit;seconds=$stopwatch.Elapsed.TotalSeconds;log=[IO.Path]::GetFileName($logPath)} | ConvertTo-Json), [Text.UTF8Encoding]::new($false))
exit $nativeExit
