#Requires -RunAsAdministrator
<#
.SYNOPSIS
    Tests COM registration for the OpenDecal project.

.DESCRIPTION
    Builds the Decal solution (optionally), registers 10 COM server DLLs using
    regasm /codebase, and verifies that every expected CLSID appears in the
    registry under HKCR:\CLSID\{GUID}\InprocServer32.

.PARAMETER Build
    If specified, builds Decal.sln (Release) before registration.

.PARAMETER Unregister
    If specified, unregisters all DLLs instead of registering them.

.EXAMPLE
    .\Test-ComRegistration.ps1 -Build
    Builds the solution, registers all DLLs, and verifies CLSIDs.

.EXAMPLE
    .\Test-ComRegistration.ps1 -Unregister
    Unregisters all DLLs.
#>
[CmdletBinding()]
param(
    [switch]$Build,
    [switch]$Unregister
)

Set-StrictMode -Version Latest
$ErrorActionPreference = 'Continue'

# ── Paths ────────────────────────────────────────────────────────────────────
$RegAsm   = 'C:\Windows\Microsoft.NET\Framework\v4.0.30319\RegAsm.exe'
$SolutionRoot = Split-Path -Parent $PSScriptRoot          # reconstructed\
$ManagedRoot  = Join-Path $SolutionRoot 'Managed'
$SolutionFile = Join-Path $ManagedRoot  'Decal.sln'

# ── Verify regasm exists ─────────────────────────────────────────────────────
if (-not (Test-Path $RegAsm)) {
    Write-Error "RegAsm.exe not found at $RegAsm"
    exit 1
}

# ── DLL → CLSID map ─────────────────────────────────────────────────────────
# Each key is the relative path from $ManagedRoot to the built DLL.
# Each value is an ordered list of [CLSID, FriendlyName] pairs.
$DllMap = [ordered]@{

    'Decal.Core\bin\Release\Decal.Core.dll' = @(
        ,@('CB8875CD-ABC2-42AD-8175-8908706EED37', 'ACHooks')
        ,@('4557D5A1-00DB-48F6-ACB3-4FEF30E2F358', 'DecalCore')
        ,@('6DE65A82-C451-46E6-A82D-92137BE851AD', 'DecalEnum')
        ,@('E2284FC7-17E5-4846-ADAB-07953273C5FB', 'PluginSite2')
    )

    'Decal.DecalControls\bin\Release\Decal.DecalControls.dll' = @(
        ,@('5AE37451-F79C-478A-834E-EDCF95F01B0E', 'Checkbox')
        ,@('E099DC60-0F19-4690-AB7C-8B5834DA286E', 'Choice')
        ,@('3035299A-C5FB-4CC7-A63C-66400B80DCA4', 'DerethMap')
        ,@('8E8F88D2-AA47-474E-9DB7-912D49C8BE9D', 'Edit')
        ,@('CD6556CD-F8D9-4CB0-AB7C-7C33058294E4', 'FixedLayout')
        ,@('CA121762-31BB-4073-8597-33BAB4BDCAA3', 'BorderLayout')
        ,@('5AD04D45-D4BF-4729-8A2E-5D37CF726CAA', 'PageLayout')
        ,@('3839008F-AF5B-43FC-9F6A-3376B99E3DAF', 'List')
        ,@('864DEABF-D079-4B61-A8CF-081418179239', 'TextColumn')
        ,@('F12A2C4C-3B78-46EB-8722-68B27A75AE55', 'IconColumn')
        ,@('48E444F1-8E30-4E4C-B203-4C87FC901586', 'CheckColumn')
        ,@('ED14E7C5-11BE-4DFD-9829-873AB6BE3CFC', 'Notebook')
        ,@('FE361225-6BB7-4AE0-A10C-8A6420621680', 'Progress')
        ,@('AE4525BE-81D1-40FB-9170-77172077EB49', 'PushButton')
        ,@('8FC80D21-1731-4816-9AD3-B0364D5F2C27', 'Scroller')
        ,@('5D14557A-1268-43C6-A283-3B5B271359AA', 'Slider')
        ,@('4887101C-A9F9-495B-921B-EF22822CFB97', 'StaticText')
    )

    'Decal.DecalDat\bin\Release\Decal.DecalDat.dll' = @(
        ,@('37B083F0-276E-43AD-8D26-3F7449B519DC', 'DatService')
        ,@('9F7F6CD9-D164-418D-8CB5-3B9ACD70BEAF', 'DatStream')
        ,@('6FA05FDA-B4B5-4386-AB45-92D7E6A5D698', 'DatLibrary')
    )

    'Decal.DecalFilters\bin\Release\Decal.DecalFilters.dll' = @(
        ,@('50A7E9EC-AB12-4484-9C28-C2A39274A636', 'WorldObject')
        ,@('2681B113-294E-4ABF-B543-624194846BE1', 'WorldIterator')
        ,@('443D4A68-5422-4E0C-9460-973F8FBDB190', 'Prefilter')
        ,@('53092D1B-F0B0-46FF-BF11-8F031EC9B137', 'World')
        ,@('8C2FA400-315D-41DE-B063-D6EF04F12E1F', 'EchoFilter')
        ,@('34239EAD-6317-4C40-A405-193BA5232DD8', 'EchoFilter2')
        ,@('4540C969-08D1-46BF-97AD-6B19D3C10BEE', 'CharacterStats')
    )

    'Decal.DecalInput\bin\Release\Decal.DecalInput.dll' = @(
        ,@('F183506A-3664-49D6-8CA4-CFD295F2811D', 'Hotkey')
        ,@('85AB0296-124E-4E68-A6A8-FCF5721AC09B', 'WndMsg')
        ,@('B33307BA-706D-474A-80B9-70BB8D13EF3E', 'InputService')
        ,@('F3170E85-517E-43A4-B7B4-6F006A7B1B85', 'WinMsgHook')
        ,@('79497C87-92E1-416B-AE5C-9D6C4C59133C', 'Timer')
        ,@('F0A17A04-7F8F-4A17-A41D-8C297A1E929B', 'InputBuffer')
    )

    'Decal.DecalNet\bin\Release\Decal.DecalNet.dll' = @(
        ,@('15631E36-55CB-4D16-ADE7-74D9F8A5F4B6', 'WebRequest')
        ,@('C8C406F8-BA2E-4964-8B04-FF38394A8E0E', 'NetService')
    )

    'Decal.DecalRender\bin\Release\Decal.DecalRender.dll' = @(
        ,@('FB3C8286-88ED-4B4D-B413-94B40F346239', 'RenderService')
        ,@('218432A3-8960-4D91-BBC6-8CDC105C191E', 'HUDBackground')
        ,@('EF810EF1-C843-4563-A3DE-19CE8BEEEEC0', 'HUDView')
        ,@('175D6713-E942-41C0-8BC9-90A565024F76', 'RenderTargetWrapper')
    )

    'Decal.D3DService\bin\Release\Decal.D3DService.dll' = @(
        ,@('F0CC07A0-2C89-4FA4-9356-714665BC2F8B', 'D3DService')
        ,@('81E79859-2783-4B9A-ADC4-308073F5BB3F', 'D3DObj')
    )

    'Decal.DHS\bin\Release\Decal.DHS.dll' = @(
        ,@('6B6B9FA8-37DE-4FA3-8C60-52BD6A2F9855', 'HotkeySystem')
        ,@('F5AA853D-CCF3-4562-A654-3A68AB583BCC', 'Hotkey')
    )

    'Decal.SpellFilter\bin\Release\Decal.SpellFilter.dll' = @(
        ,@('C2D43735-BE7E-4829-AF73-F2E7E820EB16', 'Spells')
        ,@('3040368A-B77A-4FE1-B8CC-D77FA3F36A6D', 'Spell')
        ,@('20425F2F-54FF-4EAE-9538-36699E3DFB56', 'Component')
    )
}

# ── Helper: coloured write ───────────────────────────────────────────────────
function Write-Status {
    param(
        [string]$Label,
        [string]$Message,
        [ConsoleColor]$Color = 'White'
    )
    Write-Host "  [$Label] " -ForegroundColor $Color -NoNewline
    Write-Host $Message
}

# ── Administrator check ──────────────────────────────────────────────────────
$identity  = [Security.Principal.WindowsIdentity]::GetCurrent()
$principal = New-Object Security.Principal.WindowsPrincipal $identity
if (-not $principal.IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator)) {
    Write-Error 'This script must be run as Administrator (regasm requires elevated privileges).'
    exit 1
}

# ── Optional build step ──────────────────────────────────────────────────────
if ($Build) {
    Write-Host "`n=== Building Decal.sln (Release) ===" -ForegroundColor Cyan
    $buildArgs = @('build', $SolutionFile, '-c', 'Release')
    & dotnet @buildArgs
    if ($LASTEXITCODE -ne 0) {
        Write-Error "Build failed with exit code $LASTEXITCODE"
        exit 1
    }
    Write-Host ''
}

# ── Counters ─────────────────────────────────────────────────────────────────
$totalCLSIDs  = 0
$passedCLSIDs = 0
$failedCLSIDs = 0

# ── Process each DLL ─────────────────────────────────────────────────────────
foreach ($relPath in $DllMap.Keys) {
    $dllPath = Join-Path $ManagedRoot $relPath
    $dllName = Split-Path -Leaf $dllPath

    Write-Host "`n=== $dllName ===" -ForegroundColor Cyan

    # Check the DLL exists
    if (-not (Test-Path $dllPath)) {
        Write-Status 'SKIP' "$dllPath not found. Build the solution first (-Build)." Yellow
        $clsids = $DllMap[$relPath]
        $totalCLSIDs  += $clsids.Count
        $failedCLSIDs += $clsids.Count
        continue
    }

    # ── Register or unregister ───────────────────────────────────────────
    if ($Unregister) {
        Write-Host "  Unregistering..." -ForegroundColor Yellow
        $output = & $RegAsm /unregister /nologo $dllPath 2>&1
        $regExitCode = $LASTEXITCODE
    }
    else {
        Write-Host "  Registering..." -ForegroundColor Yellow
        $output = & $RegAsm /codebase /nologo $dllPath 2>&1
        $regExitCode = $LASTEXITCODE
    }

    # Show regasm output
    if ($output) {
        foreach ($line in $output) {
            Write-Host "    $line" -ForegroundColor Gray
        }
    }
    if ($regExitCode -ne 0) {
        Write-Status 'WARN' "regasm exited with code $regExitCode" Yellow
    }

    # ── Verify CLSIDs ────────────────────────────────────────────────────
    if (-not $Unregister) {
        $clsids = $DllMap[$relPath]
        foreach ($entry in $clsids) {
            $guid = $entry[0]
            $name = $entry[1]
            $totalCLSIDs++

            # 32-bit regasm registers under WOW6432Node on 64-bit Windows
            $regKey64 = "Registry::HKEY_CLASSES_ROOT\CLSID\{$guid}\InprocServer32"
            $regKey32 = "Registry::HKEY_CLASSES_ROOT\WOW6432Node\CLSID\{$guid}\InprocServer32"
            if ((Test-Path $regKey64) -or (Test-Path $regKey32)) {
                Write-Status 'PASS' "{$guid}  $name" Green
                $passedCLSIDs++
            }
            else {
                Write-Status 'FAIL' "{$guid}  $name" Red
                $failedCLSIDs++
            }
        }
    }
}

# ── Summary ──────────────────────────────────────────────────────────────────
Write-Host "`n============================================" -ForegroundColor Cyan
if ($Unregister) {
    Write-Host "  Unregistration complete." -ForegroundColor Cyan
}
else {
    $summaryColor = if ($failedCLSIDs -eq 0) { 'Green' } else { 'Red' }
    Write-Host "  Total CLSIDs checked : $totalCLSIDs" -ForegroundColor $summaryColor
    Write-Host "  Passed               : $passedCLSIDs" -ForegroundColor Green
    Write-Host "  Failed               : $failedCLSIDs" -ForegroundColor $(if ($failedCLSIDs -eq 0) { 'Green' } else { 'Red' })
}
Write-Host "============================================`n" -ForegroundColor Cyan

if ($failedCLSIDs -gt 0) {
    exit 1
}
exit 0
