<?php
/**
 * DIAGNOSTICO ERROR 520 CLOUDFLARE - SUBDOMINIOS SIE
 * ====================================================
 * Identifica las causas mas comunes del error 520 (Web server returning unknown error)
 * que aparece cuando Cloudflare recibe una respuesta vacia, malformada o con timeout
 * del servidor origen.
 *
 * CAUSAS COMUNES:
 *   1. PHP fatal error / parse error (respuesta vacia)
 *   2. Memory exhaustion (PHP se queda sin RAM)
 *   3. Timeout de PHP (max_execution_time)
 *   4. .htaccess corrupto o con directivas invalidas
 *   5. Headers HTTP demasiado grandes (>8KB = Cloudflare rechaza)
 *   6. Disco lleno (no puede escribir sesion/cache)
 *   7. error_log enorme bloqueando I/O
 *   8. Permisos incorrectos en archivos criticos
 *   9. Archivo index.php corrupto o vacio
 *  10. Conexion DB fallando (SQLite lock, etc.)
 *
 * MODOS:
 *   ?modo=escanear   (default) - Solo detectar, no modificar nada
 *   ?sub=nombre                - Verificar uno solo
 *
 * SEGURIDAD: Solo lectura - este script NUNCA modifica archivos.
 */

// =====================================================================
//  CONFIGURACION
// =====================================================================

$basePath = '/home/systeng3/public_html';

$excluir = ['.', '..', 'cgi-bin', '.well-known', '.trash', '.htaccess',
            'respaldo', 'diagnosticos', 'tmp', 'error_log'];

// =====================================================================
//  PARAMETROS
// =====================================================================

$esCLI = (php_sapi_name() === 'cli');

if (!$esCLI) {
    header('Content-Type: text/html; charset=UTF-8');
}

$targetSub = $esCLI ? ($argv[1] ?? '') : ($_GET['sub'] ?? '');

if (!empty($targetSub)) {
    if (preg_match('#^https?://#i', $targetSub)) {
        $parsed = parse_url($targetSub);
        if (isset($parsed['host'])) {
            $parts = explode('.', $parsed['host']);
            $targetSub = $parts[0];
        }
    }
    $targetSub = basename(preg_replace('/[^a-zA-Z0-9._-]/', '', $targetSub));
}

// =====================================================================
//  FUNCIONES DE ANALISIS
// =====================================================================

/**
 * Verifica el espacio libre en disco
 */
function checkDisco($basePath)
{
    $free = @disk_free_space($basePath);
    $total = @disk_total_space($basePath);
    if ($free === false || $total === false) {
        return ['estado' => 'ERROR', 'sev' => 'MEDIO', 'libre_mb' => 0, 'total_mb' => 0, 'uso_pct' => 0, 'msg' => 'No se pudo obtener info del disco'];
    }
    $pct = round(($total - $free) / $total * 100, 1);
    $freeMB = round($free / 1024 / 1024, 0);

    $estado = 'OK';
    $sev = 'INFO';
    if ($freeMB < 100) {
        $estado = 'CRITICO';
        $sev = 'CRITICO';
    } elseif ($freeMB < 500) {
        $estado = 'ALERTA';
        $sev = 'MEDIO';
    }

    return [
        'estado'   => $estado,
        'sev'      => $sev,
        'libre_mb' => $freeMB,
        'total_mb' => round($total / 1024 / 1024, 0),
        'uso_pct'  => $pct,
        'msg'      => "Disco: {$freeMB} MB libres ({$pct}% usado)"
    ];
}

/**
 * Verifica configuracion PHP relevante para 520
 */
function checkPHPConfig()
{
    $problemas = [];

    $memLimit = ini_get('memory_limit');
    $memBytes = convertToBytes($memLimit);
    if ($memBytes > 0 && $memBytes < 128 * 1024 * 1024) {
        $problemas[] = ['sev' => 'MEDIO', 'causa' => 'memory_limit bajo', 'msg' => "memory_limit={$memLimit} (recomendado: 256M+)", 'solucion' => 'Aumentar en php.ini o .htaccess: php_value memory_limit 256M'];
    }

    $maxExec = ini_get('max_execution_time');
    if ($maxExec > 0 && $maxExec < 60) {
        $problemas[] = ['sev' => 'MEDIO', 'causa' => 'max_execution_time bajo', 'msg' => "max_execution_time={$maxExec}s (recomendado: 120+)", 'solucion' => 'Aumentar en php.ini: max_execution_time = 120'];
    }

    $postMax = ini_get('post_max_size');
    $uploadMax = ini_get('upload_max_filesize');

    $errorLog = ini_get('error_log');
    if (!empty($errorLog) && file_exists($errorLog)) {
        $logSize = @filesize($errorLog);
        if ($logSize > 100 * 1024 * 1024) {
            $sizeMB = round($logSize / 1024 / 1024, 0);
            $problemas[] = ['sev' => 'ALTO', 'causa' => 'error_log global enorme', 'msg' => "PHP error_log: {$sizeMB} MB — puede causar I/O lento", 'solucion' => 'Rotar o vaciar el error_log: truncate -s 0 ' . $errorLog];
        }
    }

    return [
        'memory_limit'       => $memLimit,
        'max_execution_time' => $maxExec,
        'post_max_size'      => $postMax,
        'upload_max_filesize' => $uploadMax,
        'display_errors'     => ini_get('display_errors'),
        'error_reporting'    => ini_get('error_reporting'),
        'problemas'          => $problemas,
    ];
}

/**
 * Analiza un subdominio en busca de causas de Error 520
 */
function analizarSubdominio($subPath, $carpeta)
{
    $r = [
        'carpeta'    => $carpeta,
        'tiene_doku' => false,
        'problemas'  => [],
        'info'       => [],
        'estado'     => 'NO_DOKU',
        'score'      => 0, // 0 = ok, mayor = peor
    ];

    if (!file_exists($subPath . '/doku.php')) return $r;
    $r['tiene_doku'] = true;
    $r['estado'] = 'OK';

    // ----- CHECK 1: Archivos criticos vacios o corruptos -----
    $criticos = [
        'doku.php'            => 'Punto de entrada principal',
        'index.php'           => 'Index principal',
        'inc/init.php'        => 'Inicializacion de DokuWiki',
        'inc/auth.php'        => 'Sistema de autenticacion',
        'vendor/autoload.php' => 'Autoloader de dependencias',
    ];

    foreach ($criticos as $file => $desc) {
        $fullPath = $subPath . '/' . $file;
        if (!file_exists($fullPath)) {
            $r['problemas'][] = [
                'sev' => 'CRITICO', 'causa' => 'Archivo critico faltante',
                'msg' => "{$file} no existe — {$desc}",
                'solucion' => "Restaurar {$file} desde backup o reinstalar DokuWiki"
            ];
            $r['score'] += 10;
        } elseif (filesize($fullPath) === 0) {
            $r['problemas'][] = [
                'sev' => 'CRITICO', 'causa' => 'Archivo critico vacio',
                'msg' => "{$file} tiene 0 bytes — PHP devolvera respuesta vacia",
                'solucion' => "Restaurar {$file} desde backup"
            ];
            $r['score'] += 10;
        } else {
            // Verificar sintaxis PHP si exec esta disponible
            if (function_exists('exec') && $file !== 'vendor/autoload.php') {
                $output = [];
                $retCode = 0;
                @exec('php -l ' . escapeshellarg($fullPath) . ' 2>&1', $output, $retCode);
                if ($retCode !== 0) {
                    $err = implode(' ', $output);
                    $r['problemas'][] = [
                        'sev' => 'CRITICO', 'causa' => 'Error de sintaxis PHP',
                        'msg' => "{$file}: {$err}",
                        'solucion' => "Corregir error de sintaxis en {$file}"
                    ];
                    $r['score'] += 10;
                }
            }
        }
    }

    // ----- CHECK 2: .htaccess problematico -----
    $htaccess = $subPath . '/.htaccess';
    if (file_exists($htaccess)) {
        $htContent = @file_get_contents($htaccess);
        if ($htContent === false) {
            $r['problemas'][] = [
                'sev' => 'ALTO', 'causa' => '.htaccess ilegible',
                'msg' => 'No se puede leer .htaccess',
                'solucion' => 'Verificar permisos: chmod 644 .htaccess'
            ];
            $r['score'] += 5;
        } else {
            $htSize = strlen($htContent);

            // .htaccess demasiado grande
            if ($htSize > 50000) {
                $sizeKB = round($htSize / 1024, 1);
                $r['problemas'][] = [
                    'sev' => 'MEDIO', 'causa' => '.htaccess muy grande',
                    'msg' => ".htaccess tiene {$sizeKB} KB — puede causar lentitud en cada request",
                    'solucion' => 'Revisar y limpiar reglas obsoletas del .htaccess'
                ];
                $r['score'] += 3;
            }

            // Reglas de rewrite infinitas (loop)
            if (preg_match_all('/RewriteRule/i', $htContent, $m) && count($m[0]) > 20) {
                $r['problemas'][] = [
                    'sev' => 'MEDIO', 'causa' => 'Exceso de RewriteRules',
                    'msg' => count($m[0]) . ' RewriteRules en .htaccess — riesgo de loop de redireccion',
                    'solucion' => 'Revisar reglas de rewrite y eliminar duplicadas'
                ];
                $r['score'] += 3;
            }

            // php_value/php_flag en .htaccess con mod_php desactivado causa 500
            if (preg_match('/^\s*php_(?:value|flag|admin_value|admin_flag)/mi', $htContent)) {
                // Verificar si estamos en CGI/FPM (donde php_value causa 500)
                if (strpos(php_sapi_name(), 'cgi') !== false || strpos(php_sapi_name(), 'fpm') !== false) {
                    $r['problemas'][] = [
                        'sev' => 'CRITICO', 'causa' => 'php_value en .htaccess con PHP-FPM/CGI',
                        'msg' => '.htaccess usa php_value/php_flag pero PHP corre como FPM/CGI — causa Error 500 → 520',
                        'solucion' => 'Mover configuraciones PHP a .user.ini en vez de .htaccess, o comentar las lineas php_value/php_flag'
                    ];
                    $r['score'] += 10;
                }
            }
        }
    }

    // ----- CHECK 3: error_log enorme -----
    $errorLogFiles = ['error_log', 'php_errorlog', 'error.log'];
    foreach ($errorLogFiles as $logFile) {
        $logPath = $subPath . '/' . $logFile;
        if (file_exists($logPath)) {
            $logSize = @filesize($logPath);
            $sizeMB = round($logSize / 1024 / 1024, 1);

            if ($logSize > 500 * 1024 * 1024) {
                $r['problemas'][] = [
                    'sev' => 'CRITICO', 'causa' => 'error_log gigante',
                    'msg' => "{$logFile}: {$sizeMB} MB — puede llenar disco y bloquear I/O",
                    'solucion' => "Vaciar: truncate -s 0 {$subPath}/{$logFile}"
                ];
                $r['score'] += 8;
            } elseif ($logSize > 50 * 1024 * 1024) {
                $r['problemas'][] = [
                    'sev' => 'ALTO', 'causa' => 'error_log grande',
                    'msg' => "{$logFile}: {$sizeMB} MB — revisar errores recurrentes",
                    'solucion' => "Revisar y vaciar: tail -100 {$subPath}/{$logFile}"
                ];
                $r['score'] += 4;
            }

            // Leer ultimas lineas para detectar errores fatales recientes
            if ($logSize > 0) {
                $r['info']['last_errors_' . $logFile] = leerUltimasLineas($logPath, 10);
            }
        }
    }

    // ----- CHECK 4: Permisos de directorios criticos -----
    $dirs = ['data', 'data/cache', 'data/tmp', 'data/meta', 'data/log', 'conf'];
    foreach ($dirs as $dir) {
        $dirPath = $subPath . '/' . $dir;
        if (is_dir($dirPath) && !is_writable($dirPath)) {
            $perms = substr(sprintf('%o', fileperms($dirPath)), -4);
            $r['problemas'][] = [
                'sev' => 'ALTO', 'causa' => 'Directorio no escribible',
                'msg' => "{$dir}/ no es escribible (permisos: {$perms}) — PHP no puede escribir cache/sesiones",
                'solucion' => "chmod 775 {$dirPath}"
            ];
            $r['score'] += 5;
        }
    }

    // ----- CHECK 5: conf/local.php y conf/local_act.php -----
    $localPhp = $subPath . '/conf/local.php';
    if (file_exists($localPhp)) {
        $localContent = @file_get_contents($localPhp);
        if ($localContent !== false) {
            // Verificar sintaxis
            if (function_exists('exec')) {
                $output = [];
                $retCode = 0;
                @exec('php -l ' . escapeshellarg($localPhp) . ' 2>&1', $output, $retCode);
                if ($retCode !== 0) {
                    $r['problemas'][] = [
                        'sev' => 'CRITICO', 'causa' => 'conf/local.php con error de sintaxis',
                        'msg' => 'conf/local.php: ' . implode(' ', $output),
                        'solucion' => 'Corregir error de sintaxis en conf/local.php'
                    ];
                    $r['score'] += 10;
                }
            }
        }
    }

    // ----- CHECK 6: SQLite locks -----
    $sqliteFiles = ['data/meta/do.sqlite3', 'data/meta/do.sqlite', 'data/meta/struct.sqlite3'];
    foreach ($sqliteFiles as $dbFile) {
        $dbPath = $subPath . '/' . $dbFile;
        if (file_exists($dbPath)) {
            // Verificar journal/wal huerfano (indica crash o lock)
            $walFile = $dbPath . '-wal';
            $journalFile = $dbPath . '-journal';

            if (file_exists($journalFile)) {
                $jAge = time() - filemtime($journalFile);
                if ($jAge > 3600) {
                    $r['problemas'][] = [
                        'sev' => 'ALTO', 'causa' => 'SQLite journal huerfano',
                        'msg' => basename($dbFile) . '-journal tiene ' . round($jAge / 3600, 1) . 'h — posible DB bloqueada',
                        'solucion' => 'Eliminar el journal: rm ' . $journalFile
                    ];
                    $r['score'] += 5;
                }
            }

            if (file_exists($walFile)) {
                $walSize = @filesize($walFile);
                if ($walSize > 50 * 1024 * 1024) {
                    $sizeMB = round($walSize / 1024 / 1024, 1);
                    $r['problemas'][] = [
                        'sev' => 'ALTO', 'causa' => 'SQLite WAL enorme',
                        'msg' => basename($dbFile) . '-wal: ' . $sizeMB . ' MB — DB necesita checkpoint',
                        'solucion' => 'Ejecutar PRAGMA wal_checkpoint(TRUNCATE) en la DB'
                    ];
                    $r['score'] += 4;
                }
            }

            // Verificar si la DB se puede abrir
            try {
                $pdo = new PDO('sqlite:' . $dbPath);
                $pdo->setAttribute(PDO::ATTR_TIMEOUT, 3);
                $pdo->query('SELECT 1');
            } catch (\Exception $e) {
                $r['problemas'][] = [
                    'sev' => 'CRITICO', 'causa' => 'SQLite inaccesible',
                    'msg' => basename($dbFile) . ': ' . $e->getMessage(),
                    'solucion' => 'Verificar permisos y eliminar locks: rm ' . $dbPath . '-journal ' . $dbPath . '-wal'
                ];
                $r['score'] += 8;
            }
        }
    }

    // ----- CHECK 7: web/index.html muy pesado -----
    $webIndex = $subPath . '/web/index.html';
    if (file_exists($webIndex)) {
        $webSize = @filesize($webIndex);
        if ($webSize > 2 * 1024 * 1024) {
            $sizeMB = round($webSize / 1024 / 1024, 1);
            $r['problemas'][] = [
                'sev' => 'MEDIO', 'causa' => 'web/index.html pesado',
                'msg' => "web/index.html: {$sizeMB} MB — puede causar timeout en primer render",
                'solucion' => 'Optimizar HTML, imagenes inline y scripts en web/index.html'
            ];
            $r['score'] += 2;
        }
    }

    // ----- CHECK 8: PHP sessions -----
    $sessDir = $subPath . '/data/tmp';
    if (is_dir($sessDir)) {
        $sessCount = 0;
        $dh = @opendir($sessDir);
        if ($dh) {
            while (($f = readdir($dh)) !== false) {
                if (strpos($f, 'sess_') === 0) $sessCount++;
                if ($sessCount > 500) break;
            }
            closedir($dh);
        }
        if ($sessCount > 500) {
            $r['problemas'][] = [
                'sev' => 'MEDIO', 'causa' => 'Exceso de archivos de sesion',
                'msg' => "data/tmp/ tiene 500+ archivos de sesion — I/O lento",
                'solucion' => 'Limpiar sesiones antiguas: find data/tmp -name "sess_*" -mtime +7 -delete'
            ];
            $r['score'] += 2;
        }
    }

    // ----- CHECK 9: DokuWiki data/log/ (errores propios del wiki) -----
    $dokuLogDir = $subPath . '/data/log';
    if (is_dir($dokuLogDir)) {
        // Buscar logs recientes (ultimas 48 horas)
        $logFiles = @glob($dokuLogDir . '/*.log');
        if ($logFiles) {
            foreach ($logFiles as $logFile) {
                $logAge = time() - filemtime($logFile);
                $logSize = @filesize($logFile);
                if ($logAge < 172800 && $logSize > 0) { // < 48h y no vacio
                    $logName = basename($logFile);
                    $lastLines = leerUltimasLineas($logFile, 15);
                    if (!empty($lastLines)) {
                        $r['info']['doku_log_' . $logName] = $lastLines;

                        // Buscar fatal errors en las ultimas lineas
                        $fatals = 0;
                        foreach ($lastLines as $line) {
                            if (preg_match('/fatal|critical|exception|out of memory/i', $line)) {
                                $fatals++;
                            }
                        }
                        if ($fatals > 0) {
                            $r['problemas'][] = [
                                'sev' => 'ALTO', 'causa' => 'Errores fatales en DokuWiki log',
                                'msg' => "data/log/{$logName}: {$fatals} linea(s) con errores fatales/criticos en ultimas 48h",
                                'solucion' => "Revisar: cat {$subPath}/data/log/{$logName}"
                            ];
                            $r['score'] += 5;
                        }
                    }
                }
            }
        }
    }

    // ----- CHECK 9b: .user.ini (config PHP-FPM que puede causar problemas) -----
    $userIni = $subPath . '/.user.ini';
    if (file_exists($userIni)) {
        $iniContent = @file_get_contents($userIni);
        if ($iniContent !== false) {
            $r['info']['user_ini'] = explode("\n", trim($iniContent));

            // Verificar memory_limit demasiado bajo en .user.ini
            if (preg_match('/^\s*memory_limit\s*=\s*(\S+)/mi', $iniContent, $m)) {
                $localMem = convertToBytes($m[1]);
                if ($localMem > 0 && $localMem < 64 * 1024 * 1024) {
                    $r['problemas'][] = [
                        'sev' => 'ALTO', 'causa' => '.user.ini memory_limit restrictivo',
                        'msg' => ".user.ini: memory_limit={$m[1]} — puede causar fatal error en paginas pesadas",
                        'solucion' => 'Aumentar memory_limit en .user.ini a 256M minimo'
                    ];
                    $r['score'] += 5;
                }
            }

            // max_execution_time demasiado bajo
            if (preg_match('/^\s*max_execution_time\s*=\s*(\d+)/mi', $iniContent, $m)) {
                if ((int)$m[1] < 30) {
                    $r['problemas'][] = [
                        'sev' => 'MEDIO', 'causa' => '.user.ini max_execution_time bajo',
                        'msg' => ".user.ini: max_execution_time={$m[1]}s — puede causar timeout en operaciones complejas",
                        'solucion' => 'Aumentar max_execution_time en .user.ini a 120'
                    ];
                    $r['score'] += 3;
                }
            }
        }
    }

    // ----- CHECK 9c: Errores recientes en error_log (buscar fatales ultimas 24h) -----
    foreach (['error_log', 'php_errorlog'] as $logName) {
        $logPath = $subPath . '/' . $logName;
        if (file_exists($logPath) && filesize($logPath) > 0) {
            $logAge = time() - filemtime($logPath);
            if ($logAge < 86400) { // modificado en ultimas 24h
                $lastLines = $r['info']['last_errors_' . $logName] ?? leerUltimasLineas($logPath, 20);
                $fatalCount = 0;
                $lastFatal = '';
                foreach ($lastLines as $line) {
                    if (preg_match('/PHP Fatal error|Allowed memory size|Maximum execution time|Call to undefined/i', $line)) {
                        $fatalCount++;
                        $lastFatal = $line;
                    }
                }
                if ($fatalCount > 0) {
                    $shortFatal = strlen($lastFatal) > 200 ? substr($lastFatal, 0, 200) . '...' : $lastFatal;
                    $r['problemas'][] = [
                        'sev' => 'CRITICO', 'causa' => 'PHP Fatal error reciente',
                        'msg' => "{$logName} (ultimas 24h): {$fatalCount} errores fatales. Ultimo: " . trim($shortFatal),
                        'solucion' => "Revisar el error completo: tail -50 {$subPath}/{$logName}"
                    ];
                    $r['score'] += 8;
                }
            }
        }
    }

    // ----- CHECK 10: Test HTTP real (solo si no es CLI y hay curl) -----
    // Se omite en escaneo masivo por rendimiento, disponible con ?sub=X&http=1

    // ----- CHECK 11: Plugins con errores de sintaxis -----
    $pluginsDir = $subPath . '/lib/plugins';
    if (is_dir($pluginsDir) && function_exists('exec')) {
        $criticalPlugins = ['bez', 'niccadminmenu', 'wasabisync', 'sqlite', 'struct'];
        foreach ($criticalPlugins as $plugin) {
            $pluginDir = $pluginsDir . '/' . $plugin;
            if (!is_dir($pluginDir)) continue;

            // Verificar archivos PHP principales del plugin
            $phpFiles = glob($pluginDir . '/*.php');
            if (!$phpFiles) continue;

            foreach ($phpFiles as $phpFile) {
                $output = [];
                $retCode = 0;
                @exec('php -l ' . escapeshellarg($phpFile) . ' 2>&1', $output, $retCode);
                if ($retCode !== 0) {
                    $relPath = 'lib/plugins/' . $plugin . '/' . basename($phpFile);
                    $r['problemas'][] = [
                        'sev' => 'CRITICO', 'causa' => 'Plugin con error de sintaxis',
                        'msg' => "{$relPath}: " . implode(' ', $output),
                        'solucion' => "Corregir error de sintaxis en {$relPath}"
                    ];
                    $r['score'] += 8;
                }
            }
        }
    }

    // Calcular estado final
    if ($r['score'] >= 10) {
        $r['estado'] = 'CRITICO';
    } elseif ($r['score'] >= 5) {
        $r['estado'] = 'ALERTA';
    } elseif ($r['score'] > 0) {
        $r['estado'] = 'MEDIO';
    }

    return $r;
}

/**
 * Hace un test HTTP real a un subdominio para medir tiempos y detectar errores
 */
function testHTTP($url, $timeout = 15)
{
    if (!function_exists('curl_init')) {
        return [
            'url' => $url, 'http_code' => 0, 'ttfb' => 0, 'total_time' => 0,
            'dns_time' => 0, 'connect_time' => 0, 'size' => 0, 'redirect_count' => 0,
            'error' => 'cURL no disponible', 'errno' => -1,
            'problemas' => [['sev' => 'ALTO', 'causa' => 'cURL no disponible', 'msg' => 'No se puede hacer test HTTP sin extension cURL', 'solucion' => 'Instalar extension php-curl']],
        ];
    }

    $ch = curl_init();
    curl_setopt_array($ch, [
        CURLOPT_URL            => $url,
        CURLOPT_RETURNTRANSFER => true,
        CURLOPT_FOLLOWLOCATION => true,
        CURLOPT_MAXREDIRS      => 5,
        CURLOPT_TIMEOUT        => $timeout,
        CURLOPT_CONNECTTIMEOUT => 10,
        CURLOPT_SSL_VERIFYPEER => false,
        CURLOPT_SSL_VERIFYHOST => 0,
        CURLOPT_HEADER         => true,
        CURLOPT_NOBODY         => false,
        CURLOPT_USERAGENT      => 'SIE-Diagnostico/1.0',
        // Bypass Cloudflare: ir directo al servidor
        CURLOPT_HTTPHEADER     => ['Cache-Control: no-cache'],
    ]);

    $response = curl_exec($ch);
    $info = curl_getinfo($ch);
    $error = curl_error($ch);
    $errno = curl_errno($ch);
    curl_close($ch);

    $result = [
        'url'         => $url,
        'http_code'   => $info['http_code'] ?? 0,
        'ttfb'        => round(($info['starttransfer_time'] ?? 0) * 1000),
        'total_time'  => round(($info['total_time'] ?? 0) * 1000),
        'dns_time'    => round(($info['namelookup_time'] ?? 0) * 1000),
        'connect_time' => round(($info['connect_time'] ?? 0) * 1000),
        'size'        => $info['size_download'] ?? 0,
        'redirect_count' => $info['redirect_count'] ?? 0,
        'error'       => $error,
        'errno'       => $errno,
        'problemas'   => [],
    ];

    if ($errno) {
        $result['problemas'][] = [
            'sev' => 'CRITICO', 'causa' => 'Error cURL',
            'msg' => "cURL error {$errno}: {$error}",
            'solucion' => 'Verificar que el dominio resuelve y el servidor responde'
        ];
    } elseif ($info['http_code'] === 0) {
        $result['problemas'][] = [
            'sev' => 'CRITICO', 'causa' => 'Sin respuesta HTTP',
            'msg' => 'El servidor no devolvio ninguna respuesta — causa principal de Error 520',
            'solucion' => 'Revisar error_log del subdominio y reiniciar PHP-FPM: systemctl restart lsphp*'
        ];
    } elseif ($info['http_code'] >= 500) {
        $result['problemas'][] = [
            'sev' => 'CRITICO', 'causa' => "HTTP {$info['http_code']}",
            'msg' => "Servidor devuelve HTTP {$info['http_code']} — Cloudflare lo convierte en 520",
            'solucion' => 'Revisar error_log para encontrar el error PHP que causa el ' . $info['http_code']
        ];
    }

    if ($result['ttfb'] > 10000) {
        $result['problemas'][] = [
            'sev' => 'ALTO', 'causa' => 'TTFB muy alto',
            'msg' => "Time to First Byte: {$result['ttfb']}ms (Cloudflare timeout: 100s)",
            'solucion' => 'Limpiar cache DokuWiki, verificar plugins lentos, revisar SQLite locks'
        ];
    } elseif ($result['ttfb'] > 5000) {
        $result['problemas'][] = [
            'sev' => 'MEDIO', 'causa' => 'TTFB alto',
            'msg' => "Time to First Byte: {$result['ttfb']}ms",
            'solucion' => 'Optimizar rendimiento del servidor'
        ];
    }

    if ($info['size_download'] === 0 && $info['http_code'] === 200) {
        $result['problemas'][] = [
            'sev' => 'CRITICO', 'causa' => 'Respuesta vacia con 200',
            'msg' => 'HTTP 200 pero 0 bytes en body — causa comun de Error 520',
            'solucion' => 'Verificar que index.php/doku.php generan output correctamente'
        ];
    }

    // Verificar headers grandes
    if ($response) {
        $headerEnd = strpos($response, "\r\n\r\n");
        if ($headerEnd !== false) {
            $headerSize = $headerEnd;
            if ($headerSize > 8000) {
                $result['problemas'][] = [
                    'sev' => 'ALTO', 'causa' => 'Headers HTTP muy grandes',
                    'msg' => "Headers: " . round($headerSize / 1024, 1) . " KB — Cloudflare rechaza >8KB",
                    'solucion' => 'Reducir cookies y headers personalizados'
                ];
            }
        }
    }

    return $result;
}

/**
 * Lee las ultimas N lineas de un archivo grande eficientemente
 */
function leerUltimasLineas($file, $numLineas = 10)
{
    $size = @filesize($file);
    if ($size === 0) return [];

    $readBytes = min($size, 8192);
    $fp = @fopen($file, 'r');
    if (!$fp) return [];

    fseek($fp, -$readBytes, SEEK_END);
    $data = fread($fp, $readBytes);
    fclose($fp);

    $lines = explode("\n", trim($data));
    return array_slice($lines, -$numLineas);
}

/**
 * Convierte tamanio PHP (128M, 2G) a bytes
 */
function convertToBytes($val)
{
    $val = trim($val);
    if ($val === '-1') return -1;
    $last = strtolower($val[strlen($val) - 1]);
    $num = (int) $val;
    switch ($last) {
        case 'g': $num *= 1024;
        case 'm': $num *= 1024;
        case 'k': $num *= 1024;
    }
    return $num;
}

// =====================================================================
//  EJECUCION
// =====================================================================

$tiempoInicio = microtime(true);
$resultados   = [];
$discoInfo    = checkDisco($basePath);
$phpInfo      = checkPHPConfig();
$doHTTP       = isset($_GET['http']) && $_GET['http'] === '1';

// Obtener lista de carpetas
if (!empty($targetSub)) {
    $carpetas = [$targetSub];
} else {
    $carpetas = [];
    $scan = @scandir($basePath);
    if (!$scan) {
        echo $esCLI ? "ERROR: No se puede leer {$basePath}\n" : "<p style='color:red'>ERROR: No se puede leer {$basePath}</p>";
        exit(1);
    }
    foreach ($scan as $c) {
        if (in_array($c, $excluir)) continue;
        if (!is_dir($basePath . '/' . $c)) continue;
        $carpetas[] = $c;
    }
    sort($carpetas);
}

// Escanear subdominios
foreach ($carpetas as $carpeta) {
    $subPath = $basePath . '/' . $carpeta;
    $r = analizarSubdominio($subPath, $carpeta);
    if (!$r['tiene_doku']) continue;
    $resultados[] = $r;
}

// Test HTTP individual (solo con ?sub=X&http=1)
$httpResult = null;
if ($doHTTP && !empty($targetSub)) {
    $testUrl = 'https://' . $targetSub . '.sistemavirtualdegestion.com';
    $httpResult = testHTTP($testUrl);

    // Tambien probar dominio directo
    $testUrlDirect = 'https://' . $targetSub . '.systemsie.site';
    $httpResultDirect = testHTTP($testUrlDirect);
}

$tiempoFin = round(microtime(true) - $tiempoInicio, 2);

// Estadisticas
$totalOK      = count(array_filter($resultados, fn($r) => $r['estado'] === 'OK'));
$totalCritico = count(array_filter($resultados, fn($r) => $r['estado'] === 'CRITICO'));
$totalAlerta  = count(array_filter($resultados, fn($r) => $r['estado'] === 'ALERTA'));
$totalMedio   = count(array_filter($resultados, fn($r) => $r['estado'] === 'MEDIO'));

// Ordenar por score (peores primero)
usort($resultados, fn($a, $b) => $b['score'] - $a['score']);

// =====================================================================
//  SALIDA CLI
// =====================================================================

if ($esCLI) {
    echo "================================================================\n";
    echo " DIAGNOSTICO ERROR 520 CLOUDFLARE - SUBDOMINIOS SIE\n";
    echo " " . date('Y-m-d H:i:s') . " | Tiempo: {$tiempoFin}s\n";
    echo "================================================================\n\n";

    echo "Disco: {$discoInfo['libre_mb']} MB libres ({$discoInfo['uso_pct']}% usado)\n";
    echo "PHP: memory_limit={$phpInfo['memory_limit']} | max_execution_time={$phpInfo['max_execution_time']}s\n\n";

    echo "Total: " . count($resultados) . " | OK: {$totalOK} | Critico: {$totalCritico} | Alerta: {$totalAlerta} | Medio: {$totalMedio}\n\n";

    $afectados = array_filter($resultados, fn($r) => $r['estado'] !== 'OK');
    foreach ($afectados as $r) {
        echo "=== {$r['carpeta']} [{$r['estado']}] score={$r['score']} ===\n";
        foreach ($r['problemas'] as $p) {
            echo "  [{$p['sev']}] {$p['causa']}: {$p['msg']}\n";
            echo "         -> {$p['solucion']}\n";
        }
        echo "\n";
    }
    exit(0);
}

// =====================================================================
//  SALIDA HTML
// =====================================================================
?>
<!DOCTYPE html>
<html lang="es">
<head>
<meta charset="UTF-8">
<title>Diagnostico Error 520 - SIE</title>
<style>
    * { box-sizing: border-box; margin: 0; padding: 0; }
    body { font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; background: #f1f5f9; color: #1e293b; padding: 20px; }
    .container { max-width: 1400px; margin: 0 auto; }
    h1 { font-size: 1.5em; color: #0f172a; margin-bottom: 4px; }
    .subtitle { color: #64748b; font-size: 0.88em; margin-bottom: 20px; }

    .summary { display: grid; grid-template-columns: repeat(auto-fit, minmax(150px, 1fr)); gap: 12px; margin-bottom: 20px; }
    .card { background: #fff; border-radius: 10px; padding: 14px; text-align: center; box-shadow: 0 1px 3px rgba(0,0,0,0.08); }
    .card .num { font-size: 1.8em; font-weight: 700; }
    .card .label { font-size: 0.8em; color: #64748b; }
    .green { color: #16a34a; } .red { color: #dc2626; } .yellow { color: #d97706; } .blue { color: #2563eb; } .orange { color: #ea580c; }

    .section { background: #fff; border-radius: 10px; padding: 20px; margin-bottom: 16px; box-shadow: 0 1px 3px rgba(0,0,0,0.08); }
    .section h2 { font-size: 1.1em; margin-bottom: 12px; }

    .explain { background: #fef2f2; border: 1px solid #fecaca; border-radius: 10px; padding: 16px; margin-bottom: 20px; }
    .explain h2 { color: #991b1b; margin-bottom: 8px; font-size: 1em; }
    .explain p { font-size: 0.86em; color: #334155; margin-bottom: 4px; }
    .explain code { background: #fee2e2; color: #991b1b; padding: 1px 4px; border-radius: 3px; font-size: 0.9em; }

    .info-box { background: #eff6ff; border: 1px solid #bfdbfe; border-radius: 10px; padding: 16px; margin-bottom: 20px; }
    .info-box h3 { font-size: 0.95em; color: #1e40af; margin-bottom: 8px; }
    .info-box .grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); gap: 6px; font-size: 0.85em; }
    .info-box .item { display: flex; justify-content: space-between; }
    .info-box .item strong { color: #1e40af; }

    .actions { display: flex; gap: 12px; margin-bottom: 20px; flex-wrap: wrap; align-items: center; }
    .btn { padding: 10px 20px; border: none; border-radius: 8px; font-size: 0.9em; cursor: pointer; text-decoration: none; display: inline-block; font-weight: 600; }
    .btn-blue { background: #2563eb; color: #fff; } .btn-blue:hover { background: #1d4ed8; }
    .btn-green { background: #16a34a; color: #fff; } .btn-gray { background: #64748b; color: #fff; }

    .problema-card { border-radius: 10px; padding: 16px; margin-bottom: 14px; }
    .pc-critico { background: #fef2f2; border: 1px solid #fecaca; }
    .pc-alerta { background: #fef3c7; border: 1px solid #fde68a; }
    .pc-medio { background: #fff7ed; border: 1px solid #fed7aa; }
    .pc-header { display: flex; justify-content: space-between; align-items: center; margin-bottom: 10px; }
    .pc-name { font-weight: 700; font-size: 1em; }
    .pc-score { font-size: 0.78em; color: #64748b; }

    .prob-item { padding: 6px 10px; margin: 4px 0; border-radius: 6px; font-size: 0.85em; }
    .prob-critico { background: #fee2e2; border-left: 3px solid #dc2626; }
    .prob-alto { background: #fef3c7; border-left: 3px solid #f59e0b; }
    .prob-medio { background: #fff7ed; border-left: 3px solid #fb923c; }
    .prob-causa { font-weight: 600; }
    .prob-solucion { color: #16a34a; font-size: 0.9em; margin-top: 2px; }

    .http-box { background: #f0fdf4; border: 1px solid #86efac; border-radius: 10px; padding: 16px; margin-bottom: 20px; }
    .http-box.http-error { background: #fef2f2; border-color: #fecaca; }
    .http-box h3 { font-size: 0.95em; margin-bottom: 8px; }
    .http-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(140px, 1fr)); gap: 8px; }
    .http-metric { text-align: center; padding: 8px; background: rgba(255,255,255,0.7); border-radius: 6px; }
    .http-metric .val { font-size: 1.3em; font-weight: 700; }
    .http-metric .lbl { font-size: 0.75em; color: #64748b; }

    .error-log { background: #1e1e1e; color: #d4d4d4; padding: 12px; border-radius: 8px; font-family: monospace; font-size: 0.78em; white-space: pre-wrap; max-height: 300px; overflow-y: auto; margin-top: 8px; }

    table { width: 100%; border-collapse: collapse; font-size: 0.83em; }
    th { text-align: left; padding: 8px 6px; border-bottom: 2px solid #e2e8f0; color: #475569; font-size: 0.85em; }
    td { padding: 7px 6px; border-bottom: 1px solid #f1f5f9; }
    tr:hover { background: #f8fafc; }
    tr.row-err { background: #fef2f2; }
    tr.row-warn { background: #fffbeb; }

    .badge { display: inline-block; padding: 2px 7px; border-radius: 4px; font-size: 0.76em; font-weight: 600; }
    .badge-ok { background: #dcfce7; color: #166534; }
    .badge-critico { background: #fee2e2; color: #991b1b; }
    .badge-alerta { background: #fef3c7; color: #92400e; }
    .badge-medio { background: #fff7ed; color: #9a3412; }

    .filter-bar { margin-bottom: 12px; display: flex; gap: 8px; align-items: center; flex-wrap: wrap; }
    .filter-bar input, .filter-bar select { padding: 6px 10px; border: 1px solid #cbd5e1; border-radius: 6px; font-size: 0.86em; }
    .filter-bar input { width: 200px; }
</style>
</head>
<body>
<div class="container">

<h1>&#9889; Diagnostico Error 520 Cloudflare</h1>
<p class="subtitle">
    <?= date('Y-m-d H:i:s') ?> |
    Tiempo: <?= $tiempoFin ?>s |
    Sistemas: <?= count($resultados) ?>
    <?php if (!empty($targetSub)): ?> | Subdominio: <strong><?= htmlspecialchars($targetSub) ?></strong><?php endif; ?>
</p>

<div class="explain">
    <h2>&#9888;&#65039; Que es el Error 520</h2>
    <p><strong>Error 520</strong> significa que Cloudflare contacto al servidor origen pero recibio una respuesta <em>vacia, malformada o inesperada</em>.</p>
    <p><strong>Causas mas comunes:</strong> PHP fatal error (respuesta vacia), <code>.htaccess</code> corrupto, disco lleno, error_log gigante, SQLite bloqueado, timeout PHP.</p>
    <p><strong>Este script:</strong> Escanea cada subdominio buscando las 10 causas mas frecuentes de Error 520 sin modificar ningun archivo.</p>
</div>

<!-- Info servidor -->
<div class="info-box">
    <h3>&#128187; Informacion del servidor</h3>
    <div class="grid">
        <div class="item"><span>Disco libre:</span> <strong class="<?= $discoInfo['libre_mb'] < 500 ? 'red' : 'green' ?>"><?= number_format($discoInfo['libre_mb']) ?> MB</strong></div>
        <div class="item"><span>Disco usado:</span> <strong><?= $discoInfo['uso_pct'] ?>%</strong></div>
        <div class="item"><span>memory_limit:</span> <strong><?= $phpInfo['memory_limit'] ?></strong></div>
        <div class="item"><span>max_execution_time:</span> <strong><?= $phpInfo['max_execution_time'] ?>s</strong></div>
        <div class="item"><span>post_max_size:</span> <strong><?= $phpInfo['post_max_size'] ?></strong></div>
        <div class="item"><span>upload_max_filesize:</span> <strong><?= $phpInfo['upload_max_filesize'] ?></strong></div>
        <div class="item"><span>PHP SAPI:</span> <strong><?= php_sapi_name() ?></strong></div>
        <div class="item"><span>PHP version:</span> <strong><?= PHP_VERSION ?></strong></div>
    </div>
    <?php if (!empty($phpInfo['problemas'])): ?>
    <div style="margin-top:10px;">
        <?php foreach ($phpInfo['problemas'] as $p): ?>
        <div class="prob-item prob-medio"><span class="prob-causa">[<?= $p['sev'] ?>] <?= $p['causa'] ?>:</span> <?= htmlspecialchars($p['msg']) ?><div class="prob-solucion">&#8594; <?= htmlspecialchars($p['solucion']) ?></div></div>
        <?php endforeach; ?>
    </div>
    <?php endif; ?>
</div>

<!-- Resumen -->
<div class="summary">
    <div class="card"><div class="num blue"><?= count($resultados) ?></div><div class="label">Total DokuWiki</div></div>
    <div class="card"><div class="num green"><?= $totalOK ?></div><div class="label">Sin problemas</div></div>
    <div class="card"><div class="num <?= $totalCritico > 0 ? 'red' : 'green' ?>"><?= $totalCritico ?></div><div class="label">Critico</div></div>
    <div class="card"><div class="num <?= $totalAlerta > 0 ? 'yellow' : 'green' ?>"><?= $totalAlerta ?></div><div class="label">Alerta</div></div>
    <div class="card"><div class="num <?= $totalMedio > 0 ? 'orange' : 'green' ?>"><?= $totalMedio ?></div><div class="label">Medio</div></div>
    <div class="card"><div class="num" style="color:#64748b;"><?= number_format($discoInfo['libre_mb']) ?></div><div class="label">MB libres</div></div>
</div>

<!-- Botones -->
<div class="actions">
    <a href="?" class="btn btn-blue">&#128269; Escanear todos</a>
    <?php if (!empty($targetSub)): ?>
        <a href="?sub=<?= urlencode($targetSub) ?>&http=1" class="btn btn-green">&#127760; Test HTTP: <?= htmlspecialchars($targetSub) ?></a>
        <a href="?" class="btn btn-gray">Ver todos</a>
    <?php endif; ?>
</div>

<?php if ($httpResult): ?>
<!-- Resultado test HTTP via Cloudflare -->
<div class="http-box <?= !empty($httpResult['problemas']) ? 'http-error' : '' ?>">
    <h3>&#127760; Test HTTP via Cloudflare: <?= htmlspecialchars($httpResult['url']) ?></h3>
    <div class="http-grid">
        <div class="http-metric"><div class="val <?= $httpResult['http_code'] >= 400 ? 'red' : 'green' ?>"><?= $httpResult['http_code'] ?: 'N/A' ?></div><div class="lbl">HTTP Code</div></div>
        <div class="http-metric"><div class="val <?= $httpResult['ttfb'] > 5000 ? 'red' : ($httpResult['ttfb'] > 2000 ? 'yellow' : 'green') ?>"><?= $httpResult['ttfb'] ?> ms</div><div class="lbl">TTFB</div></div>
        <div class="http-metric"><div class="val"><?= $httpResult['total_time'] ?> ms</div><div class="lbl">Total</div></div>
        <div class="http-metric"><div class="val"><?= $httpResult['dns_time'] ?> ms</div><div class="lbl">DNS</div></div>
        <div class="http-metric"><div class="val"><?= round($httpResult['size'] / 1024, 1) ?> KB</div><div class="lbl">Tamanio</div></div>
        <div class="http-metric"><div class="val"><?= $httpResult['redirect_count'] ?></div><div class="lbl">Redirects</div></div>
    </div>
    <?php foreach ($httpResult['problemas'] as $p): ?>
    <div class="prob-item prob-<?= strtolower($p['sev']) ?>" style="margin-top:8px;"><span class="prob-causa">[<?= $p['sev'] ?>] <?= htmlspecialchars($p['causa']) ?>:</span> <?= htmlspecialchars($p['msg']) ?><div class="prob-solucion">&#8594; <?= htmlspecialchars($p['solucion']) ?></div></div>
    <?php endforeach; ?>
</div>

<?php if (isset($httpResultDirect)): ?>
<!-- Resultado test HTTP directo (sin Cloudflare) -->
<div class="http-box <?= !empty($httpResultDirect['problemas']) ? 'http-error' : '' ?>">
    <h3>&#9889; Test HTTP Directo (sin Cloudflare): <?= htmlspecialchars($httpResultDirect['url']) ?></h3>
    <div class="http-grid">
        <div class="http-metric"><div class="val <?= $httpResultDirect['http_code'] >= 400 ? 'red' : 'green' ?>"><?= $httpResultDirect['http_code'] ?: 'N/A' ?></div><div class="lbl">HTTP Code</div></div>
        <div class="http-metric"><div class="val <?= $httpResultDirect['ttfb'] > 5000 ? 'red' : ($httpResultDirect['ttfb'] > 2000 ? 'yellow' : 'green') ?>"><?= $httpResultDirect['ttfb'] ?> ms</div><div class="lbl">TTFB</div></div>
        <div class="http-metric"><div class="val"><?= $httpResultDirect['total_time'] ?> ms</div><div class="lbl">Total</div></div>
        <div class="http-metric"><div class="val"><?= round($httpResultDirect['size'] / 1024, 1) ?> KB</div><div class="lbl">Tamanio</div></div>
    </div>
    <?php foreach ($httpResultDirect['problemas'] as $p): ?>
    <div class="prob-item prob-<?= strtolower($p['sev']) ?>" style="margin-top:8px;"><span class="prob-causa">[<?= $p['sev'] ?>] <?= htmlspecialchars($p['causa']) ?>:</span> <?= htmlspecialchars($p['msg']) ?><div class="prob-solucion">&#8594; <?= htmlspecialchars($p['solucion']) ?></div></div>
    <?php endforeach; ?>

    <?php if (empty($httpResultDirect['problemas']) && !empty($httpResult['problemas'])): ?>
    <div class="prob-item" style="background:#dcfce7;border-left:3px solid #16a34a;margin-top:8px;">
        <span class="prob-causa" style="color:#166534;">CONCLUSION:</span> El servidor responde bien por conexion directa pero falla via Cloudflare.<br>
        <span style="color:#166534;">&#8594; El problema es de Cloudflare, NO del servidor. Verificar: reglas de firewall Cloudflare, modo SSL (debe ser Full/Strict), y que el certificado del origen sea valido.</span>
    </div>
    <?php endif; ?>
</div>
<?php endif; ?>
<?php endif; ?>

<?php
// Mostrar sistemas con problemas (cards detalladas)
$afectados = array_filter($resultados, fn($r) => $r['estado'] !== 'OK');
if (!empty($afectados)):
?>
<div class="section">
    <h2>&#9888;&#65039; Sistemas con problemas (<?= count($afectados) ?>)</h2>
    <?php foreach ($afectados as $r): ?>
    <div class="problema-card pc-<?= strtolower($r['estado']) ?>">
        <div class="pc-header">
            <span class="pc-name">
                <a href="?sub=<?= urlencode($r['carpeta']) ?>" style="color:inherit;text-decoration:none;"><?= htmlspecialchars($r['carpeta']) ?></a>
                <span class="badge badge-<?= strtolower($r['estado']) ?>"><?= $r['estado'] ?></span>
            </span>
            <span class="pc-score">Score: <?= $r['score'] ?> | <a href="?sub=<?= urlencode($r['carpeta']) ?>&http=1" style="font-size:0.9em;">Test HTTP &#8594;</a></span>
        </div>
        <?php foreach ($r['problemas'] as $p): ?>
        <div class="prob-item prob-<?= strtolower($p['sev']) ?>">
            <span class="prob-causa">[<?= $p['sev'] ?>] <?= htmlspecialchars($p['causa']) ?>:</span> <?= htmlspecialchars($p['msg']) ?>
            <div class="prob-solucion">&#8594; <?= htmlspecialchars($p['solucion']) ?></div>
        </div>
        <?php endforeach; ?>

        <?php
        // Mostrar logs detallados
        foreach ($r['info'] as $key => $lines) {
            if (empty($lines)) continue;
            // Determinar titulo segun tipo de log
            if (strpos($key, 'last_errors_') === 0) {
                $logName = 'error_log: ' . str_replace('last_errors_', '', $key);
            } elseif (strpos($key, 'doku_log_') === 0) {
                $logName = 'DokuWiki log: ' . str_replace('doku_log_', '', $key);
            } elseif ($key === 'user_ini') {
                $logName = '.user.ini (config PHP-FPM)';
            } else {
                $logName = $key;
            }
            echo '<details style="margin-top:6px;"><summary style="cursor:pointer;font-size:0.82em;color:#64748b;">' . htmlspecialchars($logName) . ' (' . count($lines) . ' lineas)</summary>';
            echo '<div class="error-log">';
            foreach ($lines as $line) {
                // Resaltar lineas con errores fatales
                $hl = preg_match('/fatal|critical|exception|error/i', $line);
                if ($hl) echo '<span style="color:#ff6b6b;">';
                echo htmlspecialchars($line) . "\n";
                if ($hl) echo '</span>';
            }
            echo '</div></details>';
        }
        ?>
    </div>
    <?php endforeach; ?>
</div>
<?php endif; ?>

<!-- Tabla completa -->
<div class="section">
    <h2>&#128203; Todos los sistemas (<?= count($resultados) ?>)</h2>
    <div class="filter-bar">
        <input type="text" id="filtro" placeholder="Buscar..." oninput="filtrar()">
        <select id="filtroEstado" onchange="filtrar()">
            <option value="">Todos</option>
            <option value="err">Con problemas</option>
            <option value="OK">Solo OK</option>
        </select>
    </div>
    <table id="tabla">
        <thead>
            <tr>
                <th>#</th>
                <th>Subdominio</th>
                <th>Score</th>
                <th>Problemas</th>
                <th>Estado</th>
                <th>Acciones</th>
            </tr>
        </thead>
        <tbody>
        <?php $i = 0; foreach ($resultados as $r): $i++; ?>
            <?php $rowClass = $r['estado'] === 'CRITICO' ? 'row-err' : ($r['estado'] !== 'OK' ? 'row-warn' : ''); ?>
            <tr class="<?= $rowClass ?>" data-name="<?= htmlspecialchars(strtolower($r['carpeta'])) ?>" data-estado="<?= $r['estado'] === 'OK' ? 'OK' : 'err' ?>">
                <td><?= $i ?></td>
                <td><strong><?= htmlspecialchars($r['carpeta']) ?></strong></td>
                <td><?= $r['score'] ?></td>
                <td><?= count($r['problemas']) ?></td>
                <td>
                    <span class="badge badge-<?= strtolower($r['estado']) ?>"><?= $r['estado'] ?></span>
                </td>
                <td>
                    <a href="?sub=<?= urlencode($r['carpeta']) ?>" style="font-size:0.82em;">Detalle</a>
                    &nbsp;|&nbsp;
                    <a href="?sub=<?= urlencode($r['carpeta']) ?>&http=1" style="font-size:0.82em;">Test HTTP</a>
                </td>
            </tr>
        <?php endforeach; ?>
        </tbody>
    </table>
</div>

</div><!-- /container -->

<script>
function filtrar() {
    var texto = document.getElementById('filtro').value.toLowerCase();
    var estado = document.getElementById('filtroEstado').value;
    var rows = document.querySelectorAll('#tabla tbody tr');
    rows.forEach(function(row) {
        var name = row.dataset.name || '';
        var est = row.dataset.estado || '';
        var showText = !texto || name.indexOf(texto) !== -1;
        var showEstado = !estado || est === estado;
        row.style.display = (showText && showEstado) ? '' : 'none';
    });
}
</script>
</body>
</html>
