<?php
/**
 * NEXUS IT — API Router v3.0 (Fase 1 + 2 + 3)
 */

require_once __DIR__ . '/config/app.php';
require_once __DIR__ . '/config/database.php';
require_once __DIR__ . '/config/cors.php';

require_once __DIR__ . '/helpers/JWT.php';
require_once __DIR__ . '/helpers/Response.php';
require_once __DIR__ . '/helpers/Validator.php';
require_once __DIR__ . '/helpers/FileUpload.php';
if (file_exists(__DIR__ . '/helpers/EmailService.php')) require_once __DIR__ . '/helpers/EmailService.php';
if (file_exists(__DIR__ . '/helpers/AuditService.php')) require_once __DIR__ . '/helpers/AuditService.php';
if (file_exists(__DIR__ . '/helpers/EmailToTicket.php')) require_once __DIR__ . '/helpers/EmailToTicket.php';
if (file_exists(__DIR__ . '/helpers/AIClassifier.php')) require_once __DIR__ . '/helpers/AIClassifier.php';
if (file_exists(__DIR__ . '/helpers/SLACalculator.php')) require_once __DIR__ . '/helpers/SLACalculator.php';
if (file_exists(__DIR__ . '/helpers/AppConfig.php')) require_once __DIR__ . '/helpers/AppConfig.php';

require_once __DIR__ . '/middleware/AuthMiddleware.php';
require_once __DIR__ . '/middleware/RoleMiddleware.php';
require_once __DIR__ . '/middleware/RateLimiter.php';

// Controladores (carga segura)
$controllers = [
    'AuthController', 'TicketController', 'ActivoController',
    'UsuarioController', 'TelemetriaController', 'DashboardController',
    'NotificacionController', 'CategoriaController',
    'MantenimientoController', 'AlertaController', 'KnowledgeBaseController',
    'ReporteController', 'ConfiguracionController', 'LogController',
    'CronController', 'DepreciacionController', 'IntegracionController', 'SesionRemotaController', 'CredencialController', 'EncuestaController',
    'TicketTemplateController', 'LicenciaController', 'ContratoController', 'ImportController', 'SupervisorController',
];
foreach ($controllers as $ctrl) {
    $f = __DIR__ . "/controllers/{$ctrl}.php";
    if (file_exists($f)) require_once $f;
}

configurarCORS();
header('Content-Type: application/json; charset=utf-8');
RateLimiter::verificar(120, 60);

$method = $_SERVER['REQUEST_METHOD'];
$path   = parse_url($_SERVER['REQUEST_URI'], PHP_URL_PATH);

$path = preg_replace('#^(.*/)?api/(v1/)?#', '', $path);
$path = trim($path, '/');
$segments = $path ? explode('/', $path) : [];

$s0 = $segments[0] ?? '';
$s1 = $segments[1] ?? null;
$s2 = $segments[2] ?? null;

$id = null;
$accion = null;
if ($s1 !== null && ctype_digit($s1)) {
    $id = (int)$s1;
    $accion = $s2;
} else {
    $accion = $s1;
}

// Export CSV — needs special content-type handling
if ($s0 === 'reportes' && $accion === 'exportar-tickets' && $method === 'GET') {
    if (empty($_SERVER['HTTP_AUTHORIZATION']) && !empty($_GET['token'])) {
        $_SERVER['HTTP_AUTHORIZATION'] = 'Bearer ' . $_GET['token'];
    }
    header_remove('Content-Type');
    ReporteController::exportarTickets();
    exit;
}
if ($s0 === 'logs' && $accion === 'exportar' && $method === 'GET') {
    if (empty($_SERVER['HTTP_AUTHORIZATION']) && !empty($_GET['token'])) {
        $_SERVER['HTTP_AUTHORIZATION'] = 'Bearer ' . $_GET['token'];
    }
    header_remove('Content-Type');
    LogController::exportar();
    exit;
}

set_exception_handler(function (Throwable $e) {
    $code = ($e->getCode() >= 400 && $e->getCode() < 600) ? $e->getCode() : 500;
    http_response_code($code);
    echo json_encode([
        'success' => false,
        'message' => APP_DEBUG ? $e->getMessage() : 'Error interno del servidor',
        'trace'   => APP_DEBUG ? $e->getTraceAsString() : null
    ]);
});

// ---- AGENTE (public, autenticado por token) ----
if ($s0 === 'agente' && $method === 'POST' && $accion === 'heartbeat') {
    $body = json_decode(file_get_contents('php://input'), true);
    $token = $body['agent_token'] ?? '';
    if (!$token) Response::error('Token requerido', 401);
    
    try {
        $db = getDB();
        
        // Find activo by token
        $stmt = $db->prepare("SELECT id FROM activos WHERE agent_token = ?");
        $stmt->execute([$token]);
        $activo = $stmt->fetch();
        
        if (!$activo) {
            // Auto-register new asset
            $hostname = $body['hostname'] ?? 'UNKNOWN';
            $tipo = $body['tipo'] ?? 'desktop';
            $prefix = strtoupper(substr($tipo, 0, 3));
            $count = (int)$db->query("SELECT COUNT(*) FROM activos WHERE tipo = '{$tipo}'")->fetchColumn();
            $codigo = $prefix . '-' . str_pad($count + 1, 4, '0', STR_PAD_LEFT);
            
            // Get actual columns from activos table
            $cols = array_column($db->query("SHOW COLUMNS FROM activos")->fetchAll(), 'Field');
            
            $data = ['codigo_activo' => $codigo, 'tipo' => $tipo, 'estado' => 'activo', 'agent_token' => $token];
            // Generate uuid if column exists
            if (in_array('uuid', $cols)) {
                $data['uuid'] = sprintf('%s-%s-%s-%s-%s', bin2hex(random_bytes(4)), bin2hex(random_bytes(2)), bin2hex(random_bytes(2)), bin2hex(random_bytes(2)), bin2hex(random_bytes(6)));
            }
            $map = [
                'marca' => $body['manufacturer'] ?? $body['marca'] ?? 'Desconocido',
                'modelo' => $body['modelo'] ?? 'Desconocido',
                'hostname' => $hostname,
                'serial' => $body['serial'] ?? null,
                'numero_serie' => $body['serial'] ?? null,
                'agent_version' => $body['agent_version'] ?? '1.0',
                'direccion_ip' => $body['ip'] ?? null,
                'ip_address' => $body['ip'] ?? null,
                'direccion_mac' => $body['mac'] ?? null,
                'mac_address' => $body['mac'] ?? null,
                'sistema_operativo' => $body['so'] ?? null,
                'so' => $body['so'] ?? null,
                'procesador' => $body['procesador'] ?? null,
                'ram_total_gb' => $body['ram_total_gb'] ?? null,
                'ram_gb' => isset($body['ram_total_gb']) ? (int)$body['ram_total_gb'] : null,
                'disco_total_gb' => $body['disco_total_gb'] ?? null,
                'disco_gb' => isset($body['disco_total_gb']) ? (int)$body['disco_total_gb'] : null,
                'antivirus' => $body['antivirus'] ?? null,
                'dominio' => $body['dominio'] ?? null,
                'ultimo_usuario' => $body['ultimo_usuario'] ?? null,
                'ultima_conexion' => date('Y-m-d H:i:s'),
            ];
            foreach ($map as $col => $val) {
                if (in_array($col, $cols)) $data[$col] = $val;
            }
            
            $columns = implode(',', array_keys($data));
            $placeholders = implode(',', array_fill(0, count($data), '?'));
            $db->prepare("INSERT INTO activos ({$columns}) VALUES ({$placeholders})")->execute(array_values($data));
            $activoId = (int)$db->lastInsertId();
        } else {
            $activoId = (int)$activo['id'];
            $cols = array_column($db->query("SHOW COLUMNS FROM activos")->fetchAll(), 'Field');
            
            $map = [
                'hostname' => $body['hostname'] ?? null,
                'direccion_ip' => $body['ip'] ?? null,
                'ip_address' => $body['ip'] ?? null,
                'direccion_mac' => $body['mac'] ?? null,
                'mac_address' => $body['mac'] ?? null,
                'sistema_operativo' => $body['so'] ?? null,
                'so' => $body['so'] ?? null,
                'procesador' => $body['procesador'] ?? null,
                'ram_total_gb' => $body['ram_total_gb'] ?? null,
                'ram_gb' => isset($body['ram_total_gb']) ? (int)$body['ram_total_gb'] : null,
                'disco_total_gb' => $body['disco_total_gb'] ?? null,
                'disco_gb' => isset($body['disco_total_gb']) ? (int)$body['disco_total_gb'] : null,
                'antivirus' => $body['antivirus'] ?? null,
                'dominio' => $body['dominio'] ?? null,
                'ultimo_usuario' => $body['ultimo_usuario'] ?? null,
                'agent_version' => $body['agent_version'] ?? '1.0',
                'ultima_conexion' => date('Y-m-d H:i:s'),
            ];
            $sets = []; $params = [];
            foreach ($map as $col => $val) {
                if (in_array($col, $cols)) { $sets[] = "{$col}=?"; $params[] = $val; }
            }
            $params[] = $activoId;
            if ($sets) $db->prepare("UPDATE activos SET " . implode(',', $sets) . " WHERE id=?")->execute($params);
        }
        
        // Telemetry
        if (isset($body['cpu_uso']) || isset($body['ram_uso'])) {
            $telCols = array_column($db->query("SHOW COLUMNS FROM telemetria")->fetchAll(), 'Field');
            $telData = ['activo_id' => $activoId];
            $telMap = [
                'cpu_uso_pct' => $body['cpu_uso'] ?? null,
                'cpu_uso' => $body['cpu_uso'] ?? null,
                'ram_uso_pct' => $body['ram_uso'] ?? null,
                'ram_uso' => $body['ram_uso'] ?? null,
                'disco_uso_pct' => $body['disco_uso'] ?? null,
                'disco_uso' => $body['disco_uso'] ?? null,
                'ram_usada_gb' => $body['ram_usada_gb'] ?? null,
                'disco_libre_gb' => $body['disco_libre_gb'] ?? null,
                'uptime_seconds' => $body['uptime_seconds'] ?? null,
                'procesos_activos' => $body['procesos_activos'] ?? null,
            ];
            foreach ($telMap as $col => $val) {
                if (in_array($col, $telCols) && $val !== null) $telData[$col] = $val;
            }
            $columns = implode(',', array_keys($telData));
            $placeholders = implode(',', array_fill(0, count($telData), '?'));
            $db->prepare("INSERT INTO telemetria ({$columns}) VALUES ({$placeholders})")->execute(array_values($telData));
        }
        
        // Software
        if (isset($body['software']) && is_array($body['software'])) {
            $db->prepare("DELETE FROM activo_software WHERE activo_id = ?")->execute([$activoId]);
            $swCols = array_column($db->query("SHOW COLUMNS FROM activo_software")->fetchAll(), 'Field');
            $useNombre = in_array('nombre_software', $swCols) ? 'nombre_software' : 'nombre';
            $useEditor = in_array('editor', $swCols) ? 'editor' : 'publisher';
            $swStmt = $db->prepare("INSERT INTO activo_software (activo_id, {$useNombre}, version, {$useEditor}) VALUES (?,?,?,?)");
            foreach (array_slice($body['software'], 0, 200) as $sw) {
                $swStmt->execute([$activoId, $sw['nombre'] ?? '', $sw['version'] ?? '', $sw['editor'] ?? '']);
            }
        }
        
        // Load alert thresholds from config
        $umbrales = ['disco' => 90, 'ram' => 95, 'temp_cpu' => 85];
        try {
            $cfgStmt = $db->query("SELECT clave, valor FROM configuracion WHERE grupo = 'agente'");
            $cfgRows = $cfgStmt->fetchAll(\PDO::FETCH_KEY_PAIR);
            if (!empty($cfgRows['agent_alerta_disco_pct'])) $umbrales['disco'] = (int)$cfgRows['agent_alerta_disco_pct'];
            if (!empty($cfgRows['agent_alerta_ram_pct'])) $umbrales['ram'] = (int)$cfgRows['agent_alerta_ram_pct'];
            if (!empty($cfgRows['agent_alerta_temp_cpu'])) $umbrales['temp_cpu'] = (int)$cfgRows['agent_alerta_temp_cpu'];
        } catch (\Exception $e) {}

        // Alerts using configurable thresholds
        $alertas = [];
        if (isset($body['disco_uso']) && $body['disco_uso'] > $umbrales['disco']) {
            $alertas[] = ['tipo'=>'disco_lleno','mensaje'=>"Disco al {$body['disco_uso']}% en {$body['hostname']} (umbral: {$umbrales['disco']}%)",'severidad'=>$body['disco_uso']>95?'critical':'warning'];
        }
        if (isset($body['ram_uso']) && $body['ram_uso'] > $umbrales['ram']) {
            $alertas[] = ['tipo'=>'ram_alta','mensaje'=>"RAM al {$body['ram_uso']}% en {$body['hostname']} (umbral: {$umbrales['ram']}%)",'severidad'=>'warning'];
        }
        if (isset($body['cpu_uso']) && $body['cpu_uso'] > 95) {
            $alertas[] = ['tipo'=>'cpu_alta','mensaje'=>"CPU al {$body['cpu_uso']}% en {$body['hostname']}",'severidad'=>'warning'];
        }
        if (isset($body['temp_cpu']) && $body['temp_cpu'] > $umbrales['temp_cpu']) {
            $alertas[] = ['tipo'=>'temp_alta','mensaje'=>"Temperatura CPU {$body['temp_cpu']}°C en {$body['hostname']} (umbral: {$umbrales['temp_cpu']}°C)",'severidad'=>$body['temp_cpu']>95?'critical':'warning'];
        }
        if (empty($body['antivirus']) || $body['antivirus'] === 'No detectado') {
            $alertas[] = ['tipo'=>'sin_antivirus','mensaje'=>"Sin antivirus en {$body['hostname']}",'severidad'=>'critical'];
        }
        foreach ($alertas as $a) {
            $ex = $db->prepare("SELECT id FROM agente_alertas WHERE activo_id=? AND tipo=? AND resuelta=0 AND created_at>DATE_SUB(NOW(),INTERVAL 1 HOUR)");
            $ex->execute([$activoId, $a['tipo']]);
            if (!$ex->fetch()) {
                $db->prepare("INSERT INTO agente_alertas (activo_id,tipo,mensaje,severidad) VALUES(?,?,?,?)")->execute([$activoId,$a['tipo'],$a['mensaje'],$a['severidad']]);
            }
        }
        
        Response::success(['activo_id' => $activoId, 'alertas' => count($alertas)]);
    } catch (\Exception $e) {
        Response::error('Heartbeat error: ' . $e->getMessage(), 500);
    }
    exit;
}

if ($s0 === 'agente' && $method === 'POST' && $accion === 'generar-token') {
    $payload = AuthMiddleware::verificar();
    RoleMiddleware::requireTecnico($payload);
    $token = bin2hex(random_bytes(32));
    Response::success(['token' => $token]);
    exit;
}

if ($s0 === 'agente' && $method === 'GET' && $accion === 'descargar') {
    $payload = AuthMiddleware::verificar();
    $token = $_GET['token'] ?? '';
    if (!$token) Response::error('Token requerido');
    
    $paths = [
        dirname(__DIR__) . '/agente/NexusAgent.ps1',
        __DIR__ . '/../agente/NexusAgent.ps1',
        $_SERVER['DOCUMENT_ROOT'] . '/agente/NexusAgent.ps1',
        '/home/evolucionamos/public_html/ana/agente/NexusAgent.ps1',
    ];
    $agentPath = null;
    foreach ($paths as $p) {
        if (file_exists($p)) { $agentPath = $p; break; }
    }
    if (!$agentPath) Response::error('Archivo no encontrado. Sube agente/NexusAgent.ps1 al servidor.');
    
    $content = file_get_contents($agentPath);
    $content = str_replace('##TOKEN##', $token, $content);
    
    Response::success(['contenido' => base64_encode($content)]);
    exit;
}

if ($s0 === 'agente' && $method === 'GET' && $accion === 'alertas') {
    $payload = AuthMiddleware::verificar();
    $db = getDB();
    $soloActivas = ($_GET['activas'] ?? '1') === '1' ? 'AND resuelta = 0' : '';
    $stmt = $db->query("SELECT aa.*, a.hostname, a.codigo_activo FROM agente_alertas aa JOIN activos a ON aa.activo_id = a.id WHERE 1=1 {$soloActivas} ORDER BY aa.created_at DESC LIMIT 100");
    Response::success($stmt->fetchAll());
    exit;
}

if ($s0 === 'agente' && $method === 'GET' && $accion === 'stats') {
    $payload = AuthMiddleware::verificar();
    $db = getDB();
    $totalAgentes = $db->query("SELECT COUNT(*) FROM activos WHERE agent_token IS NOT NULL")->fetchColumn();
    $online = $db->query("SELECT COUNT(*) FROM activos WHERE agent_token IS NOT NULL AND ultima_conexion > DATE_SUB(NOW(), INTERVAL 20 MINUTE)")->fetchColumn();
    $offline = $totalAgentes - $online;
    $alertasActivas = $db->query("SELECT COUNT(*) FROM agente_alertas WHERE resuelta = 0")->fetchColumn();
    $alertasCriticas = $db->query("SELECT COUNT(*) FROM agente_alertas WHERE resuelta = 0 AND severidad = 'critical'")->fetchColumn();
    Response::success([
        'total_agentes' => (int)$totalAgentes,
        'online' => (int)$online,
        'offline' => (int)$offline,
        'alertas_activas' => (int)$alertasActivas,
        'alertas_criticas' => (int)$alertasCriticas
    ]);
    exit;
}

// ---- AUTH (pre-router, no middleware) ----
if ($s0 === 'auth' && $method === 'POST' && $accion === 'login') { 
    $body = Validator::getBody();
    if (empty($body['email']) || empty($body['password'])) {
        Response::error('Email y contraseña requeridos', 400);
    }
    $db = getDB();
    $stmt = $db->prepare("SELECT * FROM usuarios WHERE email = ? AND activo = 1 LIMIT 1");
    $stmt->execute([$body['email']]);
    $user = $stmt->fetch();
    if (!$user || !password_verify($body['password'], $user['password_hash'])) {
        Response::error('Credenciales incorrectas', 401);
    }
    $db->prepare("UPDATE usuarios SET ultimo_login = NOW() WHERE id = ?")->execute([$user['id']]);
    $payload = ['sub' => $user['id'], 'email' => $user['email'], 'rol' => $user['rol'], 'nombre' => $user['nombre_completo']];
    $token = JWT::generar($payload);
    Response::success([
        'token' => $token,
        'usuario' => [
            'id' => $user['id'], 'nombre_completo' => $user['nombre_completo'],
            'email' => $user['email'], 'rol' => $user['rol'],
            'departamento' => $user['departamento']
        ]
    ], 'Login exitoso');
    exit;
}
if ($s0 === 'auth' && $method === 'POST' && $accion === 'register') { AuthController::register(); exit; }

// DEBUG: if login still fails, log what's happening
if ($s0 === 'auth' && $accion === 'login') {
    error_log("DEBUG LOGIN: method=$method s0=$s0 accion=$accion path=$path");
}
if ($s0 === 'debug-info') {
    echo json_encode(['s0'=>$s0,'s1'=>$s1,'s2'=>$s2,'accion'=>$accion,'method'=>$method,'path'=>$path,'segments'=>$segments,'uri'=>$_SERVER['REQUEST_URI']]);
    exit;
}

try {
    match (true) {
    
        // ---- AUTH ----
        $s0 === 'auth' && $accion === 'login'    && $method === 'POST' => AuthController::login(),
        $s0 === 'auth' && $accion === 'register'  && $method === 'POST' => AuthController::register(),
        $s0 === 'auth' && $accion === 'verify-password' && $method === 'POST' => call_user_func(function() {
            $payload = AuthMiddleware::verificar();
            $body = Validator::getBody();
            $password = $body['password'] ?? '';
            if (empty($password)) Response::error('Contraseña requerida');
            $db = getDB();
            $stmt = $db->prepare("SELECT password FROM usuarios WHERE id = ?");
            $stmt->execute([$payload['sub']]);
            $hash = $stmt->fetchColumn();
            if ($hash && password_verify($password, $hash)) {
                Response::success(true, 'Verificación exitosa');
            } else {
                Response::error('Contraseña incorrecta', 401);
            }
        }),
        $s0 === 'auth' && $accion === 'refresh'   && $method === 'POST' => AuthController::refresh(),
        $s0 === 'auth' && $accion === 'me'        && $method === 'GET'  => AuthController::me(),
        $s0 === 'auth' && $accion === 'password'  && $method === 'PUT'  => AuthController::cambiarPassword(),
        
        // ---- TICKETS ----
        $s0 === 'tickets' && !$id && !$accion && $method === 'GET'  => TicketController::index(),
        $s0 === 'tickets' && !$id && !$accion && $method === 'POST' => TicketController::crear(),
        $s0 === 'tickets' && $id  && !$accion && $method === 'GET'  => TicketController::ver($id),
        $s0 === 'tickets' && $id  && !$accion && $method === 'PUT'  => TicketController::actualizar($id),
        $s0 === 'tickets' && $id  && $accion === 'comentarios' && $method === 'POST' => TicketController::agregarComentario($id),
        $s0 === 'tickets' && $id  && $accion === 'adjuntos'    && $method === 'POST' => TicketController::subirAdjunto($id),
        $s0 === 'tickets' && $id  && $accion === 'reabrir'     && $method === 'POST' => TicketController::reabrir($id),
        $s0 === 'tickets' && $id  && $accion === 'calificar'   && $method === 'POST' => TicketController::calificar($id),
        
        // ---- ACTIVOS ----
        $s0 === 'activos' && !$id && !$accion && $method === 'GET'  => ActivoController::index(),
        $s0 === 'activos' && !$id && !$accion && $method === 'POST' => ActivoController::crear(),
        $s0 === 'activos' && $id  && !$accion && $method === 'GET'  => ActivoController::ver($id),
        $s0 === 'activos' && $id  && !$accion && $method === 'PUT'  => ActivoController::actualizar($id),
        $s0 === 'activos' && $id  && $accion === 'asignar'   && $method === 'POST' => ActivoController::asignar($id),
        $s0 === 'activos' && $id  && $accion === 'timeline'  && $method === 'GET'  => ActivoController::timeline($id),
        // Bitácora
        $s0 === 'activos' && $id  && $accion === 'bitacora'  && !isset($segmentos[3]) && $method === 'GET'  => ActivoController::bitacora($id),
        $s0 === 'activos' && $id  && $accion === 'bitacora'  && !isset($segmentos[3]) && $method === 'POST' => ActivoController::registrarBitacora($id),
        $s0 === 'activos' && $id  && $accion === 'bitacora'  && isset($segmentos[3])  && $method === 'PUT'    => ActivoController::actualizarBitacora($id, (int)$segmentos[3]),
        $s0 === 'activos' && $id  && $accion === 'bitacora'  && isset($segmentos[3])  && $method === 'DELETE' => ActivoController::eliminarBitacora($id, (int)$segmentos[3]),
        
        // ---- USUARIOS ----
        $s0 === 'usuarios' && !$id && !$accion && $method === 'GET'  => UsuarioController::index(),
        $s0 === 'usuarios' && $accion === 'tecnicos'      && $method === 'GET' => UsuarioController::tecnicos(),
        $s0 === 'usuarios' && $accion === 'departamentos' && $method === 'GET' => UsuarioController::departamentos(),
        $s0 === 'usuarios' && $id  && !$accion && $method === 'GET'  => UsuarioController::ver($id),
        $s0 === 'usuarios' && $id  && !$accion && $method === 'PUT'  => UsuarioController::actualizar($id),
        
        // ---- AGENTE / TELEMETRÍA ----
        $s0 === 'agent' && $accion === 'register'  && $method === 'POST' => TelemetriaController::registrarAgente(),
        $s0 === 'agent' && $accion === 'telemetry' && $method === 'POST' => TelemetriaController::recibirTelemetria(),
        $s0 === 'agent' && $accion === 'hardware'  && $method === 'POST' => TelemetriaController::reportarHardware(),
        $s0 === 'agent' && $accion === 'software'  && $method === 'POST' => TelemetriaController::reportarSoftware(),
        
        // ---- DASHBOARD ----
        $s0 === 'dashboard' && $accion === 'stats'                && $method === 'GET' => DashboardController::stats(),
        $s0 === 'dashboard' && $accion === 'tickets-por-estado'   && $method === 'GET' => DashboardController::ticketsPorEstado(),
        $s0 === 'dashboard' && $accion === 'tickets-por-categoria' && $method === 'GET' => DashboardController::ticketsPorCategoria(),
        $s0 === 'dashboard' && $accion === 'activos-por-tipo'     && $method === 'GET' => DashboardController::activosPorTipo(),
        $s0 === 'dashboard' && $accion === 'rendimiento-tecnicos' && $method === 'GET' => DashboardController::rendimientoTecnicos(),
        $s0 === 'dashboard' && $accion === 'tickets-tendencia'    && $method === 'GET' => DashboardController::ticketsTendencia(),
        
        // ---- CATEGORÍAS ----
        $s0 === 'categorias' && !$id && $method === 'GET'  => CategoriaController::index(),
        $s0 === 'categorias' && !$id && $method === 'POST' => CategoriaController::crear(),
        $s0 === 'categorias' && $id  && !$accion && $method === 'PUT'  => CategoriaController::actualizar($id),
        $s0 === 'categorias' && $id  && !$accion && $method === 'DELETE' => CategoriaController::eliminar($id),
        $s0 === 'categorias' && $id  && $accion === 'subcategorias' && $method === 'GET' => CategoriaController::subsPorCategoria($id),
        $s0 === 'subcategorias' && !$id && $method === 'POST' => CategoriaController::crearSub(),
        $s0 === 'subcategorias' && $id  && $method === 'PUT'  => CategoriaController::actualizarSub($id),
        $s0 === 'subcategorias' && $id  && $method === 'DELETE' => CategoriaController::eliminarSub($id),
        
        // ---- NOTIFICACIONES ----
        $s0 === 'notificaciones' && !$id && !$accion && $method === 'GET'  => NotificacionController::index(),
        $s0 === 'notificaciones' && $accion === 'no-leidas' && $method === 'GET' => NotificacionController::noLeidas(),
        $s0 === 'notificaciones' && $accion === 'leer-todas' && $method === 'PUT' => NotificacionController::marcarTodasLeidas(),
        $s0 === 'notificaciones' && $id && $accion === 'leer' && $method === 'PUT' => NotificacionController::marcarLeida($id),
        
        // ---- MANTENIMIENTOS (FASE 2) ----
        $s0 === 'mantenimientos' && class_exists('MantenimientoController') && !$id && !$accion && $method === 'GET'  => MantenimientoController::index(),
        $s0 === 'mantenimientos' && class_exists('MantenimientoController') && !$id && !$accion && $method === 'POST' => MantenimientoController::crear(),
        $s0 === 'mantenimientos' && class_exists('MantenimientoController') && $accion === 'calendario'  && $method === 'GET' => MantenimientoController::calendario(),
        $s0 === 'mantenimientos' && class_exists('MantenimientoController') && $accion === 'proximos'    && $method === 'GET' => MantenimientoController::proximos(),
        $s0 === 'mantenimientos' && class_exists('MantenimientoController') && $id && !$accion && $method === 'GET'  => MantenimientoController::ver($id),
        $s0 === 'mantenimientos' && class_exists('MantenimientoController') && $id && !$accion && $method === 'PUT'  => MantenimientoController::actualizar($id),
        
        // ---- ALERTAS (FASE 2) ----
        $s0 === 'alertas' && class_exists('AlertaController') && !$id && !$accion && $method === 'GET'  => AlertaController::index(),
        $s0 === 'alertas' && class_exists('AlertaController') && $accion === 'centro'          && $method === 'GET' => AlertaController::centro(),
        $s0 === 'alertas' && class_exists('AlertaController') && $accion === 'resumen'         && $method === 'GET' => AlertaController::resumen(),
        $s0 === 'alertas' && class_exists('AlertaController') && $accion === 'resolver-masivo' && $method === 'PUT' => AlertaController::resolverMasivo(),
        $s0 === 'alertas' && class_exists('AlertaController') && $id && $accion === 'resolver' && $method === 'PUT' => AlertaController::resolver($id),
        
        // ---- BASE DE CONOCIMIENTOS (FASE 2) ----
        $s0 === 'kb' && class_exists('KnowledgeBaseController') && $s1 === 'articulos' && !$s2 && $method === 'GET'  => KnowledgeBaseController::index(),
        $s0 === 'kb' && class_exists('KnowledgeBaseController') && $s1 === 'articulos' && !$s2 && $method === 'POST' => KnowledgeBaseController::crear(),
        $s0 === 'kb' && class_exists('KnowledgeBaseController') && $s1 === 'buscar'            && $method === 'GET'  => KnowledgeBaseController::buscar(),
        $s0 === 'kb' && class_exists('KnowledgeBaseController') && $s1 === 'articulos' && $s2  && $method === 'GET'  => KnowledgeBaseController::ver($s2),
        $s0 === 'kb' && class_exists('KnowledgeBaseController') && $s1 === 'articulos' && $s2 && ctype_digit($s2) && $method === 'PUT' => KnowledgeBaseController::actualizar((int)$s2),
        $s0 === 'kb' && class_exists('KnowledgeBaseController') && $s1 && ctype_digit($s1) && $s2 === 'util' && $method === 'POST' => KnowledgeBaseController::votar((int)$s1),
        
        // ---- REPORTES (FASE 3) ----
        $s0 === 'reportes' && $accion === 'resumen'               && $method === 'GET' => ReporteController::resumen(),
        $s0 === 'reportes' && $accion === 'tickets-por-dia'       && $method === 'GET' => ReporteController::ticketsPorDia(),
        $s0 === 'reportes' && $accion === 'tickets-por-categoria' && $method === 'GET' => ReporteController::ticketsPorCategoria(),
        $s0 === 'reportes' && $accion === 'rendimiento-tecnicos'  && $method === 'GET' => ReporteController::rendimientoTecnicos(),
        $s0 === 'reportes' && $accion === 'sla'                   && $method === 'GET' => ReporteController::sla(),
        $s0 === 'reportes' && $accion === 'activos-resumen'       && $method === 'GET' => ReporteController::activosResumen(),
        $s0 === 'reportes' && $accion === 'tickets-por-usuario'  && $method === 'GET' => ReporteController::ticketsPorUsuario(),
        $s0 === 'reportes' && $accion === 'tiempos-atencion'     && $method === 'GET' => ReporteController::tiemposAtencion(),
        
        // ---- CONFIGURACIÓN (FASE 3) ----
        $s0 === 'configuracion' && !$id && !$accion && $method === 'GET'  => ConfiguracionController::index(),
        $s0 === 'configuracion' && $accion === 'grupos' && $method === 'GET'  => ConfiguracionController::grupos(),
        $s0 === 'configuracion' && $accion === 'branding' && $method === 'GET' => ConfiguracionController::branding(),
        $s0 === 'configuracion' && $accion === 'logo' && $method === 'GET' => ConfiguracionController::serveLogo(),
        $s0 === 'configuracion' && $accion === 'upload-logo' && $method === 'POST' => ConfiguracionController::uploadLogo(),
        $s0 === 'configuracion' && !$id && !$accion && $method === 'PUT'  => ConfiguracionController::actualizar(),
        $s0 === 'configuracion' && $id && !$accion && $method === 'PUT'   => ConfiguracionController::actualizarClave($segments[1]),
        $s0 === 'configuracion' && $accion === 'test-email' && $method === 'POST' => call_user_func(function() {
            $payload = AuthMiddleware::verificar();
            RoleMiddleware::requireAdmin($payload);
            $body = Validator::getBody();
            $to = $body['email'] ?? $payload['email'] ?? '';
            if (empty($to)) Response::error('Email requerido');
            $result = EmailService::test($to);
            Response::success($result, $result['ok'] ? 'Email enviado' : 'Error al enviar');
        }),
        
        // ---- LOG ACTIVIDAD (FASE 3) ----
        $s0 === 'logs' && !$id && !$accion && $method === 'GET' => LogController::index(),
        $s0 === 'logs' && $accion === 'acciones' && $method === 'GET' => LogController::acciones(),
        $s0 === 'logs' && $accion === 'stats' && $method === 'GET' => LogController::stats(),
        $s0 === 'logs' && $accion === 'exportar' && $method === 'GET' => LogController::exportar(),
        
        // ---- CRON (FASE 3) ----
        $s0 === 'cron' && $accion === 'run' && $method === 'GET' => CronController::run(),
        
        // ---- DEPRECIACIÓN (FASE 3) ----
        $s0 === 'depreciacion' && !$id && !$accion && $method === 'GET' => DepreciacionController::index(),
        $s0 === 'depreciacion' && $accion === 'resumen' && $method === 'GET' => DepreciacionController::resumen(),
        $s0 === 'depreciacion' && $id && $accion === 'calcular' && $method === 'POST' => DepreciacionController::calcular($id),
        
        // ---- INTEGRACIONES (FASE 7) ----
        $s0 === 'integracion' && $accion === 'api-key' && $method === 'POST' => IntegracionController::generarApiKey(),
        $s0 === 'integracion' && $accion === 'api-keys' && $method === 'GET' => IntegracionController::listarApiKeys(),
        $s0 === 'integracion' && $accion === 'api-key' && $method === 'DELETE' => IntegracionController::revocarApiKey(),
        $s0 === 'integracion' && $s1 === 'webhook' && $s2 === 'ticket' && $method === 'POST' => IntegracionController::webhookCrearTicket(),
        $s0 === 'integracion' && $accion === 'stats' && $method === 'GET' => IntegracionController::statsPublicas(),
        
        // ---- SESIONES REMOTAS ----
        $s0 === 'sesiones-remotas' && !$id && !$accion && $method === 'GET' => SesionRemotaController::index(),
        $s0 === 'sesiones-remotas' && !$id && !$accion && $method === 'POST' => SesionRemotaController::iniciar(),
        $s0 === 'sesiones-remotas' && $accion === 'stats' && $method === 'GET' => SesionRemotaController::stats(),
        $s0 === 'sesiones-remotas' && $id && !$accion && $method === 'GET' => SesionRemotaController::ver($id),
        $s0 === 'sesiones-remotas' && $id && !$accion && $method === 'PUT' => SesionRemotaController::actualizar($id),
        
        // ---- IA / CLASIFICADOR ----
        $s0 === 'ia' && $accion === 'clasificar' && $method === 'POST' => call_user_func(function() {
            $payload = AuthMiddleware::verificar();
            $body = Validator::getBody();
            $titulo = $body['titulo'] ?? '';
            $descripcion = $body['descripcion'] ?? '';
            $ticketId = isset($body['ticket_id']) ? (int)$body['ticket_id'] : null;
            if (empty($titulo) && empty($descripcion)) Response::error('Título o descripción requeridos');
            $result = AIClassifier::clasificar($titulo, $descripcion, $ticketId);
            Response::success($result, $result['ok'] ? 'Clasificación exitosa' : $result['error']);
        }),
        $s0 === 'ia' && $accion === 'test' && $method === 'POST' => call_user_func(function() {
            $payload = AuthMiddleware::verificar();
            RoleMiddleware::requireAdmin($payload);
            $result = AIClassifier::test();
            Response::success($result, $result['ok'] ? $result['message'] : $result['message']);
        }),

        // ---- IA / CLASIFICADOR ----
        $s0 === 'ia' && $accion === 'clasificar' && $method === 'POST' => call_user_func(function() {
            $payload = AuthMiddleware::verificar();
            $body = Validator::getBody();
            $titulo = $body['titulo'] ?? '';
            $descripcion = $body['descripcion'] ?? '';
            $ticketId = isset($body['ticket_id']) ? (int)$body['ticket_id'] : null;
            if (empty($titulo) && empty($descripcion)) Response::error('Título o descripción requeridos');
            $result = AIClassifier::clasificar($titulo, $descripcion, $ticketId);
            Response::success($result, $result['ok'] ? 'Clasificación exitosa' : ($result['error'] ?? 'Error'));
        }),
        $s0 === 'ia' && $accion === 'test' && $method === 'POST' => call_user_func(function() {
            $payload = AuthMiddleware::verificar();
            RoleMiddleware::requireAdmin($payload);
            $result = AIClassifier::test();
            Response::success($result, $result['message'] ?? '');
        }),

        // ---- EMAIL-TO-TICKET ----
        $s0 === 'email-to-ticket' && $accion === 'config' && $method === 'GET' => call_user_func(function() {
            $payload = AuthMiddleware::verificar();
            RoleMiddleware::requireAdmin($payload);
            $db = getDB();
            $stmt = $db->query("SELECT clave, valor FROM configuracion WHERE grupo = 'email_to_ticket'");
            $config = [];
            foreach ($stmt->fetchAll() as $row) $config[$row['clave']] = $row['valor'];
            Response::success($config);
        }),
        $s0 === 'email-to-ticket' && $accion === 'config' && $method === 'PUT' => call_user_func(function() {
            $payload = AuthMiddleware::verificar();
            RoleMiddleware::requireAdmin($payload);
            $body = Validator::getBody();
            $db = getDB();
            $allowed = ['imap_enabled','imap_host','imap_port','imap_ssl','imap_user','imap_password','imap_folder'];
            foreach ($allowed as $key) {
                if (array_key_exists($key, $body)) {
                    $db->prepare("INSERT INTO configuracion (clave, valor, grupo) VALUES (?,?,'email_to_ticket') ON DUPLICATE KEY UPDATE valor = ?")->execute([$key, $body[$key], $body[$key]]);
                }
            }
            Response::success(null, 'Configuración guardada');
        }),
        $s0 === 'email-to-ticket' && $accion === 'test' && $method === 'POST' => call_user_func(function() {
            $payload = AuthMiddleware::verificar();
            RoleMiddleware::requireAdmin($payload);
            $result = EmailToTicket::testConexion();
            Response::success($result, $result['ok'] ? 'Conexión exitosa' : 'Error de conexión');
        }),
        $s0 === 'email-to-ticket' && $accion === 'procesar' && $method === 'POST' => call_user_func(function() {
            $payload = AuthMiddleware::verificar();
            RoleMiddleware::requireAdmin($payload);
            $result = EmailToTicket::procesar();
            Response::success($result, $result['message']);
        }),
        $s0 === 'cron' && $accion === 'email-to-ticket' && $method === 'GET' => call_user_func(function() {
            // Endpoint para cron job (sin auth, protegido por token secreto)
            $token = $_GET['token'] ?? '';
            $db = getDB();
            $s = $db->prepare("SELECT valor FROM configuracion WHERE clave = 'cron_secret'");
            $s->execute();
            $secret = $s->fetchColumn();
            if (!$secret || $token !== $secret) {
                Response::error('Token inválido', 403);
            }
            $result = EmailToTicket::procesar();
            Response::success($result);
        }),
        
        // ========== SOPORTE REMOTO ==========
        $s0 === 'soporte-remoto' && $method === 'GET' && !$accion => call_user_func(function() {
            $payload = AuthMiddleware::verificar();
            $db = getDB();
            $stmt = $db->query("SELECT sr.*, t.numero_ticket, t.titulo as ticket_titulo, u.nombre_completo as tecnico_nombre FROM sesiones_remotas sr LEFT JOIN tickets t ON sr.ticket_id = t.id LEFT JOIN usuarios u ON sr.tecnico_id = u.id ORDER BY sr.created_at DESC LIMIT 100");
            Response::success($stmt->fetchAll());
        }),
        $s0 === 'soporte-remoto' && $method === 'POST' && $accion === 'iniciar' => call_user_func(function() {
            $payload = AuthMiddleware::verificar();
            $body = json_decode(file_get_contents('php://input'), true);
            $db = getDB();
            $ticketId = $body['ticket_id'] ?? null;
            $codigo = $body['codigo_acceso'] ?? '';
            $metodo = $body['metodo'] ?? 'quick_assist';
            
            $ticket = null;
            if ($ticketId) {
                $t = $db->prepare("SELECT t.*, s.id as sol_id, s.email as sol_email, s.nombre_completo as sol_nombre FROM tickets t JOIN usuarios s ON t.solicitante_id = s.id WHERE t.id = ?");
                $t->execute([$ticketId]);
                $ticket = $t->fetch();
            }
            
            $stmt = $db->prepare("INSERT INTO sesiones_remotas (ticket_id, tecnico_id, codigo_acceso, metodo, estado) VALUES (?, ?, ?, ?, ?)");
            $stmt->execute([$ticketId, $payload['sub'], $codigo, $metodo, $codigo ? 'enviada' : 'pendiente']);
            $sesionId = $db->lastInsertId();
            
            // Send email with code
            if ($ticket && $codigo && $ticket['sol_email'] && class_exists('EmailService')) {
                $tec = $db->prepare("SELECT nombre_completo FROM usuarios WHERE id = ?");
                $tec->execute([$payload['sub']]);
                $tecNombre = $tec->fetchColumn() ?: 'Soporte Técnico';
                
                $variables = [
                    'numero_ticket' => $ticket['numero_ticket'],
                    'titulo' => $ticket['titulo'],
                    'nombre_usuario' => $ticket['sol_nombre'],
                    'email_usuario' => $ticket['sol_email'],
                    'nombre_tecnico' => $tecNombre,
                    'codigo_acceso' => $codigo,
                    'url_ticket' => "https://ana.evolucionamos.com/app/portal/ticket-detalle.html?id={$ticketId}",
                ];
                $rendered = EmailService::renderPlantilla('soporte_remoto', $variables, $variables['url_ticket'], 'Ver Ticket');
                if ($rendered) {
                    EmailService::enviar($ticket['sol_email'], $rendered['asunto'], $rendered['html']);
                }
            }
            
            Response::created(['id' => $sesionId, 'message' => $ticket && $codigo ? 'Código enviado a ' . $ticket['sol_email'] : 'Sesión creada']);
        }),
        $s0 === 'soporte-remoto' && $method === 'PUT' && $id => call_user_func(function() use ($id) {
            $payload = AuthMiddleware::verificar();
            $body = json_decode(file_get_contents('php://input'), true);
            $db = getDB();
            $fields = []; $params = [];
            foreach (['codigo_acceso','estado','metodo','notas','duracion_minutos'] as $f) {
                if (array_key_exists($f, $body)) { $fields[] = "$f = ?"; $params[] = $body[$f]; }
            }
            if (isset($body['estado']) && $body['estado'] === 'conectada') { $fields[] = "connected_at = NOW()"; }
            if (isset($body['estado']) && $body['estado'] === 'finalizada') {
                $fields[] = "finished_at = NOW()";
                if (empty($body['duracion_minutos'])) {
                    $fields[] = "duracion_minutos = GREATEST(1, TIMESTAMPDIFF(MINUTE, created_at, NOW()))";
                }
            }
            if (empty($fields)) Response::error('Nada que actualizar');
            $params[] = $id;
            $db->prepare("UPDATE sesiones_remotas SET " . implode(',', $fields) . " WHERE id = ?")->execute($params);
            
            // If finalizing, add comment to ticket
            if (isset($body['estado']) && $body['estado'] === 'finalizada') {
                $stmt = $db->prepare("SELECT sr.*, t.numero_ticket FROM sesiones_remotas sr LEFT JOIN tickets t ON sr.ticket_id = t.id WHERE sr.id = ?");
                $stmt->execute([$id]);
                $sesion = $stmt->fetch();
                if ($sesion && $sesion['ticket_id']) {
                    $metodoLabel = ($sesion['metodo'] ?? '') === 'anydesk' ? 'AnyDesk' : 'Quick Assist';
                    $duracion = $sesion['duracion_minutos'] ?: 1;
                    $notas = $sesion['notas'] ?? '';
                    
                    $comentario = "🖥️ Sesión de soporte remoto finalizada\n";
                    $comentario .= "• Método: {$metodoLabel}\n";
                    $comentario .= "• Duración: {$duracion} minutos\n";
                    if ($notas) $comentario .= "• Detalle: {$notas}\n";
                    
                    $db->prepare("INSERT INTO ticket_comentarios (ticket_id, autor_id, comentario, es_interno) VALUES (?,?,?,0)")
                        ->execute([$sesion['ticket_id'], $payload['sub'], $comentario]);
                    $db->prepare("UPDATE tickets SET updated_at = NOW() WHERE id = ?")->execute([$sesion['ticket_id']]);
                    
                    // Send email notification
                    if (class_exists('EmailService')) {
                        try {
                            $tData = $db->prepare("SELECT t.*, s.email as sol_email, s.nombre_completo as sol_nombre, tec.email as tec_email FROM tickets t LEFT JOIN usuarios s ON t.solicitante_id = s.id LEFT JOIN usuarios tec ON t.tecnico_asignado_id = tec.id WHERE t.id = ?");
                            $tData->execute([$sesion['ticket_id']]);
                            $ticketData = $tData->fetch();
                            $autorStmt = $db->prepare("SELECT nombre_completo FROM usuarios WHERE id = ?");
                            $autorStmt->execute([$payload['sub']]);
                            $autorNombre = $autorStmt->fetchColumn() ?: 'Soporte Técnico';
                            if ($ticketData) {
                                // Notify solicitante
                                if ($ticketData['sol_email']) {
                                    EmailService::notificarComentario($ticketData, $ticketData['sol_email'], $autorNombre, $comentario, $ticketData['sol_nombre'] ?? '');
                                }
                            }
                        } catch(\Exception $e) {}
                    }
                }
            }
            
            Response::success(['message' => 'Sesión actualizada']);
        }),
        // Stats de soporte remoto
        $s0 === 'soporte-remoto' && $method === 'GET' && $accion === 'stats' => call_user_func(function() {
            $payload = AuthMiddleware::verificar();
            $db = getDB();
            
            // General stats
            $total = $db->query("SELECT COUNT(*) FROM sesiones_remotas")->fetchColumn();
            $finalizadas = $db->query("SELECT COUNT(*) FROM sesiones_remotas WHERE estado='finalizada'")->fetchColumn();
            $duracionProm = $db->query("SELECT ROUND(AVG(duracion_minutos),1) FROM sesiones_remotas WHERE estado='finalizada' AND duracion_minutos > 0")->fetchColumn();
            $totalMinutos = $db->query("SELECT COALESCE(SUM(duracion_minutos),0) FROM sesiones_remotas WHERE estado='finalizada'")->fetchColumn();
            
            // By method
            $porMetodo = $db->query("SELECT metodo, COUNT(*) as total, ROUND(AVG(duracion_minutos),1) as promedio_min FROM sesiones_remotas WHERE estado='finalizada' GROUP BY metodo")->fetchAll();
            
            // By technician
            $porTecnico = $db->query("SELECT u.nombre_completo as tecnico, COUNT(*) as sesiones, ROUND(AVG(sr.duracion_minutos),1) as promedio_min, SUM(sr.duracion_minutos) as total_min FROM sesiones_remotas sr JOIN usuarios u ON sr.tecnico_id = u.id WHERE sr.estado='finalizada' GROUP BY sr.tecnico_id ORDER BY sesiones DESC")->fetchAll();
            
            // By category (through ticket)
            $porCategoria = $db->query("SELECT c.nombre as categoria, COUNT(*) as sesiones, ROUND(AVG(sr.duracion_minutos),1) as promedio_min FROM sesiones_remotas sr JOIN tickets t ON sr.ticket_id = t.id LEFT JOIN categorias c ON t.categoria_id = c.id WHERE sr.estado='finalizada' GROUP BY t.categoria_id ORDER BY sesiones DESC")->fetchAll();
            
            // Last 30 days trend
            $tendencia = $db->query("SELECT DATE(created_at) as fecha, COUNT(*) as sesiones, ROUND(AVG(duracion_minutos),1) as promedio_min FROM sesiones_remotas WHERE estado='finalizada' AND created_at >= DATE_SUB(NOW(), INTERVAL 30 DAY) GROUP BY DATE(created_at) ORDER BY fecha")->fetchAll();
            
            Response::success([
                'total_sesiones' => (int)$total,
                'finalizadas' => (int)$finalizadas,
                'duracion_promedio' => $duracionProm ? (float)$duracionProm : 0,
                'total_minutos' => (int)$totalMinutos,
                'total_horas' => round($totalMinutos / 60, 1),
                'por_metodo' => $porMetodo,
                'por_tecnico' => $porTecnico,
                'por_categoria' => $porCategoria,
                'tendencia_30d' => $tendencia
            ]);
        }),

        // ========== EMAIL PLANTILLAS ==========
        $s0 === 'email-plantillas' && $method === 'GET' && !$accion => call_user_func(function() {
            $payload = AuthMiddleware::verificar();
            try {
                $db = getDB();
                $stmt = $db->query("SELECT id, codigo, nombre, descripcion, asunto, contenido, variables_disponibles, activo FROM email_plantillas ORDER BY id");
                Response::success($stmt->fetchAll());
            } catch(\Exception $e) {
                Response::error('Tabla email_plantillas no existe. Ejecuta el SQL migracion-email-plantillas.sql primero. Error: ' . $e->getMessage());
            }
        }),
        $s0 === 'email-plantillas' && $method === 'GET' && $accion => call_user_func(function() use ($accion) {
            $payload = AuthMiddleware::verificar();
            $db = getDB();
            if (ctype_digit($accion)) {
                $stmt = $db->prepare("SELECT * FROM email_plantillas WHERE id = ?");
                $stmt->execute([(int)$accion]);
            } else {
                $stmt = $db->prepare("SELECT * FROM email_plantillas WHERE codigo = ?");
                $stmt->execute([$accion]);
            }
            $row = $stmt->fetch();
            $row ? Response::success($row) : Response::notFound('Plantilla no encontrada');
        }),
        $s0 === 'email-plantillas' && $method === 'PUT' && $accion => call_user_func(function() use ($accion) {
            $payload = AuthMiddleware::verificar();
            $body = json_decode(file_get_contents('php://input'), true);
            $db = getDB();
            $fields = []; $params = [];
            foreach (['asunto','contenido','activo'] as $f) {
                if (array_key_exists($f, $body)) { $fields[] = "$f = ?"; $params[] = $body[$f]; }
            }
            if (empty($fields)) Response::error('Sin cambios');
            if (ctype_digit($accion)) {
                $params[] = (int)$accion;
                $db->prepare("UPDATE email_plantillas SET " . implode(',', $fields) . " WHERE id = ?")->execute($params);
            } else {
                $params[] = $accion;
                $db->prepare("UPDATE email_plantillas SET " . implode(',', $fields) . " WHERE codigo = ?")->execute($params);
            }
            Response::success(['message' => 'Plantilla actualizada']);
        }),
        $s0 === 'email-plantillas' && $method === 'POST' && $accion === 'preview' => call_user_func(function() {
            $payload = AuthMiddleware::verificar();
            $body = json_decode(file_get_contents('php://input'), true);
            $contenido = $body['contenido'] ?? '';
            $asunto = $body['asunto'] ?? 'Preview';
            // Replace variables with sample data
            $samples = [
                'numero_ticket'=>'TK2502001','titulo'=>'Mi impresora no funciona','descripcion'=>'La impresora del piso 3 no imprime desde ayer',
                'prioridad'=>'Alta','estado'=>'Abierto','nombre_usuario'=>'Carlos Pérez','email_usuario'=>'carlos@empresa.com',
                'nombre_tecnico'=>'Ana Gómez','email_tecnico'=>'ana@empresa.com','categoria'=>'Hardware','subcategoria'=>'Impresoras',
                'cambios_detalle'=>'• Estado: En Progreso<br>• Técnico asignado: Ana Gómez<br>• Prioridad: Alta',
                'fecha_creacion'=>date('d/m/Y H:i'),'fecha_actualizacion'=>date('d/m/Y H:i'),'fecha_cierre'=>date('d/m/Y H:i'),
                'fecha_comentario'=>date('d/m/Y H:i'),'sla_texto'=>'8 horas hábiles (~1 día hábil)','sla_horas'=>'8',
                'horario_atencion'=>'Lun a Vie de 08:00 a 18:00','sla_mensaje'=>'Nuestro equipo atenderá tu solicitud en horario hábil.',
                'url_ticket'=>'https://ana.evolucionamos.com/app/portal/ticket-detalle.html?id=1',
                'nombre_empresa'=>'Nexus IT','nombre_destinatario'=>'Carlos Pérez','nombre_autor'=>'Ana Gómez',
                'comentario'=>'Ya revisé la impresora, necesita un cambio de tóner. Lo programo para mañana.'
            ];
            foreach ($samples as $k => $v) {
                $contenido = str_replace("{{{$k}}}", $v, $contenido);
                $asunto = str_replace("{{{$k}}}", $v, $asunto);
            }
            $html = EmailService::template($asunto, $contenido, '#', 'Ver Ticket');
            Response::success(['html' => $html, 'asunto' => $asunto]);
        }),

        // ---- TICKET TEMPLATES ----
        $s0 === 'ticket-templates' && !$id && !$accion && $method === 'GET'  => TicketTemplateController::index(),
        $s0 === 'ticket-templates' && $id  && !$accion && $method === 'GET'  => TicketTemplateController::ver($id),
        $s0 === 'ticket-templates' && !$id && !$accion && $method === 'POST' => TicketTemplateController::crear(),
        $s0 === 'ticket-templates' && $id  && !$accion && $method === 'PUT'  => TicketTemplateController::actualizar($id),
        $s0 === 'ticket-templates' && $id  && !$accion && $method === 'DELETE' => TicketTemplateController::eliminar($id),

        // ---- LICENCIAS DE SOFTWARE ----
        $s0 === 'licencias' && !$id && !$accion && $method === 'GET'  => LicenciaController::index(),
        $s0 === 'licencias' && $id  && !$accion && $method === 'GET'  => LicenciaController::ver($id),
        $s0 === 'licencias' && !$id && !$accion && $method === 'POST' => LicenciaController::crear(),
        $s0 === 'licencias' && $id  && !$accion && $method === 'PUT'  => LicenciaController::actualizar($id),
        $s0 === 'licencias' && $id  && $accion === 'asignar' && $method === 'POST' => LicenciaController::asignar($id),
        $s0 === 'licencias' && $id  && $accion === 'desasignar' && $method === 'POST' => LicenciaController::desasignar($id),

        // ---- CONTRATOS DE SOPORTE ----
        $s0 === 'contratos' && !$id && !$accion && $method === 'GET'  => ContratoController::index(),
        $s0 === 'contratos' && $id  && !$accion && $method === 'GET'  => ContratoController::ver($id),
        $s0 === 'contratos' && !$id && !$accion && $method === 'POST' => ContratoController::crear(),
        $s0 === 'contratos' && $id  && !$accion && $method === 'PUT'  => ContratoController::actualizar($id),
        $s0 === 'contratos' && $id  && $accion === 'activos' && $method === 'POST'   => ContratoController::vincularActivo($id),
        $s0 === 'contratos' && $id  && $accion === 'activos' && $method === 'DELETE' => ContratoController::desvincularActivo($id),

        // ---- IMPORTACIÓN MASIVA ----
        $s0 === 'importar' && $accion === 'plantilla-usuarios' && $method === 'GET'  => ImportController::plantillaUsuarios(),
        $s0 === 'importar' && $accion === 'usuarios'           && $method === 'POST' => ImportController::importarUsuarios(),
        $s0 === 'importar' && $accion === 'plantilla-activos'  && $method === 'GET'  => ImportController::plantillaActivos(),

        // ---- SUPERVISOR / DASHBOARD EJECUTIVO ----
        $s0 === 'supervisor' && $accion === 'resumen'              && $method === 'GET' => SupervisorController::resumen(),
        $s0 === 'supervisor' && $accion === 'carga-tecnicos'       && $method === 'GET' => SupervisorController::cargaTecnicos(),
        $s0 === 'supervisor' && $accion === 'usuarios-departamento' && $method === 'GET' => SupervisorController::usuariosDepartamento(),
        $s0 === 'supervisor' && $accion === 'sla-tendencia'        && $method === 'GET' => SupervisorController::slaTendencia(),
        $s0 === 'supervisor' && $accion === 'top-solicitantes'     && $method === 'GET' => SupervisorController::topSolicitantes(),

        // ---- ENCUESTAS DE SATISFACCIÓN ----
        $s0 === 'encuestas' && !$id && $accion === 'ver'      && $method === 'GET'  => EncuestaController::verPorToken(),
        $s0 === 'encuestas' && !$id && $accion === 'responder' && $method === 'POST' => EncuestaController::responder(),
        $s0 === 'encuestas' && !$id && $accion === 'rapida'   && $method === 'GET'  => EncuestaController::calificacionRapida(),
        $s0 === 'encuestas' && !$id && $accion === 'resumen'  && $method === 'GET'  => EncuestaController::resumen(),

        // ---- CREDENCIALES (Bóveda de accesos) ----
        $s0 === 'credenciales' && !$id && $accion === 'verificar' && $method === 'POST' => CredencialController::verificarAcceso(),
        $s0 === 'credenciales' && !$id && !$accion && $method === 'GET'  => CredencialController::index(),
        $s0 === 'credenciales' && !$id && !$accion && $method === 'POST' => CredencialController::crear(),
        $s0 === 'credenciales' && $id  && !$accion && $method === 'PUT'  => CredencialController::actualizar($id),
        $s0 === 'credenciales' && $id  && !$accion && $method === 'DELETE' => CredencialController::eliminar($id),
        $s0 === 'credenciales' && $id  && $accion === 'password' && $method === 'GET' => CredencialController::verPassword($id),

        default => Response::notFound("Ruta no encontrada: $method /$path")
    };
    
} catch (Throwable $e) {
    if (APP_DEBUG) {
        Response::error($e->getMessage(), 500);
    } else {
        Response::serverError();
    }
}