<?php
/**
 * NEXUS IT - CronController
 * Tareas automáticas ejecutadas por cron job
 * Configurar en cPanel: cada 15 min -> wget -qO- https://ana.evolucionamos.com/api/v1/cron/run?key=NEXUS_CRON_2026
 */
class CronController {

    private static string $cronKey = 'NEXUS_CRON_2026';

    private static function verificarKey(): void {
        $key = $_GET['key'] ?? '';
        if ($key !== self::$cronKey) {
            Response::forbidden('Clave de cron inválida');
        }
    }

    /**
     * GET /cron/run — Ejecutar todas las tareas
     */
    public static function run(): void {
        self::verificarKey();
        $results = [];
        $results['sla_vencidos'] = self::verificarSLA();
        $results['alertas_offline'] = self::verificarOffline();
        $results['garantias'] = self::verificarGarantias();
        $results['mantenimientos'] = self::generarMantenimientosRecurrentes();
        $results['timestamp'] = date('Y-m-d H:i:s');
        Response::success($results, 'Cron ejecutado correctamente');
    }

    /**
     * Verificar tickets vencidos por SLA y generar alertas
     */
    private static function verificarSLA(): int {
        $db = getDB();
        // Tickets abiertos que ya pasaron su fecha de vencimiento
        $stmt = $db->query("SELECT t.id, t.numero_ticket, t.titulo, t.solicitante_id, t.tecnico_asignado_id,
            t.prioridad, t.fecha_vencimiento,
            TIMESTAMPDIFF(HOUR, t.fecha_vencimiento, NOW()) as horas_vencido
            FROM tickets t
            WHERE t.estado IN ('abierto','en_progreso','pendiente')
            AND t.fecha_vencimiento IS NOT NULL
            AND t.fecha_vencimiento < NOW()");
        $vencidos = $stmt->fetchAll();
        $count = 0;

        $notifStmt = $db->prepare("INSERT IGNORE INTO notificaciones (usuario_id, titulo, mensaje, tipo, url_accion, referencia_tipo, referencia_id) VALUES (?,?,?,?,?,?,?)");

        foreach ($vencidos as $t) {
            // Escalar prioridad si lleva más de 2h vencido y no es ya crítica
            if ($t['horas_vencido'] > 2 && $t['prioridad'] !== 'critica') {
                $nuevaPrioridad = match($t['prioridad']) {
                    'baja' => 'media',
                    'media' => 'alta',
                    'alta' => 'critica',
                    default => $t['prioridad']
                };
                $db->prepare("UPDATE tickets SET prioridad = ? WHERE id = ? AND prioridad = ?")
                    ->execute([$nuevaPrioridad, $t['id'], $t['prioridad']]);
            }

            // Notificar al técnico asignado (si existe)
            if ($t['tecnico_asignado_id']) {
                $notifStmt->execute([
                    $t['tecnico_asignado_id'],
                    "⏰ SLA Vencido: {$t['numero_ticket']}",
                    "El ticket '{$t['titulo']}' ha excedido su tiempo de SLA por {$t['horas_vencido']}h",
                    'ticket',
                    "/app/admin/ticket-detalle.html?id={$t['id']}",
                    'ticket', $t['id']
                ]);
            }

            // Notificar a admins
            $admins = $db->query("SELECT id FROM usuarios WHERE rol = 'admin' AND activo = 1")->fetchAll();
            foreach ($admins as $admin) {
                $notifStmt->execute([
                    $admin['id'],
                    "⏰ SLA Vencido: {$t['numero_ticket']}",
                    "Ticket '{$t['titulo']}' vencido hace {$t['horas_vencido']}h",
                    'ticket',
                    "/app/admin/ticket-detalle.html?id={$t['id']}",
                    'ticket', $t['id']
                ]);
            }
            $count++;
        }
        return $count;
    }

    /**
     * Verificar activos que no reportan (offline)
     */
    private static function verificarOffline(): int {
        $db = getDB();
        // Leer umbral de config
        $horas = 24;
        $cfg = $db->query("SELECT valor FROM configuracion WHERE clave = 'agent_alerta_offline_horas'")->fetch();
        if ($cfg) $horas = (int)$cfg['valor'];

        $stmt = $db->prepare("SELECT id, codigo_activo, hostname FROM activos
            WHERE estado = 'activo' AND ultimo_reporte IS NOT NULL
            AND ultimo_reporte < DATE_SUB(NOW(), INTERVAL ? HOUR)
            AND id NOT IN (SELECT activo_id FROM alertas WHERE tipo_alerta = 'offline' AND resuelta = 0)");
        $stmt->execute([$horas]);
        $offline = $stmt->fetchAll();
        $count = 0;

        $alertStmt = $db->prepare("INSERT INTO alertas (activo_id, tipo_alerta, mensaje, severidad) VALUES (?,?,?,?)");
        foreach ($offline as $a) {
            $alertStmt->execute([
                $a['id'], 'offline',
                "Equipo {$a['codigo_activo']} ({$a['hostname']}) sin reportar hace más de {$horas}h",
                'warning'
            ]);
            $count++;
        }
        return $count;
    }

    /**
     * Verificar garantías próximas a vencer
     */
    private static function verificarGarantias(): int {
        $db = getDB();
        $stmt = $db->query("SELECT id, codigo_activo, marca, modelo, garantia_hasta
            FROM activos
            WHERE garantia_hasta IS NOT NULL
            AND garantia_hasta BETWEEN CURDATE() AND DATE_ADD(CURDATE(), INTERVAL 30 DAY)
            AND id NOT IN (SELECT activo_id FROM alertas WHERE tipo_alerta = 'garantia_vence' AND resuelta = 0)");
        $proximos = $stmt->fetchAll();
        $count = 0;

        $alertStmt = $db->prepare("INSERT INTO alertas (activo_id, tipo_alerta, mensaje, severidad) VALUES (?,?,?,?)");
        foreach ($proximos as $a) {
            $dias = (int)((strtotime($a['garantia_hasta']) - time()) / 86400);
            $alertStmt->execute([
                $a['id'], 'garantia_vence',
                "Garantía de {$a['codigo_activo']} ({$a['marca']} {$a['modelo']}) vence en {$dias} días ({$a['garantia_hasta']})",
                $dias <= 7 ? 'critical' : 'warning'
            ]);
            $count++;
        }
        return $count;
    }

    /**
     * Generar próximas instancias de mantenimientos recurrentes
     */
    private static function generarMantenimientosRecurrentes(): int {
        $db = getDB();
        $stmt = $db->query("SELECT * FROM mantenimientos
            WHERE recurrencia != 'unica' AND estado = 'completado'
            ORDER BY fecha_completado DESC");
        $recurrentes = $stmt->fetchAll();
        $count = 0;

        foreach ($recurrentes as $m) {
            $intervalo = match($m['recurrencia']) {
                'semanal' => '+1 week',
                'mensual' => '+1 month',
                'trimestral' => '+3 months',
                'semestral' => '+6 months',
                'anual' => '+1 year',
                default => null
            };
            if (!$intervalo) continue;

            $proximaFecha = date('Y-m-d', strtotime($m['fecha_programada'] . ' ' . $intervalo));

            // Verificar que no exista ya uno programado para esa fecha
            $exists = $db->prepare("SELECT id FROM mantenimientos WHERE activo_id = ? AND titulo = ? AND fecha_programada = ? AND estado = 'programado'");
            $exists->execute([$m['activo_id'], $m['titulo'], $proximaFecha]);
            if ($exists->fetch()) continue;

            // Solo si la próxima fecha es dentro de los próximos 30 días
            if (strtotime($proximaFecha) > strtotime('+30 days')) continue;

            $db->prepare("INSERT INTO mantenimientos (activo_id, tecnico_id, tipo, titulo, descripcion, estado, fecha_programada, recurrencia) VALUES (?,?,?,?,?,?,?,?)")
                ->execute([$m['activo_id'], $m['tecnico_id'], $m['tipo'], $m['titulo'], $m['descripcion'], 'programado', $proximaFecha, $m['recurrencia']]);

            // Timeline
            $db->prepare("INSERT INTO activo_timeline (activo_id, tipo_evento, titulo, descripcion) VALUES (?,?,?,?)")
                ->execute([$m['activo_id'], 'mantenimiento', "Mantenimiento {$m['tipo']} programado: {$m['titulo']}", "Fecha: {$proximaFecha} | Recurrencia: {$m['recurrencia']}"]);

            $count++;
        }
        return $count;
    }
}
