param(
  [string]$InstallRoot = (Join-Path $env:LOCALAPPDATA 'PLAI Canonical QBFT Node'),
  [switch]$RequirePeerReachability
)

$ErrorActionPreference = 'Stop'
$configPath = Join-Path $InstallRoot 'node-config.json'
if (-not (Test-Path -LiteralPath $configPath)) { throw "Node config is missing: $configPath" }
$config = Get-Content -LiteralPath $configPath -Raw | ConvertFrom-Json
$issues = [System.Collections.Generic.List[string]]::new()

foreach ($required in @('genesis.json', 'static-nodes.json', 'manifest.json', 'key.pub', 'start-node.ps1')) {
  if (-not (Test-Path -LiteralPath (Join-Path $InstallRoot $required))) { $issues.Add("Missing $required") }
}
$localIps = @(
  [Net.NetworkInformation.NetworkInterface]::GetAllNetworkInterfaces() |
    ForEach-Object { $_.GetIPProperties().UnicastAddresses } |
    ForEach-Object { $_.Address.ToString().Trim().ToLowerInvariant() }
)
$advertisedHost = ([string]$config.advertisedHost).Trim().ToLowerInvariant()
$localAddressAssigned = @($localIps | Where-Object { ([string]$_).Equals($advertisedHost, [StringComparison]::OrdinalIgnoreCase) }).Count -gt 0
if (-not $localAddressAssigned) {
  $issues.Add("Target host $($config.advertisedHost) is not assigned to this PC")
}
if (Get-NetTCPConnection -State Listen -LocalPort $config.p2pPort -ErrorAction SilentlyContinue) {
  $issues.Add("P2P port $($config.p2pPort) is already in use")
}
if ($config.role -eq 'validator') {
  $privateKeyPath = Join-Path (Join-Path $InstallRoot 'secrets') 'validator.key'
  if (-not (Test-Path -LiteralPath $privateKeyPath)) { $issues.Add('Validator private key is missing') }
  elseif ((Get-Acl -LiteralPath $privateKeyPath).AreAccessRulesProtected -ne $true) { $issues.Add('Validator key ACL inheritance is not disabled') }
}

$peerResults = @()
$staticPeers = Get-Content -LiteralPath (Join-Path $InstallRoot 'static-nodes.json') -Raw | ConvertFrom-Json
foreach ($peer in @($staticPeers | Select-Object -Unique)) {
  $match = [regex]::Match([string]$peer, '@(?<host>[^:]+):(?<port>[0-9]+)$')
  if (-not $match.Success) {
    $issues.Add("Invalid static peer: $peer")
    continue
  }
  $peerHost = $match.Groups['host'].Value
  $peerPort = [int]$match.Groups['port'].Value
  $reachable = Test-NetConnection -ComputerName $peerHost -Port $peerPort -InformationLevel Quiet -WarningAction SilentlyContinue
  $peerResults += [pscustomobject]@{ host = $peerHost; port = $peerPort; reachable = [bool]$reachable }
  if ($RequirePeerReachability -and -not $reachable) { $issues.Add("Peer $($peerHost):$peerPort is unreachable") }
}

$result = [ordered]@{
  ok = $issues.Count -eq 0
  nodeName = $config.nodeName
  role = $config.role
  chainId = $config.chainId
  advertisedHost = $config.advertisedHost
  localAddressAssigned = $localAddressAssigned
  localAddressCount = $localIps.Count
  p2pPort = $config.p2pPort
  peerResults = $peerResults
  issues = @($issues)
  checkedAt = [DateTime]::UtcNow.ToString('o')
}
$result | ConvertTo-Json -Depth 6
if ($issues.Count -gt 0) { exit 1 }
