param(
    [Parameter(Mandatory = $true)] [string]$ResultDirectory,
    [Parameter(Mandatory = $true)] [string]$RunId,
    [Parameter(Mandatory = $true)] [string]$Scale,
    [Parameter(Mandatory = $true)] [string]$Profiles
)

$ErrorActionPreference = 'Stop'
$result = if ([IO.Path]::IsPathRooted($ResultDirectory)) {
    [IO.Path]::GetFullPath($ResultDirectory)
} else {
    [IO.Path]::GetFullPath((Join-Path (Get-Location) $ResultDirectory))
}
New-Item -ItemType Directory -Force -Path $result | Out-Null
$cpu = (Get-ItemProperty 'HKLM:\HARDWARE\DESCRIPTION\System\CentralProcessor\0').ProcessorNameString
$windows = Get-ItemProperty 'HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion'
$compilerFile = Get-ChildItem 'out\build\msvc-baseline\CMakeFiles' -Recurse `
    -Filter 'CMakeCXXCompiler.cmake' -File | Select-Object -First 1
$compilerText = if ($compilerFile) { Get-Content -Raw $compilerFile.FullName } else { '' }
$compilerVersion = if ($compilerText -match 'CMAKE_CXX_COMPILER_VERSION "([^"]+)"') {
    $Matches[1]
} else { 'unknown' }
$cache = Get-Content -Raw 'out\build\msvc-baseline\CMakeCache.txt'
$cmakePath = if ($cache -match '(?m)^CMAKE_COMMAND:INTERNAL=(.+)$') { $Matches[1].Trim() } else { '' }
$cmakeVersion = if ($cmakePath -and (Test-Path -LiteralPath $cmakePath)) {
    (& $cmakePath --version | Select-Object -First 1)
} else { 'unknown' }
$gitHead = try { (git rev-parse --verify HEAD 2>$null) } catch { '' }
$gitDirty = try { @((git status --porcelain 2>$null)).Count } catch { -1 }
$projectRoot = (Get-Location).Path
$sourceManifestName = 'source-manifest.sha256'
$binaryManifestName = 'binary-manifest.sha256'

$metadata = [ordered]@{
    run_id = $RunId
    timestamp = (Get-Date).ToString('o')
    scale = $Scale
    profiles = @($Profiles -split ' ' | Where-Object { $_ })
    cpu = $cpu.Trim()
    logical_processors = [Environment]::ProcessorCount
    windows = "$($windows.ProductName) $($windows.DisplayVersion) build $($windows.CurrentBuildNumber)"
    power_plan = ((powercfg /getactivescheme) -join ' ').Trim()
    compiler = "MSVC $compilerVersion x64"
    cmake = $cmakeVersion
    release_runtime = '/MT static'
    ipo_lto = 'off'
    scalar_flags = '/O2 /d2Qvec- /Qvec-report:1 /Oi-, no /arch:AVX*'
    baseline_flags = '/O2, no /Qvec disable, no /arch:AVX*'
    avx2_flags = '/O2 /arch:AVX2'
    git_head = $gitHead
    git_dirty_entries = $gitDirty
    source_manifest = $sourceManifestName
    binary_manifest = $binaryManifestName
}

$metadata | ConvertTo-Json -Depth 4 | Set-Content -LiteralPath (Join-Path $result 'environment.json') -Encoding utf8
$metadata.GetEnumerator() | ForEach-Object {
    $value = if ($_.Value -is [array]) { $_.Value -join ',' } else { $_.Value }
    "$($_.Key)=$value"
} | Set-Content -LiteralPath (Join-Path $result 'environment.txt') -Encoding utf8

# A manifest makes an uncommitted experimental run identifiable even before the
# repository has its first HEAD.  Results/out are intentionally excluded from
# the source set; generated executables receive their own manifest.
$sourceRoots = @(
    '.gitignore', 'CMakeLists.txt', 'CMakePresets.json', 'README.md',
    'build.bat', 'benchmark.bat', 'large_benchmark.bat',
    'include', 'src', 'tests', 'tools', 'docs'
)
$sourceFiles = @()
foreach ($entry in $sourceRoots) {
    if (Test-Path -LiteralPath $entry -PathType Leaf) {
        $sourceFiles += Get-Item -LiteralPath $entry
    } elseif (Test-Path -LiteralPath $entry -PathType Container) {
        $sourceFiles += Get-ChildItem -LiteralPath $entry -Recurse -File
    }
}
$sourceLines = @($sourceFiles | Sort-Object FullName -Unique | ForEach-Object {
    $relative = $_.FullName.Substring($projectRoot.Length).TrimStart('\') -replace '\\', '/'
    $hash = (Get-FileHash -LiteralPath $_.FullName -Algorithm SHA256).Hash.ToLowerInvariant()
    "$hash  $relative"
})
$sourceLines | Set-Content -LiteralPath (Join-Path $result $sourceManifestName) -Encoding ascii

$binaryFiles = @()
foreach ($profile in @($Profiles -split ' ' | Where-Object { $_ })) {
    $profileDirectory = Join-Path $projectRoot "out\bin\$profile\Release"
    if (Test-Path -LiteralPath $profileDirectory) {
        $binaryFiles += Get-ChildItem -LiteralPath $profileDirectory -File
    }
}
$binaryLines = @($binaryFiles | Sort-Object FullName -Unique | ForEach-Object {
    $relative = $_.FullName.Substring($projectRoot.Length).TrimStart('\') -replace '\\', '/'
    $hash = (Get-FileHash -LiteralPath $_.FullName -Algorithm SHA256).Hash.ToLowerInvariant()
    "$hash  $relative"
})
$binaryLines | Set-Content -LiteralPath (Join-Path $result $binaryManifestName) -Encoding ascii
Write-Host "Wrote benchmark environment metadata to $result"
