param(
    [Parameter(Mandatory=$true)][string]$Executable,
    [Parameter(Mandatory=$true)][string]$Evidence,
    [string]$WindowTitle='DeepPluck',
    [string[]]$Arguments=@(),
    [int]$ExpectedEditorWidth=0,
    [int]$ExpectedEditorHeight=0
)
$ErrorActionPreference='Stop'
$exe=(Resolve-Path -LiteralPath $Executable).Path
$receipt=[IO.Path]::GetFullPath($Evidence)
if(Test-Path -LiteralPath $receipt){throw 'Preserve the prior smoke receipt; choose a new path.'}
Add-Type -TypeDefinition @'
using System;
using System.Runtime.InteropServices;
using System.Text;
public static class DeepPluckSmokeWindow {
    public delegate bool Callback(IntPtr handle, IntPtr state);
    [DllImport("user32.dll")] static extern bool EnumWindows(Callback cb, IntPtr state);
    [DllImport("user32.dll")] static extern uint GetWindowThreadProcessId(IntPtr h, out uint id);
    [DllImport("user32.dll", CharSet=CharSet.Unicode)] static extern int GetWindowText(IntPtr h, StringBuilder text, int capacity);
    [StructLayout(LayoutKind.Sequential)] struct Rect { public int left, top, right, bottom; }
    [DllImport("user32.dll")] static extern bool GetWindowRect(IntPtr h, out Rect rect);
    [DllImport("user32.dll")] static extern bool GetClientRect(IntPtr h, out Rect rect);
    [DllImport("user32.dll", SetLastError=true)] public static extern bool PostMessage(IntPtr h, uint msg, IntPtr w, IntPtr l);
    public static IntPtr Find(int processId, string expectedTitle) {
        IntPtr found=IntPtr.Zero;
        EnumWindows((h,s)=>{
            uint id; GetWindowThreadProcessId(h,out id);
            if(id!=(uint)processId) return true;
            var title=new StringBuilder(512); GetWindowText(h,title,title.Capacity);
            Rect rect; GetWindowRect(h,out rect);
            // Exclude JUCE's hidden message/helper windows bearing the app name.
            if(title.ToString().Equals(expectedTitle,StringComparison.OrdinalIgnoreCase)
                && rect.right-rect.left>100 && rect.bottom-rect.top>100) { found=h; return false; }
            return true;
        },IntPtr.Zero);
        return found;
    }
    public static int[] ClientSize(IntPtr handle) {
        Rect rect;
        if(!GetClientRect(handle,out rect)) return new int[] { -1, -1 };
        return new int[] { rect.right-rect.left, rect.bottom-rect.top };
    }
}
'@
$record=[ordered]@{executable=$exe;sha256=(Get-FileHash -LiteralPath $exe).Hash;arguments=$Arguments;windowTitle=$WindowTitle;startedUtc=[DateTime]::UtcNow.ToString('o');pid=$null;nativeWindow=$false;inputIdle=$false;windowRecreatedDuringStartup=$false;clientWidth=$null;clientHeight=$null;expectedEditorWidth=$ExpectedEditorWidth;expectedEditorHeight=$ExpectedEditorHeight;normalCloseRequested=$false;postMessageError=$null;exitCode=$null;timeout=$false;forcedCleanup=$false;remainingProcess=$null;passed=$false}
$process=$null
try {
    $start=@{FilePath=$exe;WorkingDirectory=(Split-Path -Parent $exe);WindowStyle='Hidden';PassThru=$true}
    if($Arguments.Count -gt 0){$start.ArgumentList=$Arguments}
    $process=Start-Process @start
    $record.pid=$process.Id
    $watch=[Diagnostics.Stopwatch]::StartNew()
    $handle=[IntPtr]::Zero
    while($watch.Elapsed.TotalSeconds -lt 20 -and -not $process.HasExited){
        $handle=[DeepPluckSmokeWindow]::Find($process.Id,$WindowTitle)
        if($handle -ne [IntPtr]::Zero){break}
        Start-Sleep -Milliseconds 100
    }
    if($handle -eq [IntPtr]::Zero){$record.timeout=-not $process.HasExited; throw 'No native DeepPluck window appeared.'}
    $record.nativeWindow=$true
    $record.inputIdle=$process.WaitForInputIdle(5000)
    if(-not $record.inputIdle){$record.timeout=$true; throw 'Window did not finish startup.'}
    # JUCE may recreate its native peer when enabling the native title bar.
    # Never send WM_CLOSE to the earlier, now-destroyed startup handle.
    $startupHandle=$handle
    $handle=[DeepPluckSmokeWindow]::Find($process.Id,$WindowTitle)
    $record.windowRecreatedDuringStartup=($handle -ne $startupHandle)
    if($handle -eq [IntPtr]::Zero){throw 'No matching native window after startup.'}
    $clientSize=[DeepPluckSmokeWindow]::ClientSize($handle)
    $record.clientWidth=$clientSize[0]
    $record.clientHeight=$clientSize[1]
    # JUCE's Standalone DocumentWindow contributes its own in-client border and
    # title bar around the exact editor component. Bound that known wrapper
    # chrome while the component's exact size is asserted by the C++ GUI test.
    if($ExpectedEditorWidth -gt 0 -and
       ($record.clientWidth -lt $ExpectedEditorWidth -or
        $record.clientWidth -gt $ExpectedEditorWidth + 8)){
        throw "Unexpected Standalone client width $($record.clientWidth) for editor width $ExpectedEditorWidth."
    }
    if($ExpectedEditorHeight -gt 0 -and
       ($record.clientHeight -lt $ExpectedEditorHeight -or
        $record.clientHeight -gt $ExpectedEditorHeight + 64)){
        throw "Unexpected Standalone client height $($record.clientHeight) for editor height $ExpectedEditorHeight."
    }
    $record.normalCloseRequested=[DeepPluckSmokeWindow]::PostMessage($handle,0x0010,[IntPtr]::Zero,[IntPtr]::Zero)
    if(-not $record.normalCloseRequested){$record.postMessageError=[Runtime.InteropServices.Marshal]::GetLastWin32Error()}
    if(-not $record.normalCloseRequested){throw 'Normal WM_CLOSE failed.'}
    if(-not $process.WaitForExit(20000)){$record.timeout=$true; throw 'Normal close timed out.'}
    $record.exitCode=$process.ExitCode
    if($process.ExitCode -ne 0){throw "Standalone exit code $($process.ExitCode)"}
    $record.passed=$true
}
finally {
    if($null -ne $process){
        if(-not $process.HasExited){
            # Cleanup only the exact process created above; never close a user's
            # pre-existing instance. Forced cleanup is always a failed smoke.
            $record.forcedCleanup=$true; $record.passed=$false
            Stop-Process -InputObject $process -Force
            [void]$process.WaitForExit(5000)
        }
        $record.remainingProcess=-not $process.HasExited
        if($record.remainingProcess){$record.passed=$false}
    }
    $record | ConvertTo-Json | Set-Content -LiteralPath $receipt -Encoding utf8
}
Write-Host "PASS: native window $($record.clientWidth)x$($record.clientHeight), normal close, exit 0, no remaining owned process. Audio/MIDI/listening not tested."
