/**
 * get-ad-health — site-anchored AD health summary. Mirrors what openclaw
 * produced via Overshell on the Hynes DCs around 2026-04-25.
 *
 * Runs from the WNP endpoint and uses native AD cmdlets to reach the DC(s)
 * over the network. Outputs structured JSON: replication state per DC,
 * critical service status, dcdiag pass/fail per test, recent System log
 * Netlogon/DNS errors.
 */

import { type RmmScript, parseJsonOutput } from './types';

export const getAdHealth: RmmScript = {
  id: 'get-ad-health',
  name: 'AD health (DC replication, services, dcdiag)',
  description:
    'Per-DC replication state, critical service status, dcdiag summary, and recent Netlogon/DNS errors. Run from a Wulf Nurse endpoint at the site.',
  target_type: 'site_anchor',
  expected_runtime_seconds: 90,
  version: 1,
  body: `
$ErrorActionPreference = 'SilentlyContinue'
Import-Module ActiveDirectory -ErrorAction SilentlyContinue

$forest = Get-ADForest -ErrorAction SilentlyContinue
$domain = Get-ADDomain -ErrorAction SilentlyContinue
$dcs = Get-ADDomainController -Filter * -ErrorAction SilentlyContinue |
  Select-Object Name, HostName, Site, OperatingSystem, IPv4Address

# Replication partner metadata per DC.
$repl = foreach ($dc in $dcs) {
  $partners = Get-ADReplicationPartnerMetadata -Target $dc.HostName -PartnerType Both -ErrorAction SilentlyContinue
  foreach ($p in $partners) {
    [pscustomobject]@{
      dc = $dc.HostName
      partner = $p.Partner
      last_replication_success = if ($p.LastReplicationSuccess) { $p.LastReplicationSuccess.ToUniversalTime().ToString("o") } else { $null }
      last_replication_attempt = if ($p.LastReplicationAttempt) { $p.LastReplicationAttempt.ToUniversalTime().ToString("o") } else { $null }
      last_replication_result  = $p.LastReplicationResult
      consecutive_failures     = $p.ConsecutiveReplicationFailures
    }
  }
}

# Critical services per DC.
$svcNames = @('NTDS','DNS','Netlogon','W32Time','DFSR','RpcSs','Kdc')
$services = foreach ($dc in $dcs) {
  foreach ($n in $svcNames) {
    $s = Get-Service -ComputerName $dc.HostName -Name $n -ErrorAction SilentlyContinue
    [pscustomobject]@{
      dc = $dc.HostName
      service = $n
      status = if ($s) { $s.Status.ToString() } else { 'Missing' }
      start_type = if ($s) { $s.StartType.ToString() } else { $null }
    }
  }
}

# dcdiag summary — capture pass/fail per test.
$dcdiag = foreach ($dc in $dcs) {
  $raw = & dcdiag /s:$($dc.HostName) /q 2>&1
  $passed = @()
  $failed = @()
  foreach ($line in ($raw -split "\`n")) {
    if ($line -match 'passed test (\\w+)') { $passed += $Matches[1] }
    elseif ($line -match 'failed test (\\w+)') { $failed += $Matches[1] }
  }
  [pscustomobject]@{
    dc = $dc.HostName
    passed = $passed
    failed = $failed
  }
}

# Recent Netlogon/DNS errors per DC.
$recent = foreach ($dc in $dcs) {
  $events = Get-WinEvent -ComputerName $dc.HostName -FilterHashtable @{
    LogName = 'System'
    ProviderName = @('NETLOGON','Microsoft-Windows-DNS-Client','DNS-Server-Service')
    Level = @(1,2,3)
    StartTime = (Get-Date).AddHours(-24)
  } -MaxEvents 20 -ErrorAction SilentlyContinue |
    Select-Object @{n='time';e={$_.TimeCreated.ToUniversalTime().ToString("o")}},
                  Id, LevelDisplayName, ProviderName,
                  @{n='message';e={ ($_.Message -replace '\\s+',' ').Trim().Substring(0, [Math]::Min(400, $_.Message.Length)) }}
  [pscustomobject]@{ dc = $dc.HostName; events = $events }
}

@{
  hostname = $env:COMPUTERNAME
  captured_at_utc = (Get-Date).ToUniversalTime().ToString("o")
  forest = if ($forest) { $forest.Name } else { $null }
  domain = if ($domain) { $domain.DNSRoot } else { $null }
  dcs = $dcs
  replication = $repl
  services = $services
  dcdiag = $dcdiag
  recent_errors = $recent
} | ConvertTo-Json -Depth 6 -Compress
`.trim(),
  parseOutput: (stdout) => parseJsonOutput(stdout),
};
