param(
    [Parameter(Mandatory=$true)][string]$SourceDir,
    [Parameter(Mandatory=$true)][string]$BuildDir,
    [Parameter(Mandatory=$true)][string]$OutputRoot,
    [Parameter(Mandatory=$true)][string]$ExpectedVersion,
    [switch]$PathValidationOnly
)

$ErrorActionPreference = 'Stop'
$source = [IO.Path]::GetFullPath($SourceDir)
$build = [IO.Path]::GetFullPath($BuildDir)
$output = [IO.Path]::GetFullPath($OutputRoot)
$allowedRoot = [IO.Path]::GetFullPath((Join-Path $source 'Artifacts\V0.13.0'))

if (-not $build.StartsWith($source + '\', [StringComparison]::OrdinalIgnoreCase)) {
    throw 'Build tree must remain below the repository.'
}
if (-not $output.StartsWith($allowedRoot + '\', [StringComparison]::OrdinalIgnoreCase)) {
    throw 'Release-candidate packaging must remain below repository Artifacts/V0.13.0.'
}
if ($ExpectedVersion -notmatch '^\d+\.\d+\.\d+$') {
    throw 'ExpectedVersion must have exactly three canonical components.'
}
if ($PathValidationOnly) {
    Write-Host 'PASS: bounded V0.13.0 release-package path accepted.'
    exit 0
}
if (Test-Path -LiteralPath $output) {
    throw 'Preserve previous package evidence; choose a new output root.'
}

$cmakeText = [IO.File]::ReadAllText((Join-Path $source 'CMakeLists.txt'))
if ($cmakeText -notmatch ('project\(DeepPluck VERSION ' + [regex]::Escape($ExpectedVersion) + ' LANGUAGES')) {
    throw 'Source project version does not match ExpectedVersion.'
}

$verifyArtifacts = Join-Path $source 'Tests\VerifyArtifacts.ps1'
& $verifyArtifacts -ExpectedVersion $ExpectedVersion -SourceDir $source -BuildDir $build -Configuration Release
if (-not $?) {
    throw 'Release artifacts failed the existing version and identity contract.'
}

$buildRoot = Join-Path $build 'DeepPluck_artefacts\Release'
$sourceVst = Join-Path $buildRoot 'VST3\DeepPluck.vst3'
$sourceExe = Join-Path $buildRoot 'Standalone\DeepPluck.exe'
if (-not (Test-Path -LiteralPath $sourceVst -PathType Container)) {
    throw 'Release VST3 bundle is missing.'
}
if (-not (Test-Path -LiteralPath $sourceExe -PathType Leaf)) {
    throw 'Release Standalone executable is missing.'
}

[void](New-Item -ItemType Directory -Path $output)
$packageRoot = Join-Path $output 'package'
[void](New-Item -ItemType Directory -Path $packageRoot)
$packageVst = Join-Path $packageRoot 'DeepPluck.vst3'
$packageExe = Join-Path $packageRoot 'DeepPluck.exe'
Copy-Item -LiteralPath $sourceVst -Destination $packageVst -Recurse
Copy-Item -LiteralPath $sourceExe -Destination $packageExe

$forbiddenExtensions = @(
    '.pdb', '.ilk', '.lib', '.exp', '.obj', '.log', '.csv', '.etl',
    '.wav', '.f32', '.cpp', '.h', '.ps1', '.bat', '.cmd'
)
$entries = [Collections.Generic.List[object]]::new()
$files = @(Get-ChildItem -LiteralPath $packageRoot -Recurse -File | Sort-Object FullName)
if ($files.Count -lt 3) {
    throw 'Release package is incomplete.'
}

foreach ($file in $files) {
    $relative = $file.FullName.Substring($packageRoot.Length + 1)
    $safeRelative = $relative.Replace('\', '/')
    $allowed = $relative -ceq 'DeepPluck.exe' -or $relative.StartsWith('DeepPluck.vst3\', [StringComparison]::Ordinal)
    if (-not $allowed) {
        throw "Unexpected package path: $relative"
    }
    if ($forbiddenExtensions -contains $file.Extension.ToLowerInvariant()) {
        throw "Developer file is forbidden in the package: $relative"
    }
    if ($relative -ceq 'DeepPluck.exe') {
        $sourceFile = $sourceExe
    } else {
        $sourceFile = Join-Path $sourceVst $relative.Substring('DeepPluck.vst3\'.Length)
    }
    if (-not (Test-Path -LiteralPath $sourceFile -PathType Leaf)) {
        throw "Package file has no Release-build source: $relative"
    }
    $sourceHash = (Get-FileHash -LiteralPath $sourceFile -Algorithm SHA256).Hash.ToLowerInvariant()
    $packageHash = (Get-FileHash -LiteralPath $file.FullName -Algorithm SHA256).Hash.ToLowerInvariant()
    if ($sourceHash -cne $packageHash) {
        throw "Package copy differs from Release-build source: $relative"
    }
    $entries.Add([pscustomobject][ordered]@{
        path = $safeRelative
        bytes = [int64]$file.Length
        sha256 = $packageHash
    })
}

$exeVersion = (Get-Item -LiteralPath $packageExe).VersionInfo.FileVersion
$vstBinary = Join-Path $packageVst 'Contents\x86_64-win\DeepPluck.vst3'
$vstVersion = (Get-Item -LiteralPath $vstBinary).VersionInfo.FileVersion
if ($exeVersion -cne $ExpectedVersion -or $vstVersion -cne $ExpectedVersion) {
    throw "Packaged binary version mismatch: Standalone=$exeVersion, VST3=$vstVersion."
}

$manifest = [ordered]@{
    format = 'DeepPluckReleaseCandidatePackage'
    schemaVersion = 1
    product = 'DeepPluck'
    productVersion = $ExpectedVersion
    configuration = 'Release'
    createdUtc = [DateTime]::UtcNow.ToString('o')
    packageDirectory = 'package'
    allowedTopLevel = @('DeepPluck.vst3', 'DeepPluck.exe')
    excluded = @(
        'Debug binaries', 'candidate presets', 'benchmarks', 'profiles',
        'developer tools', 'test data', 'logs', 'private paths', 'source files'
    )
    entries = @($entries)
}
$manifestPath = Join-Path $output 'package-manifest.json'
[IO.File]::WriteAllText($manifestPath, ($manifest | ConvertTo-Json -Depth 8), [Text.UTF8Encoding]::new($false))

Write-Host ("PASS: Release-candidate package contains {0} allowlisted files; VST3 and Standalone report {1}. Manifest: {2}" -f $entries.Count, $ExpectedVersion, $manifestPath)
