# verify-state.ps1 - VM-side detection runner. Reads the harness matrix.json
# from the path given via -MatrixPath, runs the verify block of each app under
# the requested -PCType / -PCSubType, prints per-app PASS / FAIL / WARN, and
# exits 0 only if every check passes.
#
# Detection methods supported:
#   Registry    -> Get-ItemProperty $path[$name] -eq $value
#   File        -> Test-Path $path
#   FileVersion -> (Get-Item $path).VersionInfo.FileVersion -eq $value
#   Hash        -> Get-FileHash SHA256 -eq $value
#   FileGrep    -> Get-Content $path -match $pattern  (regex)

[CmdletBinding()]
param(
    [Parameter(Mandatory=$true)] [string]$MatrixPath,
    [Parameter(Mandatory=$true)] [string]$PCType,
    [string]$PCSubType
)

$ErrorActionPreference = 'Continue'

if (-not (Test-Path -LiteralPath $MatrixPath)) {
    Write-Host "[FAIL] matrix not found at $MatrixPath"
    exit 1
}
$matrix = Get-Content -LiteralPath $MatrixPath -Raw | ConvertFrom-Json

$entry = $matrix.pcTypes | Where-Object { $_.PCType -eq $PCType -and ($_.PCSubType -eq $PCSubType -or [string]::IsNullOrEmpty($_.PCSubType)) } | Select-Object -First 1
if (-not $entry) {
    Write-Host "[FAIL] no matrix entry for PCType=$PCType PCSubType=$PCSubType"
    exit 1
}

function Test-AppState {
    param($app)
    $v = $app.verify
    switch ($v.method) {
        'Registry' {
            if (-not (Test-Path -LiteralPath $v.path)) { return @{ pass=$false; detail="reg path missing: $($v.path)" } }
            $val = (Get-ItemProperty -LiteralPath $v.path -Name $v.name -ErrorAction SilentlyContinue).$($v.name)
            if ($null -eq $val) { return @{ pass=$false; detail="reg name $($v.name) not present" } }
            if ($v.value -and $val -ne $v.value) {
                return @{ pass=$false; detail="reg $($v.name) = '$val' (expected '$($v.value)')" }
            }
            return @{ pass=$true; detail="reg $($v.name) = '$val'" }
        }
        'File' {
            if (Test-Path -LiteralPath $v.path) { return @{ pass=$true; detail="exists: $($v.path)" } }
            return @{ pass=$false; detail="missing: $($v.path)" }
        }
        'FileVersion' {
            if (-not (Test-Path -LiteralPath $v.path)) { return @{ pass=$false; detail="missing: $($v.path)" } }
            $ver = (Get-Item -LiteralPath $v.path).VersionInfo.FileVersion
            if ($v.value -and $ver -ne $v.value) {
                return @{ pass=$false; detail="version $ver (expected $($v.value))" }
            }
            return @{ pass=$true; detail="version $ver" }
        }
        'Hash' {
            if (-not (Test-Path -LiteralPath $v.path)) { return @{ pass=$false; detail="missing: $($v.path)" } }
            $h = (Get-FileHash -LiteralPath $v.path -Algorithm SHA256).Hash
            if ($v.value -and $h -ne $v.value) {
                return @{ pass=$false; detail="hash $h (expected $($v.value))" }
            }
            return @{ pass=$true; detail="hash matches" }
        }
        'FileGrep' {
            if (-not (Test-Path -LiteralPath $v.path)) { return @{ pass=$false; detail="missing: $($v.path)" } }
            $hit = Get-Content -LiteralPath $v.path | Select-String -Pattern $v.pattern -Quiet
            if ($hit) { return @{ pass=$true; detail="pattern matched: $($v.pattern)" } }
            return @{ pass=$false; detail="pattern not found: $($v.pattern)" }
        }
        default { return @{ pass=$false; detail="unknown method: $($v.method)" } }
    }
}

$total = 0; $passed = 0; $failed = @()
foreach ($app in $entry.apps) {
    $total++
    $r = Test-AppState -app $app
    if ($r.pass) {
        Write-Host "  [PASS] $($app.name) - $($r.detail)"
        $passed++
    } else {
        Write-Host "  [FAIL] $($app.name) - $($r.detail)"
        $failed += $app.name
    }
}

Write-Host ""
Write-Host "=== verify summary: $passed/$total passed ==="
if ($failed.Count -gt 0) {
    Write-Host "failed: $($failed -join ', ')"
    exit 1
}
exit 0
