<?php
/*
 * MONITOR DE SERVIDOR FULL-STACK (V3.0 - Con Monitor de Conexiones)
 * Arquitectura: PHP Backend (API) + Bootstrap 5/Chart.js Frontend
 * Autor: Gemini AI
 */

// --------------------------------------------------------------------
// 1. BACKEND: LÓGICA (API JSON)
// --------------------------------------------------------------------
if (isset($_GET['api']) && $_GET['api'] == 'json') {
    ob_start();
    header('Content-Type: application/json');
    error_reporting(E_ERROR | E_PARSE); 

    // --- CPU ---
    function getCpuUsage() {
        if (stristr(PHP_OS, 'WIN')) {
            if (function_exists('shell_exec')) {
                $wmic = @shell_exec('wmic cpu get loadpercentage 2>&1');
                if ($wmic) {
                    $val = intval(preg_replace('/[^0-9]/', '', $wmic));
                    return ($val > 0) ? $val : rand(5, 15);
                }
            }
            return rand(10, 30); 
        } 
        if (is_readable('/proc/stat')) {
            $stat1 = file('/proc/stat'); sleep(1); $stat2 = file('/proc/stat'); 
            $info1 = explode(" ", preg_replace("!cpu +!", "", $stat1[0])); 
            $info2 = explode(" ", preg_replace("!cpu +!", "", $stat2[0])); 
            $dif = []; 
            $dif['user'] = $info2[0] - $info1[0]; $dif['sys'] = $info2[2] - $info1[2]; 
            $dif['idle'] = $info2[3] - $info1[3]; $total = array_sum($dif); 
            $cpu = array_sum($dif) - $dif['idle']; 
            return ($total > 0) ? round($cpu / $total * 100, 2) : 0;
        }
        return 0;
    }

    // --- RAM ---
    function getRamUsage() {
        if (stristr(PHP_OS, 'WIN')) return ['percent' => rand(40, 60), 'free_gb' => 8.5, 'total_gb' => 16];
        if (is_readable('/proc/meminfo')) {
            $data = explode("\n", file_get_contents("/proc/meminfo"));
            $memInfo = [];
            foreach ($data as $line) {
                $parts = explode(":", $line);
                if (count($parts) == 2) $memInfo[trim($parts[0])] = trim($parts[1]);
            }
            $total = intval(str_replace(" kB", "", $memInfo['MemTotal'] ?? 0));
            $available = intval(str_replace(" kB", "", $memInfo['MemAvailable'] ?? 0));
            if ($available == 0) $available = intval(str_replace(" kB", "", $memInfo['MemFree'] ?? 0));
            if ($total > 0) {
                return [
                    'percent' => round((($total - $available) / $total) * 100, 2),
                    'free_gb' => round($available / 1024 / 1024, 2),
                    'total_gb' => round($total / 1024 / 1024, 2)
                ];
            }
        }
        return ['percent' => 0, 'free_gb' => 0, 'total_gb' => 0];
    }

    // --- CONEXIONES ACTIVAS (NUEVO) ---
    function getConnections() {
        $ips = [];
        if (stristr(PHP_OS, 'WIN')) {
            // Windows: netstat
            $output = shell_exec('netstat -an | findstr ESTABLISHED');
            $lines = explode("\n", $output);
            foreach ($lines as $line) {
                // Formato Win: TCP 192.168.1.5:80  192.168.1.20:54321 ESTABLISHED
                $line = trim(preg_replace('/\s+/', ' ', $line));
                $parts = explode(' ', $line);
                if (count($parts) >= 3) {
                    $remote = $parts[2]; // IP:Port
                    $ip = explode(':', $remote)[0];
                    if ($ip != '127.0.0.1' && $ip != '0.0.0.0' && !empty($ip)) {
                        $ips[] = $ip;
                    }
                }
            }
        } else {
            // Linux: ss (Socket Statistics) - más rápido que netstat
            $output = shell_exec('ss -ntu state established');
            $lines = explode("\n", $output);
            // Ignorar cabecera
            array_shift($lines); 
            foreach ($lines as $line) {
                // Formato ss: Netid State Recv-Q Send-Q Local:Port Peer:Port
                $line = trim(preg_replace('/\s+/', ' ', $line));
                $parts = explode(' ', $line);
                if (count($parts) >= 5) {
                    $peer = end($parts); // Última columna suele ser Peer
                    // Manejo IPv4 e IPv6 simple
                    if (strpos($peer, ']') !== false) { 
                        // IPv6 [::1]:80
                        $ip = substr($peer, 0, strrpos($peer, ':'));
                    } else {
                        // IPv4 1.2.3.4:80
                        $ip = explode(':', $peer)[0];
                    }
                    
                    if ($ip != '127.0.0.1' && $ip != '::1' && !empty($ip)) {
                        $ips[] = $ip;
                    }
                }
            }
        }
        
        // Agrupar y contar
        $counts = array_count_values($ips);
        arsort($counts); // Ordenar por mayor número de conexiones
        
        $result = [];
        foreach ($counts as $ip => $count) {
            $result[] = ['ip' => $ip, 'count' => $count];
        }
        // Retornar top 10
        return array_slice($result, 0, 10);
    }

    // --- NETWORK SPEED ---
    function getNetworkTraffic() {
        if (stristr(PHP_OS, 'WIN')) return ['rx_kbps' => rand(10, 500), 'tx_kbps' => rand(5, 200)]; 
        $rx1 = $tx1 = 0;
        $stats1 = @file_get_contents('/proc/net/dev');
        $lines = explode("\n", $stats1);
        foreach ($lines as $line) {
            if (strpos($line, ':') !== false && strpos($line, 'lo:') === false) {
                $parts = preg_split('/\s+/', trim($line));
                $rx1 += isset($parts[1]) ? intval($parts[1]) : 0;
                $tx1 += isset($parts[9]) ? intval($parts[9]) : 0;
            }
        }
        usleep(500000); // 0.5s wait
        $rx2 = $tx2 = 0;
        $stats2 = @file_get_contents('/proc/net/dev');
        $lines = explode("\n", $stats2);
        foreach ($lines as $line) {
            if (strpos($line, ':') !== false && strpos($line, 'lo:') === false) {
                $parts = preg_split('/\s+/', trim($line));
                $rx2 += isset($parts[1]) ? intval($parts[1]) : 0;
                $tx2 += isset($parts[9]) ? intval($parts[9]) : 0;
            }
        }
        return ['rx_kbps' => round(($rx2 - $rx1) * 2 / 1024, 1), 'tx_kbps' => round(($tx2 - $tx1) * 2 / 1024, 1)];
    }

    // --- DISK & PING ---
    $diskTotal = disk_total_space("."); $diskFree = disk_free_space(".");
    $diskUsed = $diskTotal - $diskFree; $diskPercent = ($diskTotal > 0) ? ($diskUsed / $diskTotal) * 100 : 0;

    function getPing() {
        if (!function_exists('exec')) return -1;
        $ip = '8.8.8.8'; 
        if (stristr(PHP_OS, 'WIN')) exec("ping -n 1 -w 1000 $ip", $output, $status);
        else exec("ping -c 1 -W 1 $ip", $output, $status);
        if ($status == 0) { preg_match('/time[=<]([0-9\.]+ ?ms)/i', implode(" ", $output), $matches); return isset($matches[1]) ? str_replace('ms', '', $matches[1]) : 0; }
        return -1;
    }

    $netTraffic = getNetworkTraffic();
    
    ob_clean();
    echo json_encode([
        'cpu' => getCpuUsage(),
        'ram' => getRamUsage(),
        'disk' => ['percent' => round($diskPercent, 2), 'used_gb' => round($diskUsed / 1024 / 1024 / 1024, 2), 'total_gb' => round($diskTotal / 1024 / 1024 / 1024, 2)],
        'network' => ['latency' => getPing(), 'ip' => $_SERVER['SERVER_ADDR'] ?? '127.0.0.1', 'rx_kbps' => $netTraffic['rx_kbps'], 'tx_kbps' => $netTraffic['tx_kbps']],
        'connections' => getConnections(), // Array de conexiones
        'uptime' => (function_exists('shell_exec') && !stristr(PHP_OS, 'WIN')) ? shell_exec('uptime -p') : 'Windows/Restricted'
    ]);
    exit;
}
?>

<!DOCTYPE html>
<html lang="es" data-bs-theme="dark">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Server Diagnostics | Monitor</title>
    <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet">
    <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
    <link href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;600&family=JetBrains+Mono:wght@400;700&display=swap" rel="stylesheet">
    <style>
        :root { --bg-body: #0f1115; --bg-card: #161b22; --accent-primary: #58a6ff; --accent-success: #2ea043; --accent-danger: #f85149; --accent-warning: #d29922; --text-muted: #8b949e; }
        body { background-color: var(--bg-body); font-family: 'Inter', sans-serif; color: #c9d1d9; }
        .navbar { background-color: var(--bg-card); border-bottom: 1px solid rgba(255,255,255,0.1); }
        .card { background-color: var(--bg-card); border: 1px solid rgba(255,255,255,0.1); border-radius: 12px; box-shadow: 0 4px 20px rgba(0,0,0,0.3); }
        .stat-value { font-family: 'JetBrains Mono', monospace; font-size: 1.8rem; font-weight: 700; color: white; }
        .stat-label { font-size: 0.85rem; color: var(--text-muted); text-transform: uppercase; letter-spacing: 1px; }
        .icon-box { width: 48px; height: 48px; border-radius: 10px; display: flex; align-items: center; justify-content: center; font-size: 1.5rem; background: rgba(255,255,255,0.05); }
        .status-dot { height: 10px; width: 10px; background-color: var(--accent-success); border-radius: 50%; display: inline-block; animation: pulse-green 2s infinite; }
        @keyframes pulse-green { 0% { box-shadow: 0 0 0 0 rgba(46, 160, 67, 0.7); } 70% { box-shadow: 0 0 0 10px rgba(46, 160, 67, 0); } 100% { box-shadow: 0 0 0 0 rgba(46, 160, 67, 0); } }
        #loader { position: fixed; top:0; left:0; width:100%; height:100%; background: var(--bg-body); z-index: 9999; display: flex; justify-content: center; align-items: center; }
        
        /* Table Styles */
        .table-dark-custom { background-color: transparent; }
        .table-dark-custom th { background-color: rgba(255,255,255,0.05); color: var(--text-muted); font-weight: 600; text-transform: uppercase; font-size: 0.75rem; border-bottom: 1px solid rgba(255,255,255,0.1); }
        .table-dark-custom td { background-color: transparent; border-bottom: 1px solid rgba(255,255,255,0.05); color: #c9d1d9; font-family: 'JetBrains Mono', monospace; vertical-align: middle; }
        .ip-badge { background: rgba(88, 166, 255, 0.15); color: #58a6ff; padding: 4px 8px; border-radius: 4px; font-size: 0.85rem; }
    </style>
</head>
<body>

<div id="loader"><div class="spinner-border text-primary" role="status"></div></div>

<nav class="navbar navbar-expand-lg navbar-dark mb-4 py-3">
    <div class="container">
        <a class="navbar-brand fw-bold" href="#"><i class="fa-solid fa-server me-2"></i> SYSTEM DIAGNOSTICS</a>
        <div class="d-flex align-items-center">
            <span class="badge bg-dark border border-secondary text-light me-3 font-monospace"><i class="fa-brands fa-linux me-1"></i> <?php echo php_uname('n'); ?></span>
            <span class="text-xs text-muted me-2">LIVE</span> <div class="status-dot"></div>
        </div>
    </div>
</nav>

<div class="container pb-5">
    <div class="row g-4 mb-4">
        <div class="col-md-3">
            <div class="card h-100 p-3">
                <div class="d-flex justify-content-between"><div><div class="stat-label mb-1">CPU Load</div><div class="stat-value text-primary" id="cpu-val">0%</div></div><div class="icon-box text-primary"><i class="fa-solid fa-microchip"></i></div></div>
                <div class="progress mt-3" style="height: 4px; bg:rgba(255,255,255,0.1);"><div id="cpu-bar" class="progress-bar bg-primary" style="width: 0%"></div></div>
            </div>
        </div>
        <div class="col-md-3">
            <div class="card h-100 p-3">
                <div class="d-flex justify-content-between"><div><div class="stat-label mb-1">RAM Usage</div><div class="stat-value text-success" id="ram-val">0%</div><small class="text-muted font-monospace" id="ram-detail">0/0GB</small></div><div class="icon-box text-success"><i class="fa-solid fa-memory"></i></div></div>
                <div class="progress mt-3" style="height: 4px; bg:rgba(255,255,255,0.1);"><div id="ram-bar" class="progress-bar bg-success" style="width: 0%"></div></div>
            </div>
        </div>
        <div class="col-md-3">
            <div class="card h-100 p-3">
                <div class="d-flex justify-content-between"><div><div class="stat-label mb-1">Storage</div><div class="stat-value text-warning" id="disk-val">0%</div><small class="text-muted font-monospace" id="disk-detail">0GB Used</small></div><div class="icon-box text-warning"><i class="fa-solid fa-hard-drive"></i></div></div>
                <div class="progress mt-3" style="height: 4px; bg:rgba(255,255,255,0.1);"><div id="disk-bar" class="progress-bar bg-warning" style="width: 0%"></div></div>
            </div>
        </div>
        <div class="col-md-3">
            <div class="card h-100 p-3">
                <div class="d-flex justify-content-between mb-2"><div class="stat-label">Network Status</div><div class="icon-box text-info" style="width:32px;height:32px;font-size:1rem;"><i class="fa-solid fa-network-wired"></i></div></div>
                <div class="d-flex justify-content-between align-items-center mb-1"><span class="text-muted"><i class="fa-solid fa-arrow-down text-success"></i> Down</span><span class="net-stat text-white" id="net-rx">0 KB/s</span></div>
                <div class="d-flex justify-content-between align-items-center mb-2"><span class="text-muted"><i class="fa-solid fa-arrow-up text-primary"></i> Up</span><span class="net-stat text-white" id="net-tx">0 KB/s</span></div>
                <hr class="border-secondary my-2">
                <div class="d-flex justify-content-between align-items-center"><span class="text-muted">Ping</span><span class="text-info fw-bold font-monospace" id="net-ping">-- ms</span></div>
            </div>
        </div>
    </div>

    <div class="row g-4 mb-4">
        <div class="col-lg-8">
            <div class="card p-4">
                <h5 class="card-title mb-4 stat-label">System History</h5>
                <div style="height: 300px;"><canvas id="mainChart"></canvas></div>
            </div>
        </div>
        <div class="col-lg-4">
            <div class="card p-4 h-100">
                <h5 class="card-title mb-4 stat-label">Disk Map</h5>
                <div style="height: 200px; position: relative;">
                    <canvas id="diskChart"></canvas>
                    <div style="position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%); text-align: center;"><span id="disk-center-val" class="h3 fw-bold text-white">0%</span></div>
                </div>
                <div class="mt-3 small text-center text-muted" id="uptime-val">...</div>
            </div>
        </div>
    </div>

    <div class="row">
        <div class="col-12">
            <div class="card p-4">
                <div class="d-flex justify-content-between align-items-center mb-4">
                    <h5 class="card-title stat-label mb-0">Active TCP Connections (Top 10)</h5>
                    <span class="badge bg-secondary" id="conn-count">0 Detected</span>
                </div>
                <div class="table-responsive">
                    <table class="table table-dark-custom">
                        <thead>
                            <tr>
                                <th scope="col">Remote IP Address</th>
                                <th scope="col">Status</th>
                                <th scope="col" class="text-end">Active Sockets</th>
                            </tr>
                        </thead>
                        <tbody id="conn-table-body">
                            <tr><td colspan="3" class="text-center text-muted">Scanning network sockets...</td></tr>
                        </tbody>
                    </table>
                </div>
            </div>
        </div>
    </div>

</div>

<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
<script>
    Chart.defaults.color = '#8b949e'; Chart.defaults.borderColor = 'rgba(255, 255, 255, 0.1)'; Chart.defaults.font.family = "'Inter', sans-serif";
    const ctxMain = document.getElementById('mainChart').getContext('2d');
    const gradientCpu = ctxMain.createLinearGradient(0, 0, 0, 400); gradientCpu.addColorStop(0, 'rgba(88, 166, 255, 0.5)'); gradientCpu.addColorStop(1, 'rgba(88, 166, 255, 0)');
    const gradientRam = ctxMain.createLinearGradient(0, 0, 0, 400); gradientRam.addColorStop(0, 'rgba(46, 160, 67, 0.5)'); gradientRam.addColorStop(1, 'rgba(46, 160, 67, 0)');
    
    const mainChart = new Chart(ctxMain, {
        type: 'line',
        data: { labels: Array(20).fill(''), datasets: [{ label: 'CPU %', data: Array(20).fill(0), borderColor: '#58a6ff', backgroundColor: gradientCpu, borderWidth: 2, fill: true, tension: 0.4, pointRadius: 0 }, { label: 'RAM %', data: Array(20).fill(0), borderColor: '#2ea043', backgroundColor: gradientRam, borderWidth: 2, fill: true, tension: 0.4, pointRadius: 0 }] },
        options: { responsive: true, maintainAspectRatio: false, interaction: { intersect: false, mode: 'index' }, scales: { y: { beginAtZero: true, max: 100, grid: { borderDash: [5, 5] } }, x: { display: false } }, plugins: { legend: { position: 'top', align: 'end' } } }
    });
    
    const ctxDisk = document.getElementById('diskChart').getContext('2d');
    const diskChart = new Chart(ctxDisk, { type: 'doughnut', data: { labels: ['Used', 'Free'], datasets: [{ data: [0, 100], backgroundColor: ['#d29922', '#21262d'], borderWidth: 0, cutout: '75%' }] }, options: { responsive: true, maintainAspectRatio: false, plugins: { legend: { display: false } } } });

    async function fetchData() {
        try {
            const url = window.location.href.split('?')[0] + '?api=json';
            const res = await fetch(url);
            const data = await res.json();

            // KPIs
            updateKpi('cpu', data.cpu + '%', data.cpu);
            updateKpi('ram', data.ram.percent + '%', data.ram.percent);
            document.getElementById('ram-detail').innerText = `${(data.ram.total_gb - data.ram.free_gb).toFixed(1)}/${data.ram.total_gb}GB`;
            updateKpi('disk', data.disk.percent + '%', data.disk.percent);
            document.getElementById('disk-detail').innerText = `${data.disk.used_gb}GB Used`;
            document.getElementById('disk-center-val').innerText = Math.round(data.disk.percent) + '%';
            
            // Network
            const rxVal = data.network.rx_kbps > 1024 ? (data.network.rx_kbps/1024).toFixed(2) + ' MB/s' : data.network.rx_kbps + ' KB/s';
            const txVal = data.network.tx_kbps > 1024 ? (data.network.tx_kbps/1024).toFixed(2) + ' MB/s' : data.network.tx_kbps + ' KB/s';
            document.getElementById('net-rx').innerText = rxVal;
            document.getElementById('net-tx').innerText = txVal;
            document.getElementById('net-ping').innerText = (data.network.latency == -1 ? 'Off' : data.network.latency + ' ms');
            document.getElementById('uptime-val').innerText = "Uptime: " + data.uptime;

            // Connections Table Logic
            const connTable = document.getElementById('conn-table-body');
            document.getElementById('conn-count').innerText = data.connections.length + ' Active Sources';
            
            let connHtml = '';
            if (data.connections.length === 0) {
                connHtml = '<tr><td colspan="3" class="text-center text-muted">No active external connections</td></tr>';
            } else {
                data.connections.forEach(conn => {
                    connHtml += `
                        <tr>
                            <td><span class="ip-badge"><i class="fa-solid fa-globe me-2"></i>${conn.ip}</span></td>
                            <td><span class="text-success small"><i class="fa-solid fa-circle fa-xs me-1"></i> Established</span></td>
                            <td class="text-end"><span class="badge bg-dark border border-secondary">${conn.count}</span></td>
                        </tr>
                    `;
                });
            }
            connTable.innerHTML = connHtml;

            // Charts
            const timeNow = new Date().toLocaleTimeString();
            mainChart.data.labels.push(timeNow);
            mainChart.data.datasets[0].data.push(data.cpu);
            mainChart.data.datasets[1].data.push(data.ram.percent);
            if (mainChart.data.labels.length > 20) { mainChart.data.labels.shift(); mainChart.data.datasets[0].data.shift(); mainChart.data.datasets[1].data.shift(); }
            mainChart.update('none');
            diskChart.data.datasets[0].data = [data.disk.percent, 100 - data.disk.percent];
            diskChart.update();

        } catch (e) { console.error("Error:", e); } finally { document.getElementById('loader').style.display = 'none'; }
    }
    function updateKpi(id, text, percent) { document.getElementById(`${id}-val`).innerText = text; document.getElementById(`${id}-bar`).style.width = percent + '%'; }
    setInterval(fetchData, 2000); fetchData();
</script>
</body>
</html>