<?php
class TicketController {
    public static function index(): void {
        $payload = AuthMiddleware::verificar();
        $db = getDB();
        $page = max(1, (int)($_GET['page'] ?? 1));
        $perPage = min(100, max(1, (int)($_GET['per_page'] ?? 15)));
        $offset = ($page - 1) * $perPage;

        $where = ["1=1"]; $params = [];
        if (!empty($_GET['estado'])) { $where[] = "t.estado = ?"; $params[] = $_GET['estado']; }
        if (!empty($_GET['prioridad'])) { $where[] = "t.prioridad = ?"; $params[] = $_GET['prioridad']; }
        if (!empty($_GET['buscar'])) {
            $s = '%'.$_GET['buscar'].'%';
            $where[] = "(t.titulo LIKE ? OR t.numero_ticket LIKE ? OR t.descripcion LIKE ?)";
            $params = array_merge($params, [$s,$s,$s]);
        }
        // Visibilidad por rol: usuario=sus tickets, tecnico=asignados, supervisor/admin=todos
        if ($payload['rol'] === 'usuario') {
            $where[] = "t.solicitante_id = ?";
            $params[] = $payload['sub'];
        } elseif ($payload['rol'] === 'tecnico') {
            $where[] = "t.tecnico_asignado_id = ?";
            $params[] = $payload['sub'];
        }

        $whereStr = implode(' AND ', $where);
        $order = ($_GET['orden'] ?? 'reciente') === 'antiguo' ? 'ASC' : 'DESC';

        $countStmt = $db->prepare("SELECT COUNT(*) FROM tickets t WHERE $whereStr");
        $countStmt->execute($params);
        $total = (int)$countStmt->fetchColumn();

        $sql = "SELECT t.*, s.nombre_completo as solicitante_nombre,
                tec.nombre_completo as tecnico_nombre,
                c.nombre as categoria_nombre, c.color as categoria_color
            FROM tickets t
            LEFT JOIN usuarios s ON t.solicitante_id = s.id
            LEFT JOIN usuarios tec ON t.tecnico_asignado_id = tec.id
            LEFT JOIN categorias c ON t.categoria_id = c.id
            WHERE $whereStr ORDER BY t.created_at $order LIMIT $perPage OFFSET $offset";
        $stmt = $db->prepare($sql);
        $stmt->execute($params);
        Response::paginated($stmt->fetchAll(), $total, $page, $perPage);
    }

    public static function crear(): void {
        $payload = AuthMiddleware::verificar();
        $body = Validator::getBody();
        Validator::make($body)->required('titulo')->validate();
        $db = getDB();

        $year = date('y'); $month = date('m');
        $seq = (int)$db->query("SELECT COUNT(*) FROM tickets WHERE YEAR(created_at)=YEAR(NOW()) AND MONTH(created_at)=MONTH(NOW())")->fetchColumn() + 1;
        $numero = 'TK' . $year . $month . str_pad($seq, 4, '0', STR_PAD_LEFT);

        // Calculate SLA in business hours
        $prioridad = $body['prioridad'] ?? 'media';
        $categoriaId = !empty($body['categoria_id']) ? (int)$body['categoria_id'] : null;
        $slaHoras = 24;

        if (class_exists('SLACalculator')) {
            // Pasar categoría para que tome el menor SLA entre prioridad y categoría
            $slaHoras = SLACalculator::getHorasSLA($prioridad, $categoriaId);
            $fechaVenc = SLACalculator::calcularVencimiento($slaHoras);
        } else {
            // Fallback: horas calendario (sin horas hábiles)
            try {
                $slaStmt = $db->prepare("SELECT valor FROM configuracion WHERE clave = ?");
                $slaStmt->execute(["sla_$prioridad"]);
                $slaVal = $slaStmt->fetchColumn();
                if ($slaVal) $slaHoras = (int)$slaVal;
            } catch (\Exception $e) {}
            if ($categoriaId && !$slaVal) {
                $cat = $db->prepare("SELECT sla_horas FROM categorias WHERE id = ?");
                $cat->execute([$categoriaId]);
                $row = $cat->fetch();
                if ($row && (int)$row['sla_horas'] > 0) $slaHoras = (int)$row['sla_horas'];
            }
            $fechaVenc = date('Y-m-d H:i:s', strtotime("+{$slaHoras} hours"));
        }

        // ===== AUTO-ASIGNACIÓN =====
        $tecnicoId = $body['tecnico_asignado_id'] ?? null;
        $estado = 'abierto';

        if (!$tecnicoId) {
            // Verificar si autoasignar está habilitado
            try {
                $autoStmt = $db->prepare("SELECT valor FROM configuracion WHERE clave = 'ticket_autoasignar'");
                $autoStmt->execute();
                $autoAsignar = $autoStmt->fetchColumn();
            } catch (\Exception $e) { $autoAsignar = 'false'; }

            if ($autoAsignar === 'true' || $autoAsignar === '1') {
                // Buscar técnicos activos primero
                $tecnicos = $db->query("SELECT u.id, u.nombre_completo,
                    (SELECT COUNT(*) FROM tickets t WHERE t.tecnico_asignado_id = u.id AND t.estado IN ('abierto','en_progreso')) as tickets_abiertos
                    FROM usuarios u 
                    WHERE u.activo = 1 AND u.rol = 'tecnico'
                    ORDER BY tickets_abiertos ASC, u.id ASC")->fetchAll();

                // Fallback: si no hay técnicos, buscar admins
                if (count($tecnicos) === 0) {
                    $tecnicos = $db->query("SELECT u.id, u.nombre_completo,
                        (SELECT COUNT(*) FROM tickets t WHERE t.tecnico_asignado_id = u.id AND t.estado IN ('abierto','en_progreso')) as tickets_abiertos
                        FROM usuarios u 
                        WHERE u.activo = 1 AND u.rol = 'admin'
                        ORDER BY tickets_abiertos ASC, u.id ASC")->fetchAll();
                }

                if (count($tecnicos) >= 1) {
                    $tecnicoId = (int)$tecnicos[0]['id'];
                }
            }
        }

        // Si hay técnico asignado (manual o auto), estado = en_progreso
        if ($tecnicoId) {
            $estado = 'en_progreso';
        }

        // INSERT con sla_horas
        $stmt = $db->prepare("INSERT INTO tickets (numero_ticket, titulo, descripcion, solicitante_id, categoria_id, subcategoria_id, activo_id, prioridad, estado, tecnico_asignado_id, fecha_vencimiento, sla_horas) VALUES (?,?,?,?,?,?,?,?,?,?,?,?)");
        $stmt->execute([
            $numero, $body['titulo'], $body['descripcion'] ?? null,
            $body['solicitante_id'] ?? $payload['sub'],
            $body['categoria_id'] ?? null, $body['subcategoria_id'] ?? null,
            $body['activo_id'] ?? null,
            $body['prioridad'] ?? 'media', $estado,
            $tecnicoId, $fechaVenc, $slaHoras
        ]);
        $id = (int)$db->lastInsertId();

        // Log
        if (class_exists('AuditService')) AuditService::ticketCreado($id, ['titulo' => $body['titulo'], 'prioridad' => $body['prioridad'] ?? 'media']);
        try { $db->prepare("INSERT INTO log_actividad (usuario_id, accion, entidad_tipo, entidad_id, ip_address) VALUES (?,?,?,?,?)")
            ->execute([$payload['sub'], 'crear_ticket', 'tickets', $id, $_SERVER['REMOTE_ADDR'] ?? null]); } catch(\Exception $e) {}
        // Notification
        try { $db->prepare("INSERT INTO notificaciones (usuario_id, titulo, mensaje, tipo, url_accion, referencia_tipo, referencia_id) VALUES (?,?,?,?,?,?,?)")
            ->execute([$payload['sub'], "Nuevo ticket: $numero", "Se ha creado el ticket '{$body['titulo']}'", 'ticket', "/app/admin/ticket-detalle.html?id=$id", 'ticket', $id]); } catch(\Exception $e) {}
        // In-app notification for assigned technician
        $notifyTecId = $tecnicoId ?: ($body['tecnico_asignado_id'] ?? null);
        if ($notifyTecId && $notifyTecId != $payload['sub']) {
            try { $db->prepare("INSERT INTO notificaciones (usuario_id, titulo, mensaje, tipo, url_accion, referencia_tipo, referencia_id) VALUES (?,?,?,?,?,?,?)")
                ->execute([$notifyTecId, "Ticket asignado: $numero", "Se te asignó el ticket: '{$body['titulo']}'", 'ticket', "/app/admin/ticket-detalle.html?id=$id", 'ticket', $id]); } catch(\Exception $e) {}
        }
        // Email notification
        if (class_exists('EmailService')) {
            try {
                $ticketData = ['id' => $id, 'numero_ticket' => $numero, 'titulo' => $body['titulo'], 'prioridad' => $body['prioridad'] ?? 'media', 'estado' => $estado];
                // Notify assigned technician (manual or auto-assigned)
                $notifyTecId = $tecnicoId ?: ($body['tecnico_asignado_id'] ?? null);
                if (!empty($notifyTecId)) {
                    $tec = $db->prepare("SELECT email, nombre_completo FROM usuarios WHERE id = ?");
                    $tec->execute([$notifyTecId]);
                    $tecData = $tec->fetch();
                    if ($tecData) EmailService::notificarAsignacion($ticketData, $tecData['email'], $tecData['nombre_completo']);
                }
                // Notify solicitante
                $sol = $db->prepare("SELECT email, nombre_completo FROM usuarios WHERE id = ?");
                $sol->execute([$body['solicitante_id'] ?? $payload['sub']]);
                $solData = $sol->fetch();
                if ($solData) EmailService::notificarNuevoTicket($ticketData, $solData['email'], $solData['nombre_completo']);
            } catch(\Exception $e) { error_log("Email error: " . $e->getMessage()); }
        }

        Response::created(['id' => $id, 'numero_ticket' => $numero]);
    }

    public static function ver(int $id): void {
        $payload = AuthMiddleware::verificar();
        $db = getDB();
        $stmt = $db->prepare("SELECT t.*, s.nombre_completo as solicitante_nombre, s.email as solicitante_email,
            tec.nombre_completo as tecnico_nombre, c.nombre as categoria_nombre, c.color as categoria_color,
            sc.nombre as subcategoria_nombre,
            a.codigo_activo, a.marca as activo_marca, a.modelo as activo_modelo,
            s.departamento as solicitante_departamento
            FROM tickets t
            LEFT JOIN usuarios s ON t.solicitante_id = s.id
            LEFT JOIN usuarios tec ON t.tecnico_asignado_id = tec.id
            LEFT JOIN categorias c ON t.categoria_id = c.id
            LEFT JOIN subcategorias sc ON t.subcategoria_id = sc.id
            LEFT JOIN activos a ON t.activo_id = a.id
            WHERE t.id = ?");
        $stmt->execute([$id]);
        $ticket = $stmt->fetch();
        if (!$ticket) Response::notFound('Ticket no encontrado');

        $comStmt = $db->prepare("SELECT tc.*, u.nombre_completo as autor_nombre, u.rol as autor_rol FROM ticket_comentarios tc LEFT JOIN usuarios u ON tc.autor_id = u.id WHERE tc.ticket_id = ? ORDER BY tc.created_at ASC");
        $comStmt->execute([$id]);
        $ticket['comentarios'] = $comStmt->fetchAll();

        $adjStmt = $db->prepare("SELECT * FROM ticket_adjuntos WHERE ticket_id = ? ORDER BY created_at DESC");
        $adjStmt->execute([$id]);
        $ticket['adjuntos'] = $adjStmt->fetchAll();

        Response::success($ticket);
    }

    public static function actualizar(int $id): void {
        $payload = AuthMiddleware::verificar();
        $body = Validator::getBody();
        $db = getDB();
        $fields = []; $params = [];
        $allowed = ['titulo','descripcion','estado','prioridad','tecnico_asignado_id','categoria_id','subcategoria_id'];
        foreach ($allowed as $f) {
            if (array_key_exists($f, $body) && $f !== 'notificar') { $fields[] = "$f = ?"; $params[] = $body[$f]; }
        }
        if (empty($fields)) Response::error('Nada que actualizar');

        if (isset($body['estado'])) {
            if ($body['estado'] === 'resuelto') { $fields[] = "resuelto_at = NOW()"; }
            if ($body['estado'] === 'cerrado') { $fields[] = "cerrado_at = NOW()"; }
        }

        // Recalcular SLA si cambia prioridad o categoría
        if ((isset($body['prioridad']) || isset($body['categoria_id'])) && class_exists('SLACalculator')) {
            $current = $db->prepare("SELECT prioridad, categoria_id, created_at FROM tickets WHERE id = ?")->fetch() ?: [];
            if ($current) {
                $newPrioridad = $body['prioridad'] ?? $current['prioridad'];
                $newCatId = isset($body['categoria_id']) ? (int)$body['categoria_id'] : ($current['categoria_id'] ? (int)$current['categoria_id'] : null);
                $newSlaHoras = SLACalculator::getHorasSLA($newPrioridad, $newCatId);
                $newFechaVenc = SLACalculator::calcularVencimiento($newSlaHoras, $current['created_at']);
                $fields[] = "sla_horas = ?"; $params[] = $newSlaHoras;
                $fields[] = "fecha_vencimiento = ?"; $params[] = $newFechaVenc;
            }
        }

        // Auto-cambiar a en_progreso cuando se asigna técnico y el ticket está abierto
        if (isset($body['tecnico_asignado_id']) && $body['tecnico_asignado_id']) {
            $estadoActual = $db->prepare("SELECT estado FROM tickets WHERE id = ?")->fetch();
            if ($estadoActual && $estadoActual['estado'] === 'abierto') {
                // Solo cambiar si no se está enviando otro estado explícito
                if (!isset($body['estado'])) {
                    $fields[] = "estado = ?"; $params[] = 'en_progreso';
                }
            }
        }

        $fields[] = "updated_at = NOW()";
        $params[] = $id;
        $db->prepare("UPDATE tickets SET " . implode(', ', $fields) . " WHERE id = ?")->execute($params);

        // Email on state change or assignment
        if (class_exists('EmailService')) {
            try {
                $t = $db->prepare("SELECT t.*, s.email as sol_email, s.nombre_completo as sol_nombre, tec.email as tec_email, tec.nombre_completo as tec_nombre, c.nombre as cat_nombre FROM tickets t LEFT JOIN usuarios s ON t.solicitante_id = s.id LEFT JOIN usuarios tec ON t.tecnico_asignado_id = tec.id LEFT JOIN categorias c ON t.categoria_id = c.id WHERE t.id = ?");
                $t->execute([$id]);
                $tData = $t->fetch();
                if ($tData && ($body['notificar'] ?? false)) {
                    // Build changes summary
                    $cambios = [];
                    if (isset($body['estado'])) $cambios[] = "Estado: " . ucfirst(str_replace('_', ' ', $body['estado']));
                    if (isset($body['prioridad'])) $cambios[] = "Prioridad: " . ucfirst($body['prioridad']);
                    if (isset($body['tecnico_asignado_id']) && $tData['tec_nombre']) $cambios[] = "Técnico asignado: " . $tData['tec_nombre'];
                    if (isset($body['categoria_id']) && $tData['cat_nombre']) $cambios[] = "Categoría: " . $tData['cat_nombre'];
                    
                    $cambiosHtml = implode('<br>', array_map(fn($c) => "• $c", $cambios));
                    $baseVars = [
                        'numero_ticket' => $tData['numero_ticket'], 'titulo' => $tData['titulo'],
                        'prioridad' => ucfirst($body['prioridad'] ?? $tData['prioridad'] ?? 'media'),
                        'estado' => ucfirst(str_replace('_', ' ', $body['estado'] ?? $tData['estado'] ?? '')),
                        'nombre_tecnico' => $tData['tec_nombre'] ?? '', 'categoria' => $tData['cat_nombre'] ?? '',
                        'cambios_detalle' => $cambiosHtml, 'fecha_actualizacion' => date('d/m/Y H:i'),
                    ];

                    $esResuelto = ($body['estado'] ?? '') === 'resuelto';
                    $esCerrado = ($body['estado'] ?? '') === 'cerrado';

                    // Notify solicitante
                    if ($tData['sol_email']) {
                        $urlSol = "https://ana.evolucionamos.com/app/portal/ticket-detalle.html?id={$id}";

                        if ($esCerrado) {
                            // CERRADO → Enviar encuesta de satisfacción
                            error_log("EMAIL_DEBUG: Ticket $id CERRADO. Enviando encuesta a {$tData['sol_email']}");
                            $encuestaToken = null;
                            try {
                                if (class_exists('EncuestaController')) {
                                    $encuestaToken = EncuestaController::crear((int)$id, (int)$tData['solicitante_id']);
                                }
                            } catch (\Exception $encEx) {
                                error_log("EMAIL_DEBUG: Error creando encuesta: " . $encEx->getMessage());
                            }
                            $urlEncuesta = "https://ana.evolucionamos.com/api/v1/encuestas/rapida?token={$encuestaToken}";
                            $diasReabrir = 1;
                            try {
                                $drStmt = $db->prepare("SELECT valor FROM configuracion WHERE clave = 'ticket_reabrir_dias'");
                                $drStmt->execute();
                                $drVal = $drStmt->fetchColumn();
                                if ($drVal) $diasReabrir = (int)$drVal;
                            } catch (\Exception $e) {}

                            $vars = array_merge($baseVars, [
                                'nombre_usuario' => $tData['sol_nombre'] ?? '', 'url_ticket' => $urlSol,
                                'url_encuesta' => $urlEncuesta, 'dias_reabrir' => $diasReabrir,
                                'fecha_cierre' => date('d/m/Y H:i'),
                            ]);
                            $rendered = EmailService::renderPlantilla('encuesta_satisfaccion', $vars, $urlSol, 'Ver Ticket');
                            if (!$rendered) {
                                $rendered = EmailService::renderPlantilla('ticket_cerrado', $vars, $urlSol, 'Ver Ticket');
                            }
                            if ($rendered) {
                                EmailService::enviar($tData['sol_email'], $rendered['asunto'], $rendered['html']);
                                error_log("EMAIL_DEBUG: Encuesta enviada a {$tData['sol_email']}");
                            }

                        } elseif ($esResuelto) {
                            // RESUELTO → Enviar plantilla ticket_cerrado (notificación de resolución)
                            error_log("EMAIL_DEBUG: Ticket $id RESUELTO. Enviando notificación a {$tData['sol_email']}");
                            $vars = array_merge($baseVars, [
                                'nombre_usuario' => $tData['sol_nombre'] ?? '', 'url_ticket' => $urlSol,
                                'fecha_cierre' => date('d/m/Y H:i'),
                            ]);
                            $rendered = EmailService::renderPlantilla('ticket_cerrado', $vars, $urlSol, 'Ver Ticket');
                            if (!$rendered) {
                                $rendered = EmailService::renderPlantilla('ticket_actualizado', $vars, $urlSol, 'Ver Ticket');
                            }
                            if ($rendered) {
                                EmailService::enviar($tData['sol_email'], $rendered['asunto'], $rendered['html']);
                                error_log("EMAIL_DEBUG: Notificación resuelto enviada a {$tData['sol_email']}");
                            }

                        } else {
                            $tplCodigo = 'ticket_actualizado';
                            $urlSol = "https://ana.evolucionamos.com/app/portal/ticket-detalle.html?id={$id}";
                            $vars = array_merge($baseVars, [
                                'nombre_usuario' => $tData['sol_nombre'] ?? '', 'url_ticket' => $urlSol,
                                'fecha_cierre' => date('d/m/Y H:i'),
                            ]);
                            $rendered = EmailService::renderPlantilla($tplCodigo, $vars, $urlSol, 'Ver Ticket');
                            if ($rendered) {
                                EmailService::enviar($tData['sol_email'], $rendered['asunto'], $rendered['html']);
                            }
                        }
                    }
                    
                    // Notify tecnico asignado
                    if ($tData['tec_email'] && $tData['tec_email'] !== $tData['sol_email']) {
                        $urlTec = "https://ana.evolucionamos.com/app/admin/ticket-detalle.html?id={$id}";
                        $vars = array_merge($baseVars, ['nombre_tecnico' => $tData['tec_nombre'] ?? '', 'url_ticket' => $urlTec]);
                        $rendered = EmailService::renderPlantilla('ticket_actualizado_tecnico', $vars, $urlTec, 'Ver Ticket');
                        if ($rendered) {
                            EmailService::enviar($tData['tec_email'], $rendered['asunto'], $rendered['html']);
                        }
                    }
                } else if ($tData) {
                    if (isset($body['estado']) && $tData['sol_email']) {
                        EmailService::notificarCambioEstado($tData, $tData['sol_email'], $body['estado']);
                    }
                    if (isset($body['tecnico_asignado_id']) && $tData['tec_email']) {
                        EmailService::notificarAsignacion($tData, $tData['tec_email'], $tData['tec_nombre']);
                    }
                }
            } catch(\Exception $e) {
                error_log("EmailService error en actualizar ticket $id: " . $e->getMessage());
            }
        }
        if (class_exists('AuditService')) AuditService::ticketActualizado($id, [], $body);
        Response::success(null, 'Ticket actualizado');
    }

    public static function agregarComentario(int $id): void {
        $payload = AuthMiddleware::verificar();
        $body = Validator::getBody();
        Validator::make($body)->required('comentario')->validate();
        $db = getDB();
        $esInterno = ($body['es_interno'] ?? false) ? 1 : 0;
        $db->prepare("INSERT INTO ticket_comentarios (ticket_id, autor_id, comentario, es_interno) VALUES (?,?,?,?)")
            ->execute([$id, $payload['sub'], $body['comentario'], $esInterno]);

        // Primera respuesta
        $ticket = $db->prepare("SELECT primera_respuesta_at, tecnico_asignado_id FROM tickets WHERE id = ?")->fetch() ?: [];
        if (empty($ticket['primera_respuesta_at']) && in_array($payload['rol'], ['admin','tecnico'])) {
            $db->prepare("UPDATE tickets SET primera_respuesta_at = NOW() WHERE id = ? AND primera_respuesta_at IS NULL")->execute([$id]);
        }
        $db->prepare("UPDATE tickets SET updated_at = NOW() WHERE id = ?")->execute([$id]);

        // Email notification for comment
        if (class_exists('EmailService') && !$esInterno) {
            try {
                $t = $db->prepare("SELECT t.*, s.email as sol_email, 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 = ?");
                $t->execute([$id]);
                $tData = $t->fetch();
                $autorNombre = $payload['nombre'] ?? 'Usuario';
                if ($tData) {
                    if ($payload['rol'] === 'usuario' && $tData['tec_email']) EmailService::notificarComentario($tData, $tData['tec_email'], $autorNombre);
                    elseif ($tData['sol_email']) EmailService::notificarComentario($tData, $tData['sol_email'], $autorNombre);
                }
            } catch(\Exception $e) {}
        }
        if (class_exists('AuditService')) AuditService::ticketComentario($id);
        Response::created(['id' => (int)$db->lastInsertId()]);
    }

    public static function subirAdjunto(int $id): void {
        $payload = AuthMiddleware::verificar();
        $archivo = FileUpload::handle('archivo', 'tickets');
        if (!$archivo) Response::error('No se recibió archivo');
        $db = getDB();
        $db->prepare("INSERT INTO ticket_adjuntos (ticket_id, subido_por_id, nombre_archivo, nombre_original, ruta_archivo, tipo_mime, tamano_bytes) VALUES (?,?,?,?,?,?,?)")
            ->execute([$id, $payload['sub'], $archivo['nombre_servidor'], $archivo['nombre_original'], $archivo['ruta'], $archivo['mime_type'], $archivo['tamano']]);
        Response::created(['id' => (int)$db->lastInsertId()]);
    }

    public static function calificar(int $id): void {
        $payload = AuthMiddleware::verificar();
        $body = Validator::getBody();
        $db = getDB();
        $db->prepare("UPDATE tickets SET calificacion = ?, comentario_calificacion = ? WHERE id = ? AND solicitante_id = ?")
            ->execute([$body['calificacion'] ?? null, $body['comentario'] ?? null, $id, $payload['sub']]);
        Response::success(null, 'Calificación registrada');
    }

    // ==================== REABRIR TICKET ====================
    public static function reabrir(int $id): void {
        $payload = AuthMiddleware::verificar();
        $db = getDB();
        $body = Validator::getBody();

        // Obtener ticket
        $stmt = $db->prepare("SELECT t.*, s.nombre_completo as sol_nombre, s.email as sol_email,
            tec.nombre_completo as tec_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 = ?");
        $stmt->execute([$id]);
        $ticket = $stmt->fetch();
        if (!$ticket) Response::notFound('Ticket no encontrado');

        // Verificar que esté cerrado o resuelto
        if (!in_array($ticket['estado'], ['cerrado', 'resuelto'])) {
            Response::error('Solo se pueden reabrir tickets cerrados o resueltos');
        }

        // Verificar permisos: el solicitante o admin/tecnico
        $isOwner = (int)$payload['sub'] === (int)$ticket['solicitante_id'];
        $isStaff = in_array($payload['rol'], ['admin', 'tecnico']);
        if (!$isOwner && !$isStaff) Response::error('No tienes permiso para reabrir este ticket', 403);

        // Verificar si la reapertura está habilitada
        $permitir = $db->prepare("SELECT valor FROM configuracion WHERE clave = 'ticket_permitir_reabrir'");
        $permitir->execute();
        $permitirVal = $permitir->fetchColumn();
        if ($permitirVal === 'false' || $permitirVal === '0') {
            Response::error('La reapertura de tickets está deshabilitada');
        }

        // Verificar días límite
        $diasLimite = 7;
        $diasStmt = $db->prepare("SELECT valor FROM configuracion WHERE clave = 'ticket_reabrir_dias'");
        $diasStmt->execute();
        $diasVal = $diasStmt->fetchColumn();
        if ($diasVal) $diasLimite = (int)$diasVal;

        $fechaCierre = $ticket['cerrado_at'] ?: $ticket['resuelto_at'] ?: $ticket['updated_at'];
        $diasTranscurridos = (time() - strtotime($fechaCierre)) / 86400;

        if ($diasTranscurridos > $diasLimite) {
            Response::error("Han pasado más de $diasLimite días desde el cierre. Por favor crea un nuevo ticket.", 400);
        }

        // Reabrir
        $motivo = $body['motivo'] ?? 'El usuario reporta que el problema persiste.';
        $reaberturas = ((int)$ticket['reaberturas']) + 1;

        $db->prepare("UPDATE tickets SET estado = 'en_progreso', reabierto_at = NOW(), reaberturas = ?, 
            resuelto_at = NULL, cerrado_at = NULL, calificacion = NULL, comentario_calificacion = NULL,
            updated_at = NOW() WHERE id = ?")
            ->execute([$reaberturas, $id]);

        // Recalcular SLA desde ahora
        if (class_exists('SLACalculator')) {
            try {
                $slaHoras = SLACalculator::getHorasSLA($ticket['prioridad'], $ticket['categoria_id'] ? (int)$ticket['categoria_id'] : null);
                $fechaVenc = SLACalculator::calcularVencimiento($slaHoras);
                $db->prepare("UPDATE tickets SET sla_horas = ?, fecha_vencimiento = ? WHERE id = ?")
                    ->execute([$slaHoras, $fechaVenc, $id]);
            } catch (\Exception $e) {}
        }

        // Agregar comentario de reapertura
        $autorId = $payload['sub'];
        $comentario = "Ticket reabierto (vez #{$reaberturas}). Motivo: {$motivo}";
        $db->prepare("INSERT INTO ticket_comentarios (ticket_id, autor_id, comentario, es_interno) VALUES (?,?,?,0)")
            ->execute([$id, $autorId, $comentario]);

        // Notificar al técnico
        if ($ticket['tecnico_asignado_id']) {
            try {
                $db->prepare("INSERT INTO notificaciones (usuario_id, titulo, mensaje, tipo, url_accion, referencia_tipo, referencia_id) VALUES (?,?,?,?,?,?,?)")
                    ->execute([$ticket['tecnico_asignado_id'], "Ticket reabierto: {$ticket['numero_ticket']}", "El solicitante reporta que el problema persiste.", 'ticket', "/app/admin/ticket-detalle.html?id=$id", 'ticket', $id]);

                if (class_exists('EmailService') && $ticket['tec_email']) {
                    $html = EmailService::template("Ticket Reabierto: {$ticket['numero_ticket']}",
                        "<p>Hola {$ticket['tec_nombre']},</p><p>El ticket <strong>{$ticket['numero_ticket']}: {$ticket['titulo']}</strong> fue reabierto.</p><p><strong>Motivo:</strong> {$motivo}</p><p><strong>Reaperturas:</strong> #{$reaberturas}</p>",
                        "https://ana.evolucionamos.com/app/admin/ticket-detalle.html?id=$id", "Ver Ticket");
                    EmailService::enviar($ticket['tec_email'], "Ticket reabierto: {$ticket['numero_ticket']}", $html);
                }
            } catch (\Exception $e) {}
        }

        // Notificar admins
        try {
            $admins = $db->query("SELECT id FROM usuarios WHERE rol = 'admin' AND activo = 1")->fetchAll();
            foreach ($admins as $a) {
                $db->prepare("INSERT INTO notificaciones (usuario_id, titulo, mensaje, tipo, url_accion, referencia_tipo, referencia_id) VALUES (?,?,?,?,?,?,?)")
                    ->execute([$a['id'], "Ticket reabierto: {$ticket['numero_ticket']}", "{$ticket['sol_nombre']} reabrió el ticket (vez #{$reaberturas})", 'alerta', "/app/admin/ticket-detalle.html?id=$id", 'ticket', $id]);
            }
        } catch (\Exception $e) {}

        // Log
        try { $db->prepare("INSERT INTO log_actividad (usuario_id, accion, entidad_tipo, entidad_id, ip_address, descripcion) VALUES (?,?,?,?,?,?)")
            ->execute([$autorId, 'reabrir_ticket', 'tickets', $id, $_SERVER['REMOTE_ADDR'] ?? null, $comentario]); } catch (\Exception $e) {}

        Response::success(['id' => $id, 'reaberturas' => $reaberturas], 'Ticket reabierto');
    }
}