param(
    [Parameter(Mandatory = $true)]
    [string]$ExePath,
    [switch]$ExtractMenuTo,
    [switch]$Quiet
)

$ErrorActionPreference = 'Stop'
. (Join-Path $PSScriptRoot 'golden_constants.ps1')

function Write-Check([bool]$Ok, [string]$Message) {
    if ($Quiet -and $Ok) { return }
    if ($Ok) { Write-Host "[OK] $Message" -ForegroundColor Green }
    else     { Write-Host "[BLOCKED] $Message" -ForegroundColor Red }
}

function Fail([string]$Message) {
    Write-Check $false $Message
    exit 1
}

function Get-FileMd5([string]$Path) {
    return ([BitConverter]::ToString([Security.Cryptography.MD5]::Create().ComputeHash([IO.File]::ReadAllBytes($Path)))).Replace('-', '').ToLower()
}

Add-Type @'
using System;
using System.Runtime.InteropServices;
public class GoldenRes {
  [DllImport("kernel32.dll", CharSet=CharSet.Unicode, SetLastError=true)]
  public static extern IntPtr LoadLibraryEx(string f, IntPtr h, uint fl);
  [DllImport("kernel32.dll")] public static extern bool FreeLibrary(IntPtr h);
  [DllImport("kernel32.dll")] public static extern IntPtr FindResource(IntPtr m, IntPtr n, IntPtr t);
  [DllImport("kernel32.dll")] public static extern uint SizeofResource(IntPtr m, IntPtr r);
  [DllImport("kernel32.dll")] public static extern IntPtr LoadResource(IntPtr m, IntPtr r);
  [DllImport("kernel32.dll")] public static extern IntPtr LockResource(IntPtr r);
}
'@

function Get-EmbeddedResourceBytes([string]$Path, [int]$Id) {
    $h = [GoldenRes]::LoadLibraryEx($Path, [IntPtr]::Zero, 2)
    if ($h -eq [IntPtr]::Zero) { throw "LoadLibraryEx failed: $Path" }
    try {
        $res = [GoldenRes]::FindResource($h, [IntPtr]$Id, [IntPtr]10)
        if ($res -eq [IntPtr]::Zero) { return $null }
        $sz = [GoldenRes]::SizeofResource($h, $res)
        if ($sz -eq 0) { return $null }
        $hg = [GoldenRes]::LoadResource($h, $res)
        $p = [GoldenRes]::LockResource($hg)
        $buf = New-Object byte[] $sz
        [Runtime.InteropServices.Marshal]::Copy($p, $buf, 0, [int]$sz)
        return ,@($buf, [int]$sz)
    }
    finally {
        [void][GoldenRes]::FreeLibrary($h)
    }
}

function Get-SyncHookHex([byte[]]$MenuDll) {
    # RVA 0x11E15C - read PE header to convert RVA to file offset
    if ($MenuDll.Length -lt 0x200) { return '' }
    $e_lfanew = [BitConverter]::ToInt32($MenuDll, 0x3C)
    $numSections = [BitConverter]::ToInt16($MenuDll, $e_lfanew + 6)
    $optSize = [BitConverter]::ToInt16($MenuDll, $e_lfanew + 20)
    $secTable = $e_lfanew + 24 + $optSize
    $rva = [Convert]::ToInt32($GoldenPublish.SyncHookRva, 16)
    for ($i = 0; $i -lt $numSections; $i++) {
        $sec = $secTable + ($i * 40)
        $va = [BitConverter]::ToUInt32($MenuDll, $sec + 12)
        $vs = [BitConverter]::ToUInt32($MenuDll, $sec + 8)
        $raw = [BitConverter]::ToUInt32($MenuDll, $sec + 20)
        $rawPtr = [BitConverter]::ToUInt32($MenuDll, $sec + 20)
        $rawSize = [BitConverter]::ToUInt32($MenuDll, $sec + 16)
        if ($rva -ge $va -and $rva -lt ($va + [Math]::Max($vs, $rawSize))) {
            $off = $rawPtr + ($rva - $va)
            return ([BitConverter]::ToString($MenuDll, $off, 6)).Replace('-', '').ToLower()
        }
    }
    return ''
}

if (-not (Test-Path $ExePath)) { Fail "Exe not found: $ExePath" }
$ExePath = (Resolve-Path $ExePath).Path

$exeSize = (Get-Item $ExePath).Length
$exeMd5 = Get-FileMd5 $ExePath

if ($exeMd5 -ne $GoldenPublish.PublishExeMd5) {
    Fail "Exe MD5 mismatch. Got $exeMd5 - publish ONLY $($GoldenPublish.PublishExeName) (md5 $($GoldenPublish.PublishExeMd5)). LAB builds must not use PLAY-JULY2.bat."
}
Write-Check $true "Publish exe MD5 matches golden ($exeMd5)"

if ($exeSize -ne $GoldenPublish.PublishExeSize) {
    Fail "Exe size $exeSize - expected $($GoldenPublish.PublishExeSize). Wrong launcher shell."
}
Write-Check $true "Publish exe size $($GoldenPublish.PublishExeSize) bytes"

$menuRes = Get-EmbeddedResourceBytes $ExePath $GoldenPublish.ResourceMenuId
$bridgeRes = Get-EmbeddedResourceBytes $ExePath $GoldenPublish.ResourceBridgeId

if (-not $menuRes) {
    Fail "Resource $($GoldenPublish.ResourceMenuId) (menu) missing or empty in exe."
}

$menuBytes = $menuRes[0]
$menuSize = $menuRes[1]
$menuMd5 = ([BitConverter]::ToString([Security.Cryptography.MD5]::Create().ComputeHash($menuBytes))).Replace('-', '').ToLower()

if ($menuSize -ne $GoldenPublish.MenuDllBytes) {
    Fail "Embedded menu is $menuSize bytes - MUST be $($GoldenPublish.MenuDllBytes) frozen v43. Recompiled menus break online join."
}
Write-Check $true "Embedded menu size $($GoldenPublish.MenuDllBytes) bytes"

if ($GoldenBannedMenuSizes -contains $menuSize) {
    Fail "Embedded menu size $menuSize is a known-broken recompiled size."
}

if ($menuMd5 -ne $GoldenPublish.MenuDllMd5) {
    Fail "Embedded menu MD5 $menuMd5 - expected $($GoldenPublish.MenuDllMd5). Same build-id string can hide different bytes."
}
Write-Check $true "Embedded menu MD5 matches frozen v43"

$sync = Get-SyncHookHex $menuBytes
if ($sync -ne $GoldenPublish.SyncHookPlainBytes) {
    foreach ($ban in $GoldenPublish.SyncHookBannedPrefix) {
        if ($sync.StartsWith($ban)) {
            Fail "Sync hook @ $($GoldenPublish.SyncHookRva) is PATCHED ($sync). Publish requires plain $($GoldenPublish.SyncHookPlainBytes)."
        }
    }
    Fail "Sync hook @ $($GoldenPublish.SyncHookRva) unexpected: $sync (expected plain $($GoldenPublish.SyncHookPlainBytes))"
}
Write-Check $true "Sync hook unpatched (plain v43)"

if ($bridgeRes) {
    $bridgeSize = $bridgeRes[1]
    if ($bridgeSize -eq $GoldenPublish.RejectedBridgeBytes) {
        Fail "Embedded bridge is $($GoldenPublish.RejectedBridgeBytes) bytes - known to break online join."
    }
    if ($bridgeSize -ne $GoldenPublish.BridgeDllBytes) {
        Fail "Embedded bridge is $bridgeSize bytes - expected $($GoldenPublish.BridgeDllBytes) (WINMM-july2)."
    }
    $bridgeMd5 = ([BitConverter]::ToString([Security.Cryptography.MD5]::Create().ComputeHash($bridgeRes[0]))).Replace('-', '').ToLower()
    if ($bridgeMd5 -ne $GoldenPublish.BridgeDllMd5) {
        Fail "Embedded bridge MD5 $bridgeMd5 - expected $($GoldenPublish.BridgeDllMd5)."
    }
    Write-Check $true "Embedded bridge $($GoldenPublish.BridgeDllBytes) bytes, July2 MD5"
}
else {
    Write-Host '[WARN] Bridge resource 102 not readable - July2 shell may embed bridge differently; verify runtime AKBridge after launch.' -ForegroundColor Yellow
}

if ($ExtractMenuTo) {
    [IO.File]::WriteAllBytes($ExtractMenuTo, $menuBytes)
    Write-Check $true "Extracted menu to $ExtractMenuTo"
}

if (-not $Quiet) {
    Write-Host ''
    Write-Host 'GOLDEN VERIFY PASSED - safe to launch for online.' -ForegroundColor Cyan
    Write-Host ('After inject, cout.log must show Build ID: ' + $GoldenPublish.MenuBuildId)
}
exit 0
