param(
    [Parameter(Mandatory=$true)][string]$BinaryPath,
    [Parameter(Mandatory=$true)][string]$EvidenceFile,
    [Parameter(Mandatory=$true)][ValidateSet('Debug','Release')][string]$Configuration
)
$ErrorActionPreference = 'Stop'
function Test-FullyQualifiedPath([string]$Path) {
    if ([string]::IsNullOrWhiteSpace($Path) -or -not [IO.Path]::IsPathRooted($Path)) { return $false }
    $root = [IO.Path]::GetPathRoot($Path)
    return -not [string]::IsNullOrWhiteSpace($root) -and
           ($root.EndsWith([IO.Path]::DirectorySeparatorChar) -or
            $root.EndsWith([IO.Path]::AltDirectorySeparatorChar))
}
if (-not (Test-FullyQualifiedPath $BinaryPath)) { throw 'BinaryPath must be absolute.' }
if (-not (Test-FullyQualifiedPath $EvidenceFile)) { throw 'EvidenceFile must be absolute.' }
$binary = [IO.Path]::GetFullPath($BinaryPath)
$receipt = [IO.Path]::GetFullPath($EvidenceFile)
if (-not (Test-Path -LiteralPath $binary -PathType Leaf)) { throw 'VST3 binary is missing.' }
if (Test-Path -LiteralPath $receipt) { throw 'Preserve previous VST3 evidence; choose a new receipt.' }
$receiptParent = Split-Path -Parent $receipt
if (-not (Test-Path -LiteralPath $receiptParent -PathType Container)) {
    [void](New-Item -ItemType Directory -Path $receiptParent)
}

Add-Type -TypeDefinition @'
using System;
using System.ComponentModel;
using System.Runtime.InteropServices;
public static class DeepPluckVst3FactoryProbe {
    [DllImport("kernel32.dll", CharSet=CharSet.Unicode, SetLastError=true)]
    static extern IntPtr LoadLibraryW(string path);
    [DllImport("kernel32.dll", CharSet=CharSet.Ansi, ExactSpelling=true)]
    static extern IntPtr GetProcAddress(IntPtr module, string name);
    [DllImport("kernel32.dll", SetLastError=true)]
    static extern bool FreeLibrary(IntPtr module);
    [UnmanagedFunctionPointer(CallingConvention.StdCall)]
    [return: MarshalAs(UnmanagedType.I1)] delegate bool Lifecycle();
    [UnmanagedFunctionPointer(CallingConvention.StdCall)] delegate IntPtr Factory();
    [UnmanagedFunctionPointer(CallingConvention.StdCall)] delegate int Count(IntPtr instance);
    [UnmanagedFunctionPointer(CallingConvention.StdCall)] delegate uint Release(IntPtr instance);
    static T Export<T>(IntPtr module, string name) where T : class {
        var address = GetProcAddress(module, name);
        if (address == IntPtr.Zero) throw new InvalidOperationException("Missing export: " + name);
        return Marshal.GetDelegateForFunctionPointer(address, typeof(T)) as T;
    }
    public static int Run(string path) {
        if (IntPtr.Size != 8) throw new InvalidOperationException("Use native x64 PowerShell.");
        var module = LoadLibraryW(path);
        if (module == IntPtr.Zero) throw new Win32Exception(Marshal.GetLastWin32Error());
        bool initialized = false;
        IntPtr factory = IntPtr.Zero;
        try {
            initialized = Export<Lifecycle>(module, "InitDll")();
            if (!initialized) throw new InvalidOperationException("InitDll failed.");
            factory = Export<Factory>(module, "GetPluginFactory")();
            if (factory == IntPtr.Zero) throw new InvalidOperationException("Null plugin factory.");
            var vtable = Marshal.ReadIntPtr(factory);
            var count = (Count) Marshal.GetDelegateForFunctionPointer(
                Marshal.ReadIntPtr(vtable, 4 * IntPtr.Size), typeof(Count));
            return count(factory);
        } finally {
            if (factory != IntPtr.Zero) {
                var vtable = Marshal.ReadIntPtr(factory);
                var release = (Release) Marshal.GetDelegateForFunctionPointer(
                    Marshal.ReadIntPtr(vtable, 2 * IntPtr.Size), typeof(Release));
                release(factory);
            }
            if (initialized && !Export<Lifecycle>(module, "ExitDll")())
                throw new InvalidOperationException("ExitDll failed.");
            if (!FreeLibrary(module)) throw new Win32Exception(Marshal.GetLastWin32Error());
        }
    }
}
'@

$count = [DeepPluckVst3FactoryProbe]::Run($binary)
if ($count -ne 2) { throw "Unexpected VST3 factory class count: $count" }
$record = [ordered]@{
    format = 'DeepPluckVST3FactorySmoke'; schemaVersion = 1
    createdUtc = [DateTime]::UtcNow.ToString('o'); configuration = $Configuration
    binary = $binary; sha256 = (Get-FileHash -LiteralPath $binary -Algorithm SHA256).Hash.ToLowerInvariant()
    fileVersion = (Get-Item -LiteralPath $binary).VersionInfo.FileVersion
    nativeX64 = $true; initDll = $true; factoryClassCount = $count
    factoryReleased = $true; exitDll = $true; moduleFreed = $true; passed = $true
    scope = 'Native x64 module load, InitDll, GetPluginFactory, class count, release, ExitDll and FreeLibrary. No processor, device or GUI instantiation.'
}
[IO.File]::WriteAllText($receipt, ($record | ConvertTo-Json -Depth 5),
                        [Text.UTF8Encoding]::new($false))
Write-Host "PASS: $Configuration VST3 factory initialized with exactly two classes and unloaded cleanly."
