<?php
declare(strict_types=1);

/**
 * SNMP-Status-API für das Dashboard.
 *
 * Nur authentifizierte Admins dürfen auf diesen Endpunkt zugreifen.
 * Wird alle 5s vom JavaScript im Dashboard aufgerufen.
 */

session_start();

// === Authentifizierung + Autorisierung ===
$sessionKeyUser = 'admin_user'; 
if (!isset($_SESSION[$sessionKeyUser])) {
    http_response_code(401);
    header('Content-Type: application/json; charset=utf-8');
    echo json_encode(['error' => 'unauthorized']);
    exit;
}

header('Content-Type: application/json; charset=utf-8');

// === Logging-Setup ===
$logDir = dirname(__DIR__) . DIRECTORY_SEPARATOR . 'logs';
if (!is_dir($logDir)) {
    @mkdir($logDir, 0755, true);
}
$logFile = $logDir . DIRECTORY_SEPARATOR . 'snmp_api.log';

function log_msg(string $msg): void {
    global $logFile;
    $timestamp = date('Y-m-d H:i:s');
    @file_put_contents($logFile, "[$timestamp] $msg\n", FILE_APPEND);
}

function rotate_log_if_needed(): void {
    global $logFile;
    $maxSize = 500 * 1024; // 500 KB
    if (file_exists($logFile) && filesize($logFile) > $maxSize) {
        @rename($logFile, $logFile . '.old');
        log_msg('=== Log rotiert ===');
    }
}

$configPath = __DIR__ . '/../../config/config.php';
if (!is_readable($configPath)) {
    echo json_encode(['error' => 'config_not_found']);
    exit;
}

$config = require $configPath;
$snmp = $config['snmp'] ?? [];

rotate_log_if_needed();

// === Cache-Logik (Datei) ===
$cacheDir = sys_get_temp_dir();
$cacheFile = $cacheDir . DIRECTORY_SEPARATOR . 'snmp_status_cache.json';
$cacheTTL = 10; // Sekunden

if (file_exists($cacheFile)) {
    $cacheAge = time() - filemtime($cacheFile);
    if ($cacheAge < $cacheTTL) {
        $cached = file_get_contents($cacheFile);
        if ($cached !== false) {
            echo $cached;
            exit;
        }
    }
}

// === SNMP Setup ===
$host = $snmp['host'] ?? '127.0.0.1';
$community = $snmp['community'] ?? 'public';

// Timeout von Sekunden in Mikrosekunden umrechnen (wichtig für PHP snmp Funktionen)
$timeoutSec = (int)($snmp['timeout'] ?? 2);
$timeoutMicro = $timeoutSec * 1_000_000; 
$retries = (int)($snmp['retries'] ?? 1);

if (!function_exists('snmpget')) {
    echo json_encode(['error' => 'snmp_extension_missing']);
    exit;
}

// Grundeinstellungen
snmp_set_oid_output_format(SNMP_OID_OUTPUT_NUMERIC);

// Hilfsfunktion: sichere snmpget-Rückgabe
function sget(string $host, string $community, string $oid, int $timeout, int $retries) {
    $v = @snmpget($host, $community, $oid, $timeout, $retries);
    if ($v === false || $v === null) return null;
    return is_string($v) ? trim($v) : $v;
}

// --- 1. Uptime ---
// Wir deaktivieren quick_print, damit wir das Format "Timeticks: (12345) 1 day..." erhalten.
// Nur so können wir die echten Ticks in der Klammer zuverlässig parsen.
snmp_set_quick_print(false);

$uptimeOid = $snmp['oids']['uptime'] ?? '1.3.6.1.2.1.1.3.0';
$uptimeRaw = @sget($host, $community, $uptimeOid, $timeoutMicro, $retries);
$upticks = null;

// Regex sucht nach Zahl in Klammern: (12345678)
if ($uptimeRaw && preg_match('/\((.*?)\)/', (string)$uptimeRaw, $matches)) {
    $upticks = (int)$matches[1];
}

function format_uptime(?int $ticks): ?string {
    if ($ticks === null) return null;
    $seconds = (int)floor($ticks / 100);
    $days = intdiv($seconds, 86400);
    $seconds -= $days * 86400;
    $hours = intdiv($seconds, 3600);
    $seconds -= $hours * 3600;
    $minutes = intdiv($seconds, 60);
    $seconds -= $minutes * 60;
    
    $parts = [];
    if ($days) $parts[] = $days . ' Tage';
    $parts[] = sprintf('%02d:%02d:%02d', $hours, $minutes, $seconds);
    return implode(', ', $parts);
}

$uptimeStr = format_uptime($upticks);

// Für den Rest aktivieren wir quick_print wieder, um "saubere" Werte zu bekommen
snmp_set_quick_print(true);


// --- 2. CPU (Walk über alle Kerne) ---
$cpuTable = $snmp['oids']['cpu_table'] ?? '1.3.6.1.2.1.25.3.3.1.2';
$cpuValues = @snmpwalk($host, $community, $cpuTable, $timeoutMicro, $retries);

$cpuUsage = 0; 
if (is_array($cpuValues) && count($cpuValues) > 0) {
    $totalLoad = 0;
    $coreCount = 0;
    foreach ($cpuValues as $val) {
        $v = (int)filter_var($val, FILTER_SANITIZE_NUMBER_INT);
        $totalLoad += $v;
        $coreCount++;
    }
    if ($coreCount > 0) {
        $cpuUsage = round($totalLoad / $coreCount, 2);
    }
}


// --- 3. Storage (Disk & RAM) ---
$descrOid = $snmp['oids']['storage_descr'] ?? '1.3.6.1.2.1.25.2.3.1.3';
$unitsOid = $snmp['oids']['storage_units'] ?? '1.3.6.1.2.1.25.2.3.1.4';
$sizeOid  = $snmp['oids']['storage_size']  ?? '1.3.6.1.2.1.25.2.3.1.5';
$usedOid  = $snmp['oids']['storage_used']  ?? '1.3.6.1.2.1.25.2.3.1.6';

$descrWalk = @snmprealwalk($host, $community, $descrOid, $timeoutMicro, $retries);
$unitsWalk = @snmprealwalk($host, $community, $unitsOid, $timeoutMicro, $retries);
$sizeWalk  = @snmprealwalk($host, $community, $sizeOid, $timeoutMicro, $retries);
$usedWalk  = @snmprealwalk($host, $community, $usedOid, $timeoutMicro, $retries);

$diskPercent = null;
$memPercent = null;
$storageEntries = []; // Fallback-Liste

if (is_array($descrWalk)) {
    foreach ($descrWalk as $descrOidFull => $descrRaw) {
        if (!preg_match('/\.(\d+)$/', $descrOidFull, $m)) continue;
        $idx = $m[1];
        
        // Bereinigen
        $descr = trim(str_ireplace('STRING:', '', (string)$descrRaw), ' "');
        
        // Helper zum Finden der Werte
        $findVal = function($walkArr, $idx) {
            if(!is_array($walkArr)) return null;
            foreach ($walkArr as $oid => $val) {
                if (preg_match('/\.(\d+)$/', $oid, $m2) && $m2[1] === $idx) {
                    return (int)filter_var($val, FILTER_SANITIZE_NUMBER_INT);
                }
            }
            return null;
        };

        $units = $findVal($unitsWalk, $idx);
        $size  = $findVal($sizeWalk, $idx);
        $used  = $findVal($usedWalk, $idx);

        if ($size === null || $units === null || $used === null || $size === 0) continue;

        $totalBytes = $size * $units;
        $usedBytes = $used * $units;
        $percent = ($totalBytes > 0) ? ($usedBytes / $totalBytes) * 100 : 0;

        $storageEntries[] = ['idx' => $idx, 'descr' => $descr, 'percent' => $percent, 'totalGB' => $totalBytes / (1024**3)];

        $lower = strtolower($descr);

        // DISK C: oder Root
        if ($diskPercent === null) {
            if (str_starts_with($lower, 'c:') || str_starts_with($lower, 'c:\\') || $lower === '/' || str_contains($lower, 'root')) {
                $diskPercent = $percent;
            }
        }

        // RAM
        if ($memPercent === null) {
            if (str_contains($lower, 'physical memory') || str_contains($lower, 'ram')) {
                $memPercent = $percent;
            }
        }
    }
}

// Fallback Disk: Größter Speicher > 5GB
if ($diskPercent === null && count($storageEntries) > 0) {
    usort($storageEntries, fn($a, $b) => $b['totalGB'] <=> $a['totalGB']);
    foreach($storageEntries as $entry) {
        if ($entry['totalGB'] > 5) {
            $diskPercent = $entry['percent'];
            break;
        }
    }
}

// --- 4. Hostname ---
$hostnameOid = '1.3.6.1.2.1.1.5.0'; 
$hostname = @sget($host, $community, $hostnameOid, $timeoutMicro, $retries);
if($hostname) $hostname = trim(str_ireplace('STRING:', '', $hostname), ' "');

// --- Ergebnis ---
$result = [
    'hostname'      => $hostname ?? 'n/a',
    'uptime'        => $uptimeStr,
    'upticks'       => $upticks,
    'cpu_usage'     => $cpuUsage,
    'memory_usage'  => $memPercent !== null ? round($memPercent, 2) : 0,
    'disk_usage_c'  => $diskPercent !== null ? round($diskPercent, 2) : 0,
    'last_update'   => time(),
];

log_msg('RESULT: UptimeRaw='.($uptimeRaw??'null').' CPU=' . $result['cpu_usage'] . ' Mem=' . $result['memory_usage']);

$resultJson = json_encode($result);
@file_put_contents($cacheFile, $resultJson);
echo $resultJson;