[CmdletBinding()]
param(
    [string]$PackagePath = $PSScriptRoot,
    [string]$EvidencePath = (Join-Path $PWD ("ProcessMonitor-Phase2-" + (Get-Date -Format "yyyyMMdd-HHmmss"))),
    [switch]$PreflightOnly,
    [switch]$InteractiveGui
)

$ErrorActionPreference = "Stop"
$package = (Resolve-Path $PackagePath).Path
$guiExe = Join-Path $package "ProcessMonitor.exe"
$relayExe = Join-Path $package "ProcessMonitorRelay.exe"
$configPath = Join-Path $package "config\process_monitor_config.json"
$driverPath = Join-Path $package "driver\ProcessMonitor.sys"
foreach ($required in ($guiExe, $relayExe, $configPath, $driverPath)) {
    if (-not (Test-Path -LiteralPath $required -PathType Leaf)) { throw "Missing packaged artifact: $required" }
}

$evidence = New-Item -ItemType Directory -Force -Path $EvidencePath
$transcript = Join-Path $evidence.FullName "transcript.txt"
$summaryPath = Join-Path $evidence.FullName "summary.json"
$summary = [ordered]@{ started_at = (Get-Date).ToString("o"); package_path = $package; state_a = "NOT_RUN" }
Start-Transcript -LiteralPath $transcript -Force | Out-Null

function Save-Summary {
    $summary.finished_at = (Get-Date).ToString("o")
    $json = $summary | ConvertTo-Json -Depth 8
    [IO.File]::WriteAllText($summaryPath, $json, [Text.UTF8Encoding]::new($false))
}

function Invoke-Relay([string]$Action) {
    $output = & $relayExe $Action 2>&1
    if ($LASTEXITCODE -ne 0) { throw "Relay $Action failed: $output" }
    return ($output | ConvertFrom-Json)
}

function Invoke-GuiSmoke {
    if ($InteractiveGui) {
        $process = Start-Process -FilePath $guiExe -PassThru
        Read-Host "Verify the GUI layout, absent driver state, stopped relay, and LOW/0 threat score; then close the GUI and press Enter"
        if (-not $process.HasExited) { throw "Close the GUI before continuing." }
    } else {
        $env:PROCESSMONITOR_GUI_SMOKE_MS = "3000"
        try {
            $process = Start-Process -FilePath $guiExe -WindowStyle Hidden -PassThru
            $null = $process.WaitForExit(15000)
            Start-Sleep -Seconds 2
            $remaining = Get-Process -Name "ProcessMonitor" -ErrorAction SilentlyContinue |
                Where-Object { $_.Path -eq $guiExe }
            if ($remaining) { throw "Packaged GUI left a running process after timed smoke." }
        } finally {
            Remove-Item Env:PROCESSMONITOR_GUI_SMOKE_MS -ErrorAction SilentlyContinue
        }
    }
}

try {
    $computer = Get-ComputerInfo | Select-Object WindowsProductName,WindowsVersion,OsBuildNumber
    $python = Get-Command python -ErrorAction SilentlyContinue
    $py = Get-Command py -ErrorAction SilentlyContinue
    $serviceOutput = (& sc.exe query ProcessMonitor 2>&1) -join "`n"
    $serviceExit = $LASTEXITCODE
    $drivers = (& pnputil.exe /enum-drivers 2>&1) -join "`n"
    $packageMatches = @($drivers -split "`r?`n" | Select-String "ProcessMonitor|Contoso" | ForEach-Object Line)
    $signature = Get-AuthenticodeSignature -LiteralPath $driverPath

    $summary.windows = $computer
    $summary.python_command = if ($python) { $python.Source } else { $null }
    $summary.py_command = if ($py) { $py.Source } else { $null }
    $summary.driver_service_absent = ($serviceExit -eq 1060 -or $serviceOutput -match "FAILED 1060")
    $summary.driver_package_matches = $packageMatches
    $summary.driver_signature = [ordered]@{ status = [string]$signature.Status; message = $signature.StatusMessage;
        subject = if ($signature.SignerCertificate) { $signature.SignerCertificate.Subject } else { $null } }
    $summary.python_absent = (-not $python -and -not $py)
    $summary.clean_preflight = ($summary.python_absent -and $summary.driver_service_absent -and $packageMatches.Count -eq 0)

    $computer | Format-List
    "python command: " + $(if ($python) { $python.Source } else { "ABSENT" })
    "py command: " + $(if ($py) { $py.Source } else { "ABSENT" })
    $serviceOutput
    "pnputil matching lines: " + $(if ($packageMatches) { $packageMatches -join " | " } else { "NONE" })
    $signature | Format-List Status,StatusMessage,SignerCertificate

    if ($PreflightOnly) { $summary.state_a = "PREFLIGHT_ONLY"; return }
    if (-not $summary.clean_preflight) {
        $summary.state_a = "BLOCKED_PRECONDITION"
        throw "State A requires no python/py command, no ProcessMonitor service, and no staged matching driver package."
    }

    $initial = Invoke-Relay "--status"
    if ($initial.running) { throw "Relay was already running before State A." }
    Invoke-GuiSmoke
    $afterGui = Invoke-Relay "--status"
    $serviceAfter = (& sc.exe query ProcessMonitor 2>&1) -join "`n"; $serviceAfterExit = $LASTEXITCODE
    $driversAfter = (& pnputil.exe /enum-drivers 2>&1) -join "`n"
    if ($afterGui.running -or -not ($serviceAfterExit -eq 1060 -or $serviceAfter -match "FAILED 1060")) {
        throw "GUI changed relay or driver-service state."
    }
    if (@($driversAfter -split "`r?`n" | Select-String "ProcessMonitor|Contoso").Count -ne 0) {
        throw "GUI appears to have staged a matching driver package."
    }

    $started = Invoke-Relay "--start"; $workerPid = [int]$started.pid
    Start-Sleep -Milliseconds 500
    $status = Invoke-Relay "--status"; $duplicate = Invoke-Relay "--start"
    if (-not $status.running -or [int]$status.pid -ne $workerPid -or [int]$duplicate.pid -ne $workerPid) {
        throw "Relay persistence or duplicate-start validation failed."
    }
    Invoke-GuiSmoke
    if (-not (Invoke-Relay "--status").running) { throw "GUI closure stopped the relay." }
    $stopped = Invoke-Relay "--stop"
    if ($stopped.running -or (Get-Process -Id $workerPid -ErrorAction SilentlyContinue)) {
        throw "Relay stop left a worker running."
    }

    $stateRoot = Join-Path $env:LOCALAPPDATA "ProcessMonitor"
    $stateItems = @(Get-ChildItem -LiteralPath $stateRoot -Recurse -ErrorAction SilentlyContinue)
    $meiReferences = @($stateItems | Select-String "_MEI" -SimpleMatch -ErrorAction SilentlyContinue)
    $summary.state_root = $stateRoot
    $summary.state_item_count = $stateItems.Count
    $summary.persistent_mei_references = $meiReferences.Count
    $summary.relay_pid = $workerPid
    $summary.state_a = "PASS"
}
catch {
    $summary.error = $_.Exception.Message
    throw
}
finally {
    try { & $relayExe --stop | Out-Null } catch {}
    Save-Summary
    Stop-Transcript | Out-Null
    Write-Host "Evidence: $($evidence.FullName)"
}
