<?php
/**
 * API para Confirmar Asistencia
 * 
 * Endpoint que procesa las confirmaciones de asistencia
 */

// Configurar headers para JSON ANTES de cualquier output
header('Content-Type: application/json; charset=utf-8');

// Iniciar output buffering para capturar cualquier output no deseado
ob_start();

// Incluir archivos necesarios
require_once '../../includes/db.php';
require_once '../../includes/functions.php';

// Limpiar cualquier output que pueda haber generado los includes
ob_clean();

$rawInput = file_get_contents('php://input');
error_log("confirmar.php: RAW input recibido: " . $rawInput);

$data = json_decode($rawInput, true);

// Verificar si json_decode falló
if (json_last_error() !== JSON_ERROR_NONE) {
    error_log("confirmar.php: ERROR al decodificar JSON - " . json_last_error_msg() . " | Input: " . $rawInput);
    ob_clean();
    echo json_encode(['success' => false, 'message' => 'Error al procesar los datos recibidos']);
    exit;
}

// Log del JSON recibido completo para debugging
error_log("confirmar.php: JSON decodificado: " . json_encode($data));
error_log("confirmar.php: Keys en data: " . implode(', ', array_keys($data ?? [])));

// Obtener ID y token (validación doble)
$invitadoId = isset($data['id']) ? (int)$data['id'] : 0;
$tokenRaw = isset($data['token']) ? (string)$data['token'] : '';

// Log antes de normalizar
error_log("confirmar.php: Extraídos - ID: $invitadoId (tipo: " . gettype($data['id'] ?? 'no existe') . ") | Token RAW: '$tokenRaw' | Longitud RAW: " . strlen($tokenRaw));

// Limpiar el token (solo trim, no normalizar aún para mantener formato original)
$token = trim($tokenRaw);

$estado = $data['estado'] ?? ''; // 'Aceptado' o 'Rechazado'
$mensaje = $data['mensaje'] ?? '';
$nombres_admisiones = $data['nombres_admisiones'] ?? []; // Array de nombres de admisiones

// Log de los parámetros recibidos para debugging
error_log("confirmar.php: ID recibido: $invitadoId | Token recibido RAW: '$tokenRaw' | Token normalizado: '$token' | Longitud normalizado: " . strlen($token));

// Validar que tenemos ambos parámetros
if ($invitadoId <= 0) {
    error_log("confirmar.php: ID de invitado inválido: $invitadoId");
    ob_clean();
    echo json_encode(['success' => false, 'message' => 'ID de invitado no válido']);
    exit;
}

// Validación estricta del token
if (empty($token) || strlen($token) < 16 || strlen($token) > 64) {
    error_log("Token inválido recibido: " . ($data['token'] ?? 'vacío') . " | Longitud: " . strlen($token));
    ob_clean();
    echo json_encode(['success' => false, 'message' => 'Token inválido']);
    exit;
}

if (!in_array($estado, ['Aceptado', 'Rechazado'])) {
    ob_clean();
    echo json_encode(['success' => false, 'message' => 'Estado inválido']);
    exit;
}

// Obtener invitado por ID y token - CRÍTICO: Validación doble para máxima seguridad
// Usar el token tal como viene (sin normalizar) para buscar en la BD
$invitado = obtenerInvitadoPorIdYToken($invitadoId, $token);

// Si no se encuentra con ID y token, buscar solo por token (el token es la validación principal)
if (!$invitado) {
    error_log("confirmar.php: Invitado no encontrado con ID y token - ID: $invitadoId | Token: '$token' (len: " . strlen($token) . ")");
    
    // Buscar solo por token
    $invitado = obtenerInvitadoPorToken($token);
    
    if ($invitado) {
        // Verificar que el ID del invitado encontrado coincide con el ID recibido
        if ($invitado['id'] != $invitadoId) {
            error_log("confirmar.php: Token válido pero ID no coincide - ID recibido: $invitadoId | ID del invitado con este token: " . $invitado['id']);
            // Usar el ID correcto del invitado encontrado
            $invitadoId = (int)$invitado['id'];
            error_log("confirmar.php: Usando ID correcto del invitado: $invitadoId");
        }
    } else {
        // Intentar con token normalizado por si acaso
        $tokenNormalizado = preg_replace('/[^a-f0-9]/i', '', $token);
        if ($tokenNormalizado !== $token) {
            error_log("confirmar.php: Intentando con token normalizado: '$tokenNormalizado'");
            $invitado = obtenerInvitadoPorToken($tokenNormalizado);
            if ($invitado && $invitado['id'] != $invitadoId) {
                $invitadoId = (int)$invitado['id'];
            }
        }
        
        if (!$invitado) {
            error_log("confirmar.php: Invitado no encontrado con ningún método - ID: $invitadoId | Token: '$token'");
            ob_clean();
            echo json_encode(['success' => false, 'message' => 'Invitación no encontrada o parámetros inválidos']);
            exit;
        }
    }
}

// Verificación final: asegurar que tenemos el ID correcto
if ($invitado['id'] != $invitadoId) {
    error_log("confirmar.php: Ajustando ID - ID recibido: $invitadoId | ID correcto: " . $invitado['id']);
    $invitadoId = (int)$invitado['id'];
}

// Obtener el token directamente de la base de datos
$tokenDB = trim($invitado['token'] ?? '');
if (empty($tokenDB)) {
    error_log("confirmar.php: Token vacío en invitado - ID: {$invitado['id']} | Token recibido: '$token'");
    ob_clean();
    echo json_encode(['success' => false, 'message' => 'Error: Token inválido en la base de datos']);
    exit;
}

// IMPORTANTE: Si ya encontramos el invitado por token (usando BINARY comparison en la BD),
// significa que el token ES válido y coincide exactamente. No necesitamos normalizar y comparar de nuevo.
// La búsqueda en la BD con BINARY ya validó que el token es correcto.

// Verificación adicional: comparar tokens directamente (sin normalizar) para asegurar que coinciden
// Esto es solo una verificación de seguridad adicional
$tokenRecibidoLimpio = trim($token);
$tokenDBLimpio = trim($tokenDB);

if ($tokenRecibidoLimpio !== $tokenDBLimpio) {
    // Si no coinciden exactamente, puede ser un problema de encoding o espacios
    // Intentar comparación case-insensitive para tokens hexadecimales
    if (strcasecmp($tokenRecibidoLimpio, $tokenDBLimpio) !== 0) {
        error_log("confirmar.php: Token no coincide exactamente - Recibido: '$tokenRecibidoLimpio' (len: " . strlen($tokenRecibidoLimpio) . ") | DB: '$tokenDBLimpio' (len: " . strlen($tokenDBLimpio) . ") | ID: " . ($invitado['id'] ?? 'N/A'));
        // Aunque no coincidan exactamente, si la BD encontró el invitado, el token es válido
        // Usar el token de la BD para el UPDATE
    }
}

// Guardar ID y token para usar en el UPDATE (ya validados)
// CRÍTICO: Usar el token de la DB tal como está almacenado (sin modificaciones)
$tokenParaUpdate = $tokenDB; // Token exacto de la base de datos
// $invitadoId ya está validado arriba

// Log para debugging
$tokenNormalizadoParaLog = strtolower(preg_replace('/[^a-f0-9]/i', '', $token));
error_log("confirmar.php: Confirmación iniciada - ID: $invitadoId | Token DB: '$tokenParaUpdate' | Token recibido: '$token' | Nombre: " . ($invitado['nombre_invitado'] ?? 'N/A') . " | Estado: $estado");

// Verificar si el estado ya está actualizado
if (isset($invitado['estado']) && $invitado['estado'] === $estado) {
    ob_clean();
    echo json_encode([
        'success' => true, 
        'message' => 'El estado ya estaba actualizado',
        'invitado_id' => $invitadoId,
        'nombre' => $invitado['nombre_invitado']
    ]);
    exit;
}

try {
    $pdo->beginTransaction();
    
    // CRÍTICO: Actualizar usando TANTO el ID como el TOKEN para garantizar que solo se actualice el invitado correcto
    // Esto es una doble verificación de seguridad: el token debe coincidir EXACTAMENTE
    $stmt = $pdo->prepare("
        UPDATE invitados 
        SET estado = ?, 
            fecha_respuesta = NOW(), 
            mensaje_respuesta = ?,
            ip_respuesta = ?
        WHERE id = ? AND token = ?
    ");
    $resultado = $stmt->execute([
        $estado,
        $mensaje ?: null,
        $_SERVER['REMOTE_ADDR'] ?? null,
        $invitadoId,
        $tokenParaUpdate  // Token exacto de la base de datos
    ]);
    
    // Verificar que se actualizó correctamente
    if (!$resultado) {
        $pdo->rollBack();
        error_log("Error en execute UPDATE - ID: $invitadoId | Token usado: '$tokenParaUpdate' | Token recibido: '$token'");
        ob_clean();
        echo json_encode(['success' => false, 'message' => 'Error: No se pudo ejecutar la actualización']);
        exit;
    }
    
    $rowsAffected = $stmt->rowCount();
    if ($rowsAffected === 0) {
        $pdo->rollBack();
        
        // CRÍTICO: Verificar que el invitado existe con ese ID y token exacto
        // No hacer UPDATE solo con ID porque podría actualizar el invitado incorrecto
        $stmtCheck = $pdo->prepare("SELECT id, token, estado, nombre_invitado FROM invitados WHERE id = ? AND BINARY token = ?");
        $stmtCheck->execute([$invitadoId, $tokenParaUpdate]);
        $check = $stmtCheck->fetch(PDO::FETCH_ASSOC);
        
        if ($check) {
            // El invitado existe con ese ID y token exacto
            if ($check['estado'] === $estado) {
                // El estado ya está actualizado
                error_log("UPDATE no afectó filas - Estado ya actualizado - ID: $invitadoId | Token: '$tokenParaUpdate' | Estado: $estado | Nombre: " . ($check['nombre_invitado'] ?? 'N/A'));
                ob_clean();
                echo json_encode(['success' => true, 'message' => 'El estado ya estaba actualizado', 'invitado_id' => $invitadoId, 'nombre' => $check['nombre_invitado']]);
            } else {
                // El estado es diferente pero el UPDATE no funcionó
                // Verificar el token exacto en la BD
                $tokenEnBD = trim($check['token'] ?? '');
                if ($tokenEnBD !== $tokenParaUpdate) {
                    error_log("ERROR: Token en verificación no coincide - Token usado: '$tokenParaUpdate' | Token en BD: '$tokenEnBD' | ID: $invitadoId");
                    // Usar el token exacto de la BD
                    $tokenParaUpdate = $tokenEnBD;
                    
                    // Intentar UPDATE nuevamente con el token exacto de la BD
                    try {
                        $pdo->beginTransaction();
                        $stmt3 = $pdo->prepare("
                            UPDATE invitados 
                            SET estado = ?, 
                                fecha_respuesta = NOW(), 
                                mensaje_respuesta = ?,
                                ip_respuesta = ?
                            WHERE id = ? AND BINARY token = ?
                        ");
                        $resultado3 = $stmt3->execute([
                            $estado,
                            $mensaje ?: null,
                            $_SERVER['REMOTE_ADDR'] ?? null,
                            $invitadoId,
                            $tokenParaUpdate
                        ]);
                        
                        if ($resultado3 && $stmt3->rowCount() > 0) {
                            $pdo->commit();
                            ob_clean();
                            echo json_encode([
                                'success' => true, 
                                'message' => 'Confirmación registrada correctamente',
                                'invitado_id' => $invitadoId,
                                'nombre' => $check['nombre_invitado']
                            ]);
                            exit;
                        } else {
                            $pdo->rollBack();
                        }
                    } catch (Exception $e3) {
                        $pdo->rollBack();
                        error_log("Error en UPDATE con token corregido: " . $e3->getMessage());
                    }
                }
                
                error_log("UPDATE no afectó filas - Estado diferente - ID: $invitadoId | Token: '$tokenParaUpdate' | Estado actual: " . ($check['estado'] ?? 'N/A') . " | Estado intentado: $estado | Nombre: " . ($check['nombre_invitado'] ?? 'N/A'));
                ob_clean();
                echo json_encode(['success' => false, 'message' => 'Error: No se pudo actualizar el estado.']);
            }
        } else {
            // Verificar solo con ID para ver qué token tiene realmente
            $stmtCheck2 = $pdo->prepare("SELECT id, token, estado, nombre_invitado FROM invitados WHERE id = ?");
            $stmtCheck2->execute([$invitadoId]);
            $check2 = $stmtCheck2->fetch(PDO::FETCH_ASSOC);
            
            if ($check2) {
                // El invitado existe pero el token no coincide exactamente
                $tokenRealEnBD = trim($check2['token'] ?? '');
                error_log("ERROR CRÍTICO: Token no coincide en UPDATE - ID: $invitadoId | Token esperado: '$tokenParaUpdate' (len: " . strlen($tokenParaUpdate) . ", hex: " . bin2hex($tokenParaUpdate) . ") | Token en DB: '$tokenRealEnBD' (len: " . strlen($tokenRealEnBD) . ", hex: " . bin2hex($tokenRealEnBD) . ") | Nombre: " . ($check2['nombre_invitado'] ?? 'N/A'));
                ob_clean();
                echo json_encode(['success' => false, 'message' => 'Error: Token no coincide con el invitado.']);
            } else {
                error_log("UPDATE no afectó filas - Invitado no existe - ID: $invitadoId | Token: '$tokenParaUpdate'");
                ob_clean();
                echo json_encode(['success' => false, 'message' => 'Error: Invitado no encontrado.']);
            }
        }
        exit;
    }
    
    // Verificar que se actualizó el invitado correcto
    $stmtVerify = $pdo->prepare("SELECT id, token, estado, nombre_invitado FROM invitados WHERE id = ? AND token = ?");
    $stmtVerify->execute([$invitadoId, $tokenParaUpdate]);
    $verify = $stmtVerify->fetch(PDO::FETCH_ASSOC);
    
    if (!$verify || $verify['estado'] !== $estado) {
        $pdo->rollBack();
        error_log("ERROR: Verificación falló después del UPDATE - ID: $invitadoId | Token: '$tokenParaUpdate' | Estado esperado: $estado | Estado actual: " . ($verify['estado'] ?? 'N/A'));
        echo json_encode(['success' => false, 'message' => 'Error: No se pudo verificar la actualización.']);
        exit;
    }
    
    // Si es Aceptado y hay nombres de admisiones, guardarlos
    if ($estado === 'Aceptado' && !empty($nombres_admisiones) && is_array($nombres_admisiones)) {
        // Eliminar admisiones existentes (si las hay)
        $stmt = $pdo->prepare("DELETE FROM admisiones WHERE invitado_id = ?");
        $stmt->execute([$invitadoId]);
        
        // Insertar nuevas admisiones
        $stmt = $pdo->prepare("INSERT INTO admisiones (invitado_id, nombre_acompanante, orden) VALUES (?, ?, ?)");
        foreach ($nombres_admisiones as $index => $nombre) {
            $nombre = trim($nombre);
            if (!empty($nombre)) {
                $stmt->execute([$invitadoId, sanitize($nombre), $index + 1]);
            }
        }
    }
    
    $pdo->commit();
    
    // Registrar log con información detallada
    registrarLog($invitado['evento_id'] ?? null, 'confirmacion', 'Confirmar', 
        "Invitado ID: $invitadoId, Nombre: {$invitado['nombre_invitado']}, Token: $tokenParaUpdate, Estado: $estado");
    
    // Asegurar que solo se envía JSON
    ob_clean();
    echo json_encode([
        'success' => true, 
        'message' => 'Confirmación registrada correctamente',
        'invitado_id' => $invitadoId,
        'nombre' => $invitado['nombre_invitado']
    ]);
    exit;
} catch (Exception $e) {
    if (isset($pdo)) {
        $pdo->rollBack();
    }
    error_log("Error en confirmar.php: " . $e->getMessage() . " | Token recibido: " . ($token ?? 'N/A') . " | Token DB: " . ($tokenParaUpdate ?? 'N/A') . " | Invitado ID: " . ($invitadoId ?? 'N/A'));
    
    // Asegurar que solo se envía JSON
    ob_clean();
    echo json_encode(['success' => false, 'message' => 'Error al procesar: ' . $e->getMessage()]);
    exit;
}

