#!/usr/bin/env php
<?php
// NEMS Central Command (NCC) Background Daemon
set_time_limit(0);
ignore_user_abort(true);

// Ensure all date/time calculations use the system local timezone
date_default_timezone_set(date_default_timezone_get());

// Disable PHP output buffering so systemd & nems-ncc-monitor get logs instantly
ob_implicit_flush(true);
while (ob_get_level()) ob_end_clean();

$ncc_dir = '/usr/local/share/nems/ncc';
if (!is_dir($ncc_dir)) {
    @mkdir($ncc_dir, 0775, true);
    @chown($ncc_dir, 'www-data');
    @chgrp($ncc_dir, 'www-data');
}

$db_path = $ncc_dir . '/ncc_history.db';
$ollama_url = 'http://127.0.0.1:11434/api/generate';
$global_db = null;

function ncc_log($msg, $color = "") {
    $reset = "\e[0m";
    $timestamp = "\e[90m[" . date('Y-m-d H:i:s') . "]\e[0m";
    file_put_contents('php://stdout', "{$timestamp} {$color}{$msg}{$reset}\n");
    @flush();
}

function curlFetchJson($url, $retries = 2) {
    for ($i = 0; $i <= $retries; $i++) {
        $ch = curl_init($url);
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
        curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
        curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
        curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
        curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 2);
        curl_setopt($ch, CURLOPT_TIMEOUT, 4);
        $response = curl_exec($ch);
        $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
        curl_close($ch);

        if ($response && $httpCode >= 200 && $httpCode < 300) {
            $decoded = json_decode($response, true);
            if ($decoded && !empty($decoded['success'])) {
                return $decoded;
            }
        }
        if ($i < $retries) usleep(250000);
    }
    return null;
}

function getDb() {
    global $db_path, $global_db;
    if ($global_db !== null) {
        return $global_db;
    }
    try {
        $global_db = new PDO('sqlite:' . $db_path);
        $global_db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
        $global_db->exec("PRAGMA journal_mode = WAL;");
        $global_db->exec("PRAGMA busy_timeout = 5000;");

        // Live Dashboard Tables (Rolling Windows)
        $global_db->exec("CREATE TABLE IF NOT EXISTS sla_snapshots (bucket_time INTEGER PRIMARY KEY, sla_value INTEGER)");
        $global_db->exec("CREATE TABLE IF NOT EXISTS host_snapshots (bucket_time INTEGER, host_name TEXT, state_code INTEGER, PRIMARY KEY (bucket_time, host_name))");
        $global_db->exec("CREATE TABLE IF NOT EXISTS notification_feed (
            id INTEGER PRIMARY KEY AUTOINCREMENT,
            timestamp INTEGER,
            event_key TEXT,
            sender TEXT,
            display_text TEXT,
            speech_text TEXT,
            state_class TEXT,
            is_ai INTEGER
        )");
        $global_db->exec("CREATE TABLE IF NOT EXISTS active_sessions (
            client_id TEXT PRIMARY KEY,
            last_seen INTEGER,
            ip_address TEXT
        )");
        $global_db->exec("CREATE TABLE IF NOT EXISTS daemon_status (
            id INTEGER PRIMARY KEY,
            last_heartbeat INTEGER
        )");

        // Long-term Enterprise Compliance Ledger & Monthly Digest Tables
        $global_db->exec("CREATE TABLE IF NOT EXISTS event_ledger (
            id INTEGER PRIMARY KEY AUTOINCREMENT,
            timestamp INTEGER,
            event_type TEXT,
            host_name TEXT,
            service_description TEXT,
            state_code INTEGER,
            plugin_output TEXT,
            author TEXT,
            memo TEXT
        )");

        $global_db->exec("CREATE TABLE IF NOT EXISTS daily_rollups (
            date_stamp TEXT PRIMARY KEY,
            sla_average INTEGER,
            total_incidents INTEGER,
            total_downtime_sec INTEGER,
            total_acks INTEGER,
            mttr_sec INTEGER
        )");

        if (file_exists($db_path)) {
            @chown($db_path, 'www-data');
            @chgrp($db_path, 'www-data');
            @chmod($db_path, 0664);
        }
        return $global_db;
    } catch (Exception $e) {
        ncc_log("Database initialization error: " . $e->getMessage(), "\e[31m");
        $global_db = null;
        return null;
    }
}

function writeNotification($eventKey, $sender, $displayText, $speechText, $stateClass, $isAi) {
    $db = getDb();
    if ($db) {
        try {
            $stmt = $db->prepare("INSERT INTO notification_feed (timestamp, event_key, sender, display_text, speech_text, state_class, is_ai) VALUES (?, ?, ?, ?, ?, ?, ?)");
            $stmt->execute([time(), $eventKey, $sender, $displayText, $speechText, $stateClass, $isAi]);
            $stmt = null;
            ncc_log("System Notification Sent -> [{$sender}] {$displayText}");
        } catch (Exception $e) {
            ncc_log("Failed to write notification: " . $e->getMessage(), "\e[31m");
        }
    }
}

function logLedgerEvent($type, $host, $svc, $stateCode, $output, $author = null, $memo = null) {
    $db = getDb();
    if ($db) {
        try {
            $stmt = $db->prepare("INSERT INTO event_ledger (timestamp, event_type, host_name, service_description, state_code, plugin_output, author, memo) VALUES (?, ?, ?, ?, ?, ?, ?, ?)");
            $stmt->execute([time(), $type, $host, $svc, (int)$stateCode, $output, $author, $memo]);
            $stmt = null;
        } catch (Exception $e) {
            ncc_log("Failed to log ledger event: " . $e->getMessage(), "\e[31m");
        }
    }
}

function performDailyRollup($targetDate) {
    $db = getDb();
    if (!$db) return;

    try {
        $startTime = strtotime($targetDate . ' 00:00:00');
        $endTime = strtotime($targetDate . ' 23:59:59');

        // 1. Calculate Average SLA %
        $stmtSla = $db->prepare("SELECT AVG(sla_value) FROM sla_snapshots WHERE bucket_time >= ? AND bucket_time <= ?");
        $stmtSla->execute([$startTime, $endTime]);
        $avgSla = round((float)($stmtSla->fetchColumn() ?: 100));
        $stmtSla = null;

        // 2. Incident & Acknowledgement Counts
        $stmtInc = $db->prepare("SELECT COUNT(*) FROM event_ledger WHERE event_type = 'INCIDENT' AND timestamp >= ? AND timestamp <= ?");
        $stmtInc->execute([$startTime, $endTime]);
        $incCount = (int)$stmtInc->fetchColumn();
        $stmtInc = null;

        $stmtAck = $db->prepare("SELECT COUNT(*) FROM event_ledger WHERE event_type = 'ACK' AND timestamp >= ? AND timestamp <= ?");
        $stmtAck->execute([$startTime, $endTime]);
        $ackCount = (int)$stmtAck->fetchColumn();
        $stmtAck = null;

        // 3. Estimate Total Downtime & MTTR
        $stmtEvents = $db->prepare("SELECT event_type, host_name, service_description, timestamp FROM event_ledger WHERE timestamp >= ? AND timestamp <= ? ORDER BY timestamp ASC");
        $stmtEvents->execute([$startTime, $endTime]);
        $events = $stmtEvents->fetchAll(PDO::FETCH_ASSOC);
        $stmtEvents = null;

        $activeFailures = [];
        $totalDowntimeSec = 0;
        $resolvedCount = 0;
        $totalResolutionTimeSec = 0;

        foreach ($events as $ev) {
            $key = $ev['host_name'] . '_' . ($ev['service_description'] ?: 'HOST');
            if ($ev['event_type'] === 'INCIDENT') {
                $activeFailures[$key] = (int)$ev['timestamp'];
            } elseif ($ev['event_type'] === 'RECOVERY' && isset($activeFailures[$key])) {
                $duration = (int)$ev['timestamp'] - $activeFailures[$key];
                $totalDowntimeSec += $duration;
                $totalResolutionTimeSec += $duration;
                $resolvedCount++;
                unset($activeFailures[$key]);
            }
        }

        $mttrSec = $resolvedCount > 0 ? round($totalResolutionTimeSec / $resolvedCount) : 0;

        // 4. Save Rollup Record
        $stmtRollup = $db->prepare("INSERT INTO daily_rollups (date_stamp, sla_average, total_incidents, total_downtime_sec, total_acks, mttr_sec) VALUES (?, ?, ?, ?, ?, ?) ON CONFLICT(date_stamp) DO UPDATE SET sla_average = EXCLUDED.sla_average, total_incidents = EXCLUDED.total_incidents, total_downtime_sec = EXCLUDED.total_downtime_sec, total_acks = EXCLUDED.total_acks, mttr_sec = EXCLUDED.mttr_sec");
        $stmtRollup->execute([$targetDate, $avgSla, $incCount, $totalDowntimeSec, $ackCount, $mttrSec]);
        $stmtRollup = null;

        ncc_log("📊 Daily Summary Aggregated for {$targetDate}: SLA {$avgSla}%, Incidents: {$incCount}, Acks: {$ackCount}", "\e[32m");
    } catch (Exception $e) {
        ncc_log("Daily Rollup Failed for {$targetDate}: " . $e->getMessage(), "\e[31m");
    }
}

function synthesizeAiResponse($eventType, $baselineText, $incidents, $recoveries) {
    global $ollama_url;

    if (!file_exists('/usr/local/share/nems/nems-ai/api.php')) {
        return ['text' => $baselineText, 'is_ai' => false];
    }

    $prompt = "You are NEMS AI, a plain-spoken NOC voice engineer.\n"
            . "Synthesize this event in natural human language (under 20 words): '{$baselineText}'\n"
            . "DIRECTIVES: Speak naturally. Do NOT use slashes. Write ONLY the spoken sentence.";

    ncc_log("🤖 NEMS AI Synthesizing -> \"{$baselineText}\"", "\e[35m");

    $ch = curl_init($ollama_url);
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
    curl_setopt($ch, CURLOPT_POST, true);
    curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 2);
    curl_setopt($ch, CURLOPT_TIMEOUT, 10);
    curl_setopt($ch, CURLOPT_HTTPHEADER, ['Content-Type: application/json']);
    curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode([
        'model' => 'nems-ai',
        'prompt' => $prompt,
        'stream' => false,
        'options' => ['num_predict' => 60, 'temperature' => 0.2, 'num_thread' => 2]
    ]));

    $res = curl_exec($ch);
    $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
    curl_close($ch);

    if ($res && $httpCode === 200) {
        $json = json_decode($res, true);
        $speech = trim($json['response'] ?? '');

        $speech = preg_replace('/[*_#`"\r\n]/', '', $speech);
        $speech = preg_replace('/\s+/', ' ', $speech);

        if (!empty($speech)) {
            ncc_log("🤖 NEMS AI Returned <- \"{$speech}\"", "\e[32m");
            return ['text' => $speech, 'is_ai' => true];
        }
    }

    ncc_log("AI request failed or timed out. Falling back to baseline.", "\e[31m");
    return ['text' => $baselineText, 'is_ai' => false];
}

$previousStates = [];
$previousAckStates = [];
$previousActiveClients = [];
$isFirstRun = true;
$apiErrorLogged = false;
$apiConsecutiveFailures = 0;
$lastRollupDate = null;

ncc_log("⚡ NEMS Central Command Daemon started. Monitoring active...", "\e[36m");
if (!file_exists('/usr/local/share/nems/nems-ai/api.php')) {
    ncc_log("NEMS AI engine not found. Using baseline text.", "\e[33m");
}

while (true) {
    $now = time();

    // Memory Guard & Garbage Collection
    gc_collect_cycles();
    if (memory_get_usage(true) > 67108864) {
        ncc_log("⚠️ Memory threshold exceeded (64MB). Restarting daemon worker cleanly...", "\e[33m");
        exit(0);
    }

    // Daily 00:01 AM Rollup Execution
    $currentLocalDate = date('Y-m-d');
    if (date('H:i') === '00:01' && $lastRollupDate !== $currentLocalDate) {
        $yesterday = date('Y-m-d', strtotime('-1 day'));
        performDailyRollup($yesterday);
        $lastRollupDate = $currentLocalDate;
    }

    // 0. Update Daemon Heartbeat Status
    $db = getDb();
    if ($db) {
        try {
            $stmtHb = $db->prepare("INSERT INTO daemon_status (id, last_heartbeat) VALUES (1, ?) ON CONFLICT(id) DO UPDATE SET last_heartbeat = EXCLUDED.last_heartbeat");
            $stmtHb->execute([$now]);
            $stmtHb = null;
        } catch (Exception $e) {}

        // 1. Session Connection Monitoring
        try {
            $activeTimeout = $now - 90;
            $stmtSessions = $db->prepare("SELECT client_id, ip_address FROM active_sessions WHERE last_seen >= ?");
            $stmtSessions->execute([$activeTimeout]);
            $currentSessions = $stmtSessions->fetchAll(PDO::FETCH_ASSOC);
            $stmtSessions = null;

            $currentClientMap = [];
            foreach ($currentSessions as $s) {
                $currentClientMap[$s['client_id']] = $s['ip_address'];
            }

            foreach ($currentClientMap as $cid => $ip) {
                if (!isset($previousActiveClients[$cid])) {
                    $shortId = substr($cid, 0, 12);
                    $totalActive = count($currentClientMap);
                    ncc_log("🌐 CLIENT CONNECTED: Console session {$shortId} ({$ip}) | Active Consoles: {$totalActive}", "\e[34m");
                }
            }

            foreach ($previousActiveClients as $cid => $ip) {
                if (!isset($currentClientMap[$cid])) {
                    $shortId = substr($cid, 0, 12);
                    $totalActive = count($currentClientMap);
                    ncc_log("❌ CLIENT DISCONNECTED: Console session {$shortId} timed out | Active Consoles: {$totalActive}", "\e[33m");
                }
            }

            $previousActiveClients = $currentClientMap;
            $db->exec("DELETE FROM active_sessions WHERE last_seen < " . ($now - 300));
        } catch (Exception $e) {
            ncc_log("Session update error: " . $e->getMessage(), "\e[31m");
        }
    }

    // 2. Poll Nagios Core Telemetry & Comments
    $hostsData = curlFetchJson('http://127.0.0.1/nems-api/hosts?Columns=name,alias,state,address,plugin_output,last_state_change,acknowledged');
    $svcsData = curlFetchJson('http://127.0.0.1/nems-api/services?Columns=host_name,description,state,plugin_output,perf_data,last_state_change,acknowledged');
    $commentsData = curlFetchJson('http://127.0.0.1/nems-api/comments?Columns=host_name,service_description,author,comment,entry_type');

    if (!$hostsData || !$svcsData) {
        $apiConsecutiveFailures++;
        if ($apiConsecutiveFailures >= 3 && !$apiErrorLogged) {
            ncc_log("API Error: Unable to fetch host/service telemetry from NEMS API.", "\e[31m");
            $apiErrorLogged = true;
        }
    } else {
        if ($apiErrorLogged) {
            ncc_log("API Connection restored.", "\e[32m");
            $apiErrorLogged = false;
        }
        $apiConsecutiveFailures = 0;

        $hosts = $hostsData['content'] ?? [];
        $services = $svcsData['content'] ?? [];
        $comments = $commentsData['content'] ?? [];

        // Build Comment Map
        $commentMap = [];
        foreach ($comments as $cm) {
            $cHost = $cm['host_name'] ?? '';
            $cSvc = $cm['service_description'] ?? '';
            $cKey = empty($cSvc) ? "HOST_" . $cHost : "SVC_" . $cHost . "_" . $cSvc;
            $commentMap[$cKey] = ['author' => $cm['author'] ?? 'Operator', 'memo' => $cm['comment'] ?? ''];
        }

        $hUp = count(array_filter($hosts, fn($h) => $h['state'] == 0));
        $sOk = count(array_filter($services, fn($s) => $s['state'] == 0));
        $totalObj = count($hosts) + count($services);
        $overallSla = $totalObj > 0 ? round((($hUp + $sOk) / $totalObj) * 100) : 100;

        // 3. Log History Snapshots (Rolling Dashboard Windows)
        $db = getDb();
        if ($db) {
            try {
                $fiveMinBucket = floor($now / 300) * 300;
                $thirtyMinBucket = floor($now / 1800) * 1800;

                $stmt = $db->prepare("SELECT sla_value FROM sla_snapshots WHERE bucket_time = ?");
                $stmt->execute([$fiveMinBucket]);
                $existingSla = $stmt->fetchColumn();
                $stmt = null;

                if ($existingSla !== false) {
                    $stmtUp = $db->prepare("UPDATE sla_snapshots SET sla_value = ? WHERE bucket_time = ?");
                    $stmtUp->execute([min((int)$existingSla, $overallSla), $fiveMinBucket]);
                    $stmtUp = null;
                } else {
                    $stmtIn = $db->prepare("INSERT INTO sla_snapshots (bucket_time, sla_value) VALUES (?, ?)");
                    $stmtIn->execute([$fiveMinBucket, $overallSla]);
                    $stmtIn = null;
                }

                foreach ($hosts as $h) {
                    $hName = $h['name'];
                    $hAck = (int)($h['acknowledged'] ?? 0);
                    $hSvcs = array_values(array_filter($services, fn($s) => $s['host_name'] === $hName));
                    $badSvcs = array_filter($hSvcs, fn($s) => (int)$s['state'] != 0);

                    if ((int)$h['state'] === 0 && empty($badSvcs)) {
                        $hState = 0;
                    } else {
                        $hostUnack = ((int)$h['state'] != 0 && $hAck === 0);
                        $hasUnackSvc = false;
                        foreach ($badSvcs as $bs) {
                            if ((int)($bs['acknowledged'] ?? 0) === 0) {
                                $hasUnackSvc = true;
                                break;
                            }
                        }
                        $hState = ($hostUnack || $hasUnackSvc) ? 2 : 1;
                    }

                    $stmtHu = $db->prepare("INSERT INTO host_snapshots (bucket_time, host_name, state_code) VALUES (?, ?, ?) ON CONFLICT(bucket_time, host_name) DO UPDATE SET state_code = EXCLUDED.state_code");
                    $stmtHu->execute([$thirtyMinBucket, $hName, $hState]);
                    $stmtHu = null;
                }

                // Clean 24-hour window for rolling SLA/Host gauges, while event_ledger and daily_rollups remain permanent
                $pruneTime = $now - 86400;
                $db->exec("DELETE FROM sla_snapshots WHERE bucket_time < {$pruneTime}");
                $db->exec("DELETE FROM host_snapshots WHERE bucket_time < {$pruneTime}");
                $db->exec("DELETE FROM notification_feed WHERE timestamp < {$pruneTime}");
            } catch (Exception $e) {
                ncc_log("Snapshot logging error: " . $e->getMessage(), "\e[31m");
            }
        }

        // 4. Detect State Changes, Log Permanent Events & Trigger Alerts
        if (!$isFirstRun) {
            $newIncidents = [];
            $newRecoveries = [];
            $newAcks = [];

            foreach ($hosts as $h) {
                $key = "HOST_" . $h['name'];
                $prev = $previousStates[$key] ?? 0;
                $prevAck = $previousAckStates[$key] ?? 0;
                $currAck = (int)($h['acknowledged'] ?? 0);

                if ($prev === 0 && $h['state'] !== 0) {
                    $newIncidents[] = ['host' => $h['name'], 'alias' => $h['alias'], 'checkName' => 'HOST DOWN', 'stateCode' => $h['state'], 'msg' => $h['plugin_output']];
                    logLedgerEvent('INCIDENT', $h['name'], 'HOST DOWN', $h['state'], $h['plugin_output']);
                } elseif ($prev !== 0 && $h['state'] === 0) {
                    $newRecoveries[] = ['host' => $h['name'], 'alias' => $h['alias'], 'checkName' => 'HOST DOWN', 'msg' => $h['plugin_output']];
                    logLedgerEvent('RECOVERY', $h['name'], 'HOST DOWN', 0, $h['plugin_output']);
                }

                if ($prevAck === 0 && $currAck === 1 && $h['state'] !== 0) {
                    $cm = $commentMap[$key] ?? ['author' => 'Operator', 'memo' => ''];
                    $newAcks[] = ['target' => $h['alias'] ?? $h['name'], 'checkName' => 'Host', 'author' => $cm['author'], 'memo' => $cm['memo']];
                    logLedgerEvent('ACK', $h['name'], 'HOST DOWN', $h['state'], $h['plugin_output'], $cm['author'], $cm['memo']);
                }

                $previousStates[$key] = (int)$h['state'];
                $previousAckStates[$key] = $currAck;
            }

            foreach ($services as $s) {
                $key = "SVC_" . $s['host_name'] . "_" . $s['description'];
                $prev = $previousStates[$key] ?? 0;
                $prevAck = $previousAckStates[$key] ?? 0;
                $currAck = (int)($s['acknowledged'] ?? 0);

                if ($prev === 0 && $s['state'] !== 0) {
                    $parentHost = array_values(array_filter($hosts, fn($h) => $h['name'] === $s['host_name']))[0] ?? null;
                    $newIncidents[] = ['host' => $s['host_name'], 'alias' => $parentHost['alias'] ?? $s['host_name'], 'checkName' => $s['description'], 'stateCode' => $s['state'], 'msg' => $s['plugin_output']];
                    logLedgerEvent('INCIDENT', $s['host_name'], $s['description'], $s['state'], $s['plugin_output']);
                } elseif ($prev !== 0 && $s['state'] === 0) {
                    $parentHost = array_values(array_filter($hosts, fn($h) => $h['name'] === $s['host_name']))[0] ?? null;
                    $newRecoveries[] = ['host' => $s['host_name'], 'alias' => $parentHost['alias'] ?? $s['host_name'], 'checkName' => $s['description'], 'msg' => $s['plugin_output']];
                    logLedgerEvent('RECOVERY', $s['host_name'], $s['description'], 0, $s['plugin_output']);
                }

                if ($prevAck === 0 && $currAck === 1 && $s['state'] !== 0) {
                    $parentHost = array_values(array_filter($hosts, fn($h) => $h['name'] === $s['host_name']))[0] ?? null;
                    $cm = $commentMap[$key] ?? ['author' => 'Operator', 'memo' => ''];
                    $newAcks[] = ['target' => $parentHost['alias'] ?? $s['host_name'], 'checkName' => $s['description'], 'author' => $cm['author'], 'memo' => $cm['memo']];
                    logLedgerEvent('ACK', $s['host_name'], $s['description'], $s['state'], $s['plugin_output'], $cm['author'], $cm['memo']);
                }

                $previousStates[$key] = (int)$s['state'];
                $previousAckStates[$key] = $currAck;
            }

            // Process New Acknowledgements
            foreach ($newAcks as $ackItem) {
                $checkTitle = ($ackItem['checkName'] === 'Host') ? "Host" : "Service {$ackItem['checkName']}";
                $ackText = "{$checkTitle} on {$ackItem['target']} acknowledged by {$ackItem['author']}.";
                if (!empty($ackItem['memo'])) {
                    $ackText .= " Note: {$ackItem['memo']}";
                }
                ncc_log("🤝 ACKNOWLEDGEMENT: {$ackItem['target']} ({$ackItem['checkName']}) by {$ackItem['author']}", "\e[36m");
                $aiResult = synthesizeAiResponse('ack', $ackText, [], []);
                writeNotification('ack', '[ACKNOWLEDGEMENT]', $aiResult['text'], $aiResult['text'], 'ok', $aiResult['is_ai'] ? 1 : 0);
            }

            if (!empty($newIncidents)) {
                $inc = $newIncidents[0];
                $hostAlias = $inc['alias'] ?? $inc['host'];
                $targetName = count($newIncidents) === 1 ? $hostAlias : count($newIncidents) . " items";

                if (count($newIncidents) === 1) {
                    if ($inc['checkName'] === 'HOST DOWN') {
                        $baselineText = "Server {$hostAlias} is offline.";
                    } else {
                        $statusWord = ($inc['stateCode'] == 1) ? "warning" : "issue";
                        $baselineText = "Service {$inc['checkName']} on {$hostAlias} has a {$statusWord}.";
                    }
                } else {
                    $baselineText = count($newIncidents) . " active incidents detected across monitored nodes.";
                }

                ncc_log("🚨 INCIDENT DETECTED: {$targetName}", "\e[31m");
                $aiResult = synthesizeAiResponse('batch_incidents', $baselineText, $newIncidents, []);
                $stateClass = ($inc['stateCode'] == 1) ? 'warn' : 'crit';
                writeNotification('incident', '[ALERT TRANSMISSION]', $aiResult['text'], $aiResult['text'], $stateClass, $aiResult['is_ai'] ? 1 : 0);
            }

            if (!empty($newRecoveries)) {
                $rec = $newRecoveries[0];
                $hostAlias = $rec['alias'] ?? $rec['host'];
                $targetName = count($newRecoveries) === 1 ? $hostAlias : count($newRecoveries) . " items";

                if (count($newRecoveries) === 1) {
                    if ($rec['checkName'] === 'HOST DOWN') {
                        $baselineText = "Server {$hostAlias} is back online.";
                    } else {
                        $baselineText = "Service {$rec['checkName']} on {$hostAlias} has recovered.";
                    }
                } else {
                    $baselineText = count($newRecoveries) . " services have recovered on monitored nodes.";
                }

                ncc_log("✅ RECOVERY DETECTED: {$targetName}", "\e[32m");
                $aiResult = synthesizeAiResponse('batch_recoveries', $baselineText, [], $newRecoveries);
                writeNotification('recovery', '[RECOVERY TRANSMISSION]', $aiResult['text'], $aiResult['text'], 'ok', $aiResult['is_ai'] ? 1 : 0);
            }
        } else {
            foreach ($hosts as $h) {
                $previousStates["HOST_" . $h['name']] = (int)$h['state'];
                $previousAckStates["HOST_" . $h['name']] = (int)($h['acknowledged'] ?? 0);
            }
            foreach ($services as $s) {
                $previousStates["SVC_" . $s['host_name'] . "_" . $s['description']] = (int)$s['state'];
                $previousAckStates["SVC_" . $s['host_name'] . "_" . $s['description']] = (int)($s['acknowledged'] ?? 0);
            }

            ncc_log("Initial baseline recorded for " . count($hosts) . " host nodes. Permanent event logging enabled.", "\e[36m");
            $isFirstRun = false;
        }
    }

    sleep(5);
}
