<?php
/**
 * Monitor de Sistema
 * Sistema de Inventario Forestal de Guatemala
 * 
 * Este script monitorea el estado del sistema y envía alertas
 * cuando se detectan problemas críticos
 */

require_once __DIR__ . '/../api/config/environment.php';

class SystemMonitor {
    private $logFile;
    private $alertEmail;
    private $thresholds;
    
    public function __construct() {
        $this->logFile = __DIR__ . '/../logs/system_monitor.log';
        $this->alertEmail = Environment::get('MONITORING_EMAIL', 'admin@localhost');
        
        $this->thresholds = [
            'disk_usage' => Environment::get('ALERT_DISK_USAGE_THRESHOLD', 85),
            'memory_usage' => Environment::get('ALERT_MEMORY_USAGE_THRESHOLD', 90),
            'error_rate' => Environment::get('ALERT_ERROR_RATE_THRESHOLD', 10),
            'response_time' => 5000, // 5 segundos
            'database_connections' => 100
        ];
        
        // Crear directorio de logs si no existe
        $logDir = dirname($this->logFile);
        if (!is_dir($logDir)) {
            mkdir($logDir, 0755, true);
        }
    }
    
    /**
     * Ejecutar monitoreo completo
     */
    public function runFullMonitoring() {
        $results = [
            'timestamp' => date('Y-m-d H:i:s'),
            'status' => 'OK',
            'checks' => []
        ];
        
        // Ejecutar todas las verificaciones
        $checks = [
            'disk_space' => $this->checkDiskSpace(),
            'memory_usage' => $this->checkMemoryUsage(),
            'database' => $this->checkDatabase(),
            'api_health' => $this->checkApiHealth(),
            'file_permissions' => $this->checkFilePermissions(),
            'log_files' => $this->checkLogFiles(),
            'backup_status' => $this->checkBackupStatus(),
            'security' => $this->checkSecurity()
        ];
        
        $results['checks'] = $checks;
        
        // Determinar estado general
        $criticalIssues = 0;
        $warnings = 0;
        
        foreach ($checks as $check) {
            if ($check['status'] === 'CRITICAL') {
                $criticalIssues++;
            } elseif ($check['status'] === 'WARNING') {
                $warnings++;
            }
        }
        
        if ($criticalIssues > 0) {
            $results['status'] = 'CRITICAL';
        } elseif ($warnings > 0) {
            $results['status'] = 'WARNING';
        }
        
        // Registrar resultados
        $this->logResults($results);
        
        // Enviar alertas si es necesario
        if ($results['status'] !== 'OK') {
            $this->sendAlert($results);
        }
        
        return $results;
    }
    
    /**
     * Verificar espacio en disco
     */
    private function checkDiskSpace() {
        $result = [
            'name' => 'Espacio en Disco',
            'status' => 'OK',
            'message' => '',
            'details' => []
        ];
        
        try {
            $totalBytes = disk_total_space('.');
            $freeBytes = disk_free_space('.');
            $usedBytes = $totalBytes - $freeBytes;
            $usagePercent = ($usedBytes / $totalBytes) * 100;
            
            $result['details'] = [
                'total' => $this->formatBytes($totalBytes),
                'used' => $this->formatBytes($usedBytes),
                'free' => $this->formatBytes($freeBytes),
                'usage_percent' => round($usagePercent, 2)
            ];
            
            if ($usagePercent >= $this->thresholds['disk_usage']) {
                $result['status'] = 'CRITICAL';
                $result['message'] = "Uso de disco crítico: {$usagePercent}%";
            } elseif ($usagePercent >= ($this->thresholds['disk_usage'] - 10)) {
                $result['status'] = 'WARNING';
                $result['message'] = "Uso de disco alto: {$usagePercent}%";
            } else {
                $result['message'] = "Uso de disco normal: {$usagePercent}%";
            }
            
        } catch (Exception $e) {
            $result['status'] = 'ERROR';
            $result['message'] = 'Error verificando espacio en disco: ' . $e->getMessage();
        }
        
        return $result;
    }
    
    /**
     * Verificar uso de memoria
     */
    private function checkMemoryUsage() {
        $result = [
            'name' => 'Uso de Memoria',
            'status' => 'OK',
            'message' => '',
            'details' => []
        ];
        
        try {
            $memoryLimit = ini_get('memory_limit');
            $memoryUsage = memory_get_usage(true);
            $memoryPeak = memory_get_peak_usage(true);
            
            // Convertir memory_limit a bytes
            $limitBytes = $this->parseMemoryLimit($memoryLimit);
            $usagePercent = ($memoryUsage / $limitBytes) * 100;
            
            $result['details'] = [
                'limit' => $memoryLimit,
                'current' => $this->formatBytes($memoryUsage),
                'peak' => $this->formatBytes($memoryPeak),
                'usage_percent' => round($usagePercent, 2)
            ];
            
            if ($usagePercent >= $this->thresholds['memory_usage']) {
                $result['status'] = 'CRITICAL';
                $result['message'] = "Uso de memoria crítico: {$usagePercent}%";
            } elseif ($usagePercent >= ($this->thresholds['memory_usage'] - 10)) {
                $result['status'] = 'WARNING';
                $result['message'] = "Uso de memoria alto: {$usagePercent}%";
            } else {
                $result['message'] = "Uso de memoria normal: {$usagePercent}%";
            }
            
        } catch (Exception $e) {
            $result['status'] = 'ERROR';
            $result['message'] = 'Error verificando memoria: ' . $e->getMessage();
        }
        
        return $result;
    }
    
    /**
     * Verificar estado de la base de datos
     */
    private function checkDatabase() {
        $result = [
            'name' => 'Base de Datos',
            'status' => 'OK',
            'message' => '',
            'details' => []
        ];
        
        try {
            $pdo = new PDO(
                "mysql:host=" . Environment::get('DB_HOST') . ";dbname=" . Environment::get('DB_NAME'),
                Environment::get('DB_USER'),
                Environment::get('DB_PASS'),
                [PDO::ATTR_TIMEOUT => 5]
            );
            
            // Verificar conexión
            $stmt = $pdo->query('SELECT 1');
            
            // Obtener estadísticas
            $stats = $pdo->query('SHOW STATUS')->fetchAll(PDO::FETCH_KEY_PAIR);
            
            $result['details'] = [
                'connections' => $stats['Threads_connected'] ?? 0,
                'queries' => $stats['Queries'] ?? 0,
                'uptime' => $stats['Uptime'] ?? 0,
                'slow_queries' => $stats['Slow_queries'] ?? 0
            ];
            
            // Verificar número de conexiones
            $connections = (int)($stats['Threads_connected'] ?? 0);
            if ($connections >= $this->thresholds['database_connections']) {
                $result['status'] = 'WARNING';
                $result['message'] = "Muchas conexiones activas: {$connections}";
            } else {
                $result['message'] = "Base de datos funcionando correctamente";
            }
            
        } catch (Exception $e) {
            $result['status'] = 'CRITICAL';
            $result['message'] = 'Error conectando a base de datos: ' . $e->getMessage();
        }
        
        return $result;
    }
    
    /**
     * Verificar salud de la API
     */
    private function checkApiHealth() {
        $result = [
            'name' => 'API Health',
            'status' => 'OK',
            'message' => '',
            'details' => []
        ];
        
        try {
            $baseUrl = Environment::get('APP_URL', 'http://localhost');
            $endpoints = [
                '/api/auth/health',
                '/api/projects',
                '/api/species'
            ];
            
            $responses = [];
            $totalTime = 0;
            
            foreach ($endpoints as $endpoint) {
                $startTime = microtime(true);
                
                $context = stream_context_create([
                    'http' => [
                        'timeout' => 10,
                        'method' => 'GET',
                        'header' => 'Content-Type: application/json'
                    ]
                ]);
                
                $response = @file_get_contents($baseUrl . $endpoint, false, $context);
                $responseTime = (microtime(true) - $startTime) * 1000;
                $totalTime += $responseTime;
                
                $responses[$endpoint] = [
                    'response_time' => round($responseTime, 2),
                    'status' => $response !== false ? 'OK' : 'ERROR'
                ];
            }
            
            $avgResponseTime = $totalTime / count($endpoints);
            $result['details'] = [
                'endpoints' => $responses,
                'avg_response_time' => round($avgResponseTime, 2)
            ];
            
            // Verificar tiempo de respuesta
            if ($avgResponseTime >= $this->thresholds['response_time']) {
                $result['status'] = 'WARNING';
                $result['message'] = "Tiempo de respuesta alto: {$avgResponseTime}ms";
            } else {
                $result['message'] = "API respondiendo correctamente";
            }
            
            // Verificar si algún endpoint falló
            foreach ($responses as $endpoint => $data) {
                if ($data['status'] === 'ERROR') {
                    $result['status'] = 'CRITICAL';
                    $result['message'] = "Endpoint no responde: {$endpoint}";
                    break;
                }
            }
            
        } catch (Exception $e) {
            $result['status'] = 'ERROR';
            $result['message'] = 'Error verificando API: ' . $e->getMessage();
        }
        
        return $result;
    }
    
    /**
     * Verificar permisos de archivos
     */
    private function checkFilePermissions() {
        $result = [
            'name' => 'Permisos de Archivos',
            'status' => 'OK',
            'message' => '',
            'details' => []
        ];
        
        $criticalDirs = [
            'uploads' => 0755,
            'logs' => 0755,
            'cache' => 0755,
            'backups' => 0755
        ];
        
        $issues = [];
        
        foreach ($criticalDirs as $dir => $expectedPerm) {
            if (is_dir($dir)) {
                $currentPerm = fileperms($dir) & 0777;
                if ($currentPerm !== $expectedPerm) {
                    $issues[] = "{$dir}: " . decoct($currentPerm) . " (esperado: " . decoct($expectedPerm) . ")";
                }
            } else {
                $issues[] = "{$dir}: directorio no existe";
            }
        }
        
        $result['details'] = ['issues' => $issues];
        
        if (!empty($issues)) {
            $result['status'] = 'WARNING';
            $result['message'] = 'Problemas de permisos detectados: ' . count($issues);
        } else {
            $result['message'] = 'Permisos de archivos correctos';
        }
        
        return $result;
    }
    
    /**
     * Verificar archivos de log
     */
    private function checkLogFiles() {
        $result = [
            'name' => 'Archivos de Log',
            'status' => 'OK',
            'message' => '',
            'details' => []
        ];
        
        $logDir = __DIR__ . '/../logs/';
        $maxLogSize = 10 * 1024 * 1024; // 10MB
        
        if (!is_dir($logDir)) {
            $result['status'] = 'WARNING';
            $result['message'] = 'Directorio de logs no existe';
            return $result;
        }
        
        $logFiles = glob($logDir . '*.log');
        $issues = [];
        $totalSize = 0;
        
        foreach ($logFiles as $logFile) {
            $size = filesize($logFile);
            $totalSize += $size;
            
            if ($size > $maxLogSize) {
                $issues[] = basename($logFile) . ': ' . $this->formatBytes($size);
            }
        }
        
        $result['details'] = [
            'total_files' => count($logFiles),
            'total_size' => $this->formatBytes($totalSize),
            'large_files' => $issues
        ];
        
        if (!empty($issues)) {
            $result['status'] = 'WARNING';
            $result['message'] = 'Archivos de log grandes detectados: ' . count($issues);
        } else {
            $result['message'] = 'Archivos de log en tamaño normal';
        }
        
        return $result;
    }
    
    /**
     * Verificar estado de backups
     */
    private function checkBackupStatus() {
        $result = [
            'name' => 'Estado de Backups',
            'status' => 'OK',
            'message' => '',
            'details' => []
        ];
        
        $backupDir = __DIR__ . '/../backups/database/';
        
        if (!is_dir($backupDir)) {
            $result['status'] = 'WARNING';
            $result['message'] = 'Directorio de backups no existe';
            return $result;
        }
        
        $backupFiles = glob($backupDir . 'sif_gt_backup_*.sql*');
        
        if (empty($backupFiles)) {
            $result['status'] = 'CRITICAL';
            $result['message'] = 'No se encontraron backups';
            return $result;
        }
        
        // Obtener backup más reciente
        usort($backupFiles, function($a, $b) {
            return filemtime($b) - filemtime($a);
        });
        
        $latestBackup = $backupFiles[0];
        $backupAge = time() - filemtime($latestBackup);
        $maxAge = 24 * 60 * 60; // 24 horas
        
        $result['details'] = [
            'total_backups' => count($backupFiles),
            'latest_backup' => basename($latestBackup),
            'backup_age_hours' => round($backupAge / 3600, 1)
        ];
        
        if ($backupAge > $maxAge) {
            $result['status'] = 'WARNING';
            $result['message'] = 'Backup más reciente tiene más de 24 horas';
        } else {
            $result['message'] = 'Backups actualizados correctamente';
        }
        
        return $result;
    }
    
    /**
     * Verificar aspectos de seguridad
     */
    private function checkSecurity() {
        $result = [
            'name' => 'Seguridad',
            'status' => 'OK',
            'message' => '',
            'details' => []
        ];
        
        $issues = [];
        
        // Verificar archivos sensibles
        $sensitiveFiles = [
            '.env',
            '.env.production',
            'api/config/config.php',
            'install.php'
        ];
        
        foreach ($sensitiveFiles as $file) {
            if (file_exists($file)) {
                // Verificar si es accesible vía web
                $url = Environment::get('APP_URL') . '/' . $file;
                $context = stream_context_create([
                    'http' => [
                        'timeout' => 5,
                        'method' => 'HEAD'
                    ]
                ]);
                
                $headers = @get_headers($url, 1, $context);
                if ($headers && strpos($headers[0], '200') !== false) {
                    $issues[] = "Archivo sensible accesible: {$file}";
                }
            }
        }
        
        // Verificar configuración PHP
        if (ini_get('display_errors')) {
            $issues[] = 'display_errors está habilitado';
        }
        
        if (ini_get('expose_php')) {
            $issues[] = 'expose_php está habilitado';
        }
        
        $result['details'] = ['issues' => $issues];
        
        if (!empty($issues)) {
            $result['status'] = 'WARNING';
            $result['message'] = 'Problemas de seguridad detectados: ' . count($issues);
        } else {
            $result['message'] = 'Configuración de seguridad correcta';
        }
        
        return $result;
    }
    
    /**
     * Registrar resultados del monitoreo
     */
    private function logResults($results) {
        $logEntry = [
            'timestamp' => $results['timestamp'],
            'status' => $results['status'],
            'summary' => []
        ];
        
        foreach ($results['checks'] as $checkName => $check) {
            $logEntry['summary'][$checkName] = [
                'status' => $check['status'],
                'message' => $check['message']
            ];
        }
        
        $logLine = json_encode($logEntry) . "\n";
        file_put_contents($this->logFile, $logLine, FILE_APPEND | LOCK_EX);
    }
    
    /**
     * Enviar alerta por email
     */
    private function sendAlert($results) {
        if (!Environment::get('NOTIFICATIONS_ENABLED', false)) {
            return;
        }
        
        $subject = "SIF-GT: Alerta de Sistema - Estado: {$results['status']}";
        
        $message = "Sistema de Inventario Forestal Guatemala\n";
        $message .= "Alerta de Monitoreo del Sistema\n\n";
        $message .= "Fecha: {$results['timestamp']}\n";
        $message .= "Estado General: {$results['status']}\n\n";
        
        $message .= "Detalles de Verificaciones:\n";
        $message .= str_repeat("=", 50) . "\n";
        
        foreach ($results['checks'] as $checkName => $check) {
            $message .= "\n{$check['name']}: {$check['status']}\n";
            $message .= "Mensaje: {$check['message']}\n";
            
            if (!empty($check['details'])) {
                $message .= "Detalles: " . json_encode($check['details'], JSON_PRETTY_PRINT) . "\n";
            }
        }
        
        $headers = [
            'From: ' . Environment::get('MAIL_FROM_ADDRESS', 'noreply@localhost'),
            'Content-Type: text/plain; charset=UTF-8'
        ];
        
        @mail($this->alertEmail, $subject, $message, implode("\r\n", $headers));
    }
    
    /**
     * Formatear bytes a formato legible
     */
    private function formatBytes($bytes, $precision = 2) {
        $units = array('B', 'KB', 'MB', 'GB', 'TB');
        
        for ($i = 0; $bytes > 1024 && $i < count($units) - 1; $i++) {
            $bytes /= 1024;
        }
        
        return round($bytes, $precision) . ' ' . $units[$i];
    }
    
    /**
     * Parsear memory_limit a bytes
     */
    private function parseMemoryLimit($limit) {
        $limit = trim($limit);
        $last = strtolower($limit[strlen($limit)-1]);
        $limit = (int) $limit;
        
        switch($last) {
            case 'g':
                $limit *= 1024;
            case 'm':
                $limit *= 1024;
            case 'k':
                $limit *= 1024;
        }
        
        return $limit;
    }
}

// Ejecutar monitoreo si se llama directamente
if (php_sapi_name() === 'cli' || (isset($_GET['action']) && $_GET['action'] === 'monitor')) {
    $monitor = new SystemMonitor();
    $results = $monitor->runFullMonitoring();
    
    if (php_sapi_name() === 'cli') {
        echo "=== SYSTEM MONITOR REPORT ===\n";
        echo "Fecha: " . $results['timestamp'] . "\n";
        echo "Estado General: " . $results['status'] . "\n\n";
        
        foreach ($results['checks'] as $checkName => $check) {
            echo "{$check['name']}: {$check['status']}\n";
            echo "  {$check['message']}\n";
            if (!empty($check['details'])) {
                echo "  Detalles: " . json_encode($check['details']) . "\n";
            }
            echo "\n";
        }
        
        echo "=============================\n";
    } else {
        header('Content-Type: application/json');
        echo json_encode($results, JSON_PRETTY_PRINT);
    }
}
?>