<?php
/**
 * Funciones Auxiliares
 * 
 * Funciones de utilidad para el sistema
 */

require_once __DIR__ . '/db.php';

/**
 * Genera un token aleatorio seguro
 * 
 * @param int $longitud Longitud del token (por defecto 32)
 * @return string Token hexadecimal
 */
function generarToken($longitud = 32) {
    return bin2hex(random_bytes($longitud / 2));
}

/**
 * Genera un token único para un invitado (versión corta)
 * 
 * @param int $eventoId ID del evento
 * @param int $longitud Longitud del token (por defecto 16 caracteres para URLs más cortas)
 * @return string Token único
 */
function generarTokenInvitado($eventoId, $longitud = 16) {
    global $pdo;
    
    do {
        $token = generarToken($longitud);
        $stmt = $pdo->prepare("SELECT id FROM invitados WHERE token = ? AND evento_id = ?");
        $stmt->execute([$token, $eventoId]);
    } while ($stmt->fetch());
    
    return $token;
}

/**
 * Genera el enlace de invitación con ID y token
 * 
 * @param int $invitadoId ID del invitado
 * @param string $token Token del invitado
 * @return string URL completa de la invitación
 */
function generarEnlaceInvitacion($invitadoId, $token) {
    $baseUrl = defined('BASE_URL') ? BASE_URL : '';
    $invitacionPath = defined('INVITACION_PATH') ? INVITACION_PATH : '/public/invitacion';
    return $baseUrl . $invitacionPath . '/index.php?id=' . (int)$invitadoId . '&token=' . urlencode($token);
}

/**
 * Obtiene un invitado por su ID y token (validación doble)
 * 
 * @param int $invitadoId ID del invitado
 * @param string $token Token del invitado
 * @return array|false Datos del invitado o false si no existe
 */
function obtenerInvitadoPorIdYToken($invitadoId, $token) {
    global $pdo;
    
    // Validar parámetros
    if (empty($invitadoId) || !is_numeric($invitadoId) || $invitadoId <= 0) {
        error_log("obtenerInvitadoPorIdYToken: ID inválido - $invitadoId");
        return false;
    }
    
    if (empty($token) || strlen(trim($token)) < 16 || strlen(trim($token)) > 64) {
        error_log("obtenerInvitadoPorIdYToken: Token inválido - longitud: " . strlen(trim($token)));
        return false;
    }
    
    $token = trim($token);
    $invitadoId = (int)$invitadoId;
    
    try {
        // CRÍTICO: Validar TANTO el ID como el TOKEN para máxima seguridad
        $stmt = $pdo->prepare("
            SELECT i.*, eb.nombre_novia, eb.nombre_novio, eb.padre_novia, eb.madre_novia, eb.padre_novio, eb.madre_novio, eb.padrino_boda, eb.madrina_boda,
                   ec.nombre_homenajeado, ec.edad, ec.anos_a_cumplir,
                   eq.nombre_quinceanera, eq.padre_quinceanera, eq.madre_quinceanera, eq.padrino_1, eq.padrino_2,
                   eba.nombre_bautizado, eba.padre_bautizo, eba.madre_bautizo, eba.padrino_bautizo, eba.madrina_bautizo,
                   ees.nombre_evento_social, ees.nombre_organizador,
                   e.tipo_evento, e.fecha_evento, e.hora_evento, e.lugar_evento, e.direccion_evento, e.descripcion, e.mensaje_personalizado, e.activo
            FROM invitados i
            INNER JOIN eventos_base e ON i.evento_id = e.id
            LEFT JOIN eventos_boda eb ON e.id = eb.evento_id AND e.tipo_evento = 'boda'
            LEFT JOIN eventos_cumpleanos ec ON e.id = ec.evento_id AND e.tipo_evento = 'cumpleaños'
            LEFT JOIN eventos_quinceanera eq ON e.id = eq.evento_id AND e.tipo_evento = 'quinceañera'
            LEFT JOIN eventos_bautizo eba ON e.id = eba.evento_id AND e.tipo_evento = 'bautizo'
            LEFT JOIN eventos_evento_social ees ON e.id = ees.evento_id AND e.tipo_evento IN ('reunion', 'evento_social')
            WHERE i.id = ? AND BINARY i.token = ? AND e.activo = 1
            LIMIT 1
        ");
        $stmt->execute([$invitadoId, $token]);
        $resultado = $stmt->fetch(PDO::FETCH_ASSOC);
        
        if ($resultado) {
            error_log("obtenerInvitadoPorIdYToken: Invitado encontrado - ID: $invitadoId | Token: '$token' | Nombre: " . ($resultado['nombre_invitado'] ?? 'N/A'));
        } else {
            error_log("obtenerInvitadoPorIdYToken: Invitado NO encontrado - ID: $invitadoId | Token: '$token'");
        }
        
        return $resultado;
    } catch (Exception $e) {
        error_log("Error en obtenerInvitadoPorIdYToken: " . $e->getMessage() . " | ID: $invitadoId | Token: '$token'");
        return false;
    }
}

/**
 * Obtiene un invitado por su token (mantenido para compatibilidad)
 * 
 * @param string $token Token del invitado
 * @return array|false Datos del invitado o false si no existe
 */
function obtenerInvitadoPorToken($token) {
    global $pdo;
    
    // Validar que el token no esté vacío y tenga una longitud razonable
    if (empty($token) || strlen(trim($token)) < 16 || strlen(trim($token)) > 64) {
        error_log("obtenerInvitadoPorToken: Token inválido - longitud: " . strlen(trim($token)));
        return false;
    }
    
    $token = trim($token);
    
    try {
        // CRÍTICO: Usar comparación exacta del token (BINARY para case-sensitive en MySQL)
        // Esto garantiza que solo se encuentre el invitado con el token EXACTO
        $stmt = $pdo->prepare("
            SELECT i.*, eb.nombre_novia, eb.nombre_novio, eb.padre_novia, eb.madre_novia, eb.padre_novio, eb.madre_novio, eb.padrino_boda, eb.madrina_boda,
                   ec.nombre_homenajeado, ec.edad, ec.anos_a_cumplir,
                   eq.nombre_quinceanera, eq.padre_quinceanera, eq.madre_quinceanera, eq.padrino_1, eq.padrino_2,
                   eba.nombre_bautizado, eba.padre_bautizo, eba.madre_bautizo, eba.padrino_bautizo, eba.madrina_bautizo,
                   ees.nombre_evento_social, ees.nombre_organizador,
                   e.tipo_evento, e.fecha_evento, e.hora_evento, e.lugar_evento, e.direccion_evento, e.descripcion, e.mensaje_personalizado, e.activo
            FROM invitados i
            INNER JOIN eventos_base e ON i.evento_id = e.id
            LEFT JOIN eventos_boda eb ON e.id = eb.evento_id AND e.tipo_evento = 'boda'
            LEFT JOIN eventos_cumpleanos ec ON e.id = ec.evento_id AND e.tipo_evento = 'cumpleaños'
            LEFT JOIN eventos_quinceanera eq ON e.id = eq.evento_id AND e.tipo_evento = 'quinceañera'
            LEFT JOIN eventos_bautizo eba ON e.id = eba.evento_id AND e.tipo_evento = 'bautizo'
            LEFT JOIN eventos_evento_social ees ON e.id = ees.evento_id AND e.tipo_evento IN ('reunion', 'evento_social')
            WHERE BINARY i.token = ? AND e.activo = 1
            LIMIT 1
        ");
        $stmt->execute([$token]);
        $resultado = $stmt->fetch(PDO::FETCH_ASSOC);
        
        // Verificación adicional: asegurar que el token coincide exactamente
        if ($resultado && isset($resultado['token'])) {
            $tokenDB = trim($resultado['token']);
            if ($tokenDB !== $token) {
                error_log("obtenerInvitadoPorToken: Token no coincide después de SELECT - Recibido: '$token' | DB: '$tokenDB' | ID: " . ($resultado['id'] ?? 'N/A'));
                return false;
            }
        }
        
        if ($resultado) {
            error_log("obtenerInvitadoPorToken: Invitado encontrado - ID: " . ($resultado['id'] ?? 'N/A') . " | Token: '$token' | Nombre: " . ($resultado['nombre_invitado'] ?? 'N/A'));
        }
        
        return $resultado;
    } catch (Exception $e) {
        error_log("Error en obtenerInvitadoPorToken: " . $e->getMessage() . " | Token: '$token'");
        return false;
    }
}

/**
 * Obtiene la tracklist sugerida por un invitado
 * 
 * @param int $invitadoId ID del invitado
 * @return array Array de canciones con id, autor, nombre_cancion
 */
function obtenerTracklistInvitado($invitadoId) {
    global $pdo;
    
    try {
        $stmt = $pdo->prepare("
            SELECT id, autor, nombre_cancion 
            FROM eventos_tracklist 
            WHERE invitado_id = ? AND activo = 1 
            ORDER BY fecha_creacion ASC
        ");
        $stmt->execute([$invitadoId]);
        return $stmt->fetchAll(PDO::FETCH_ASSOC);
    } catch (Exception $e) {
        error_log("Error al obtener tracklist: " . $e->getMessage());
        return [];
    }
}

/**
 * Obtiene estadísticas de tracklist para un evento
 * 
 * @param int $eventoId ID del evento
 * @return array Estadísticas con artista más votado, canción más votada, y ranking
 */
function obtenerEstadisticasTracklist($eventoId) {
    global $pdo;
    
    try {
        // Artista más votado
        $stmtArtista = $pdo->prepare("
            SELECT autor, COUNT(*) as votos
            FROM eventos_tracklist
            WHERE evento_id = ? AND activo = 1
            GROUP BY autor
            ORDER BY votos DESC
            LIMIT 1
        ");
        $stmtArtista->execute([$eventoId]);
        $artistaMasVotado = $stmtArtista->fetch(PDO::FETCH_ASSOC);
        
        // Canción más votada (combinación autor + nombre_cancion)
        $stmtCancion = $pdo->prepare("
            SELECT autor, nombre_cancion, COUNT(*) as votos
            FROM eventos_tracklist
            WHERE evento_id = ? AND activo = 1
            GROUP BY autor, nombre_cancion
            ORDER BY votos DESC
            LIMIT 1
        ");
        $stmtCancion->execute([$eventoId]);
        $cancionMasVotada = $stmtCancion->fetch(PDO::FETCH_ASSOC);
        
        // Ranking de canciones (score)
        $stmtRanking = $pdo->prepare("
            SELECT 
                autor,
                nombre_cancion,
                COUNT(*) as votantes
            FROM eventos_tracklist
            WHERE evento_id = ? AND activo = 1
            GROUP BY autor, nombre_cancion
            ORDER BY votantes DESC, autor ASC, nombre_cancion ASC
        ");
        $stmtRanking->execute([$eventoId]);
        $ranking = $stmtRanking->fetchAll(PDO::FETCH_ASSOC);
        
        return [
            'artista_mas_votado' => $artistaMasVotado ?: null,
            'cancion_mas_votada' => $cancionMasVotada ?: null,
            'ranking' => $ranking
        ];
    } catch (Exception $e) {
        error_log("Error al obtener estadísticas de tracklist: " . $e->getMessage());
        return [
            'artista_mas_votado' => null,
            'cancion_mas_votada' => null,
            'ranking' => []
        ];
    }
}

/**
 * Obtiene la tracklist detallada de un evento con información de invitados
 * 
 * @param int $eventoId ID del evento
 * @return array Array de canciones con información del invitado
 */
function obtenerTracklistDetallada($eventoId) {
    global $pdo;
    
    try {
        $stmt = $pdo->prepare("
            SELECT 
                t.id,
                t.autor,
                t.nombre_cancion,
                t.fecha_creacion,
                i.id as invitado_id,
                i.nombre_cortesia,
                i.nombre_invitado,
                i.estado
            FROM eventos_tracklist t
            INNER JOIN invitados i ON t.invitado_id = i.id
            WHERE t.evento_id = ? AND t.activo = 1
            ORDER BY t.fecha_creacion DESC
        ");
        $stmt->execute([$eventoId]);
        return $stmt->fetchAll(PDO::FETCH_ASSOC);
    } catch (Exception $e) {
        error_log("Error al obtener tracklist detallada: " . $e->getMessage());
        return [];
    }
}

/**
 * Obtiene los nombres de las admisiones (acompañantes) de un invitado
 * 
 * @param int $invitadoId ID del invitado
 * @return array Array de nombres
 */
function obtenerAdmisiones($invitadoId) {
    global $pdo;
    
    $stmt = $pdo->prepare("
        SELECT nombre_acompanante FROM admisiones 
        WHERE invitado_id = ? 
        ORDER BY orden ASC
    ");
    $stmt->execute([$invitadoId]);
    return $stmt->fetchAll(PDO::FETCH_COLUMN);
}

/**
 * Obtiene los datos del evento
 * 
 * @param int $eventoId ID del evento
 * @return array|false Datos del evento o false si no existe
 */
function obtenerEvento($eventoId) {
    global $pdo;
    
    // Hacer JOIN sin condición de tipo_evento, solo por evento_id
    // Esto asegura que siempre se carguen los datos si existen, independientemente del tipo
    $stmt = $pdo->prepare("
        SELECT e.*,
               eb.nombre_novia, eb.nombre_novio, eb.padre_novia, eb.madre_novia, eb.padre_novio, eb.madre_novio, eb.padrino_boda, eb.madrina_boda,
               ec.nombre_homenajeado, ec.edad, ec.anos_a_cumplir,
               eq.nombre_quinceanera, eq.padre_quinceanera, eq.madre_quinceanera, eq.padrino_1, eq.padrino_2,
               eba.nombre_bautizado, eba.padre_bautizo, eba.madre_bautizo, eba.padrino_bautizo, eba.madrina_bautizo,
               ees.nombre_evento_social, ees.nombre_organizador
        FROM eventos_base e
        LEFT JOIN eventos_boda eb ON e.id = eb.evento_id
        LEFT JOIN eventos_cumpleanos ec ON e.id = ec.evento_id
        LEFT JOIN eventos_quinceanera eq ON e.id = eq.evento_id
        LEFT JOIN eventos_bautizo eba ON e.id = eba.evento_id
        LEFT JOIN eventos_evento_social ees ON e.id = ees.evento_id
        WHERE e.id = ?
        LIMIT 1
    ");
    $stmt->execute([$eventoId]);
    $resultado = $stmt->fetch(PDO::FETCH_ASSOC);
    
    return $resultado;
}

/**
 * Obtiene el itinerario de un evento
 * 
 * @param int $eventoId ID del evento
 * @return array Array de items del itinerario ordenados por orden y fecha
 */
function obtenerItinerario($eventoId) {
    global $pdo;
    
    try {
        $stmt = $pdo->prepare("
            SELECT * FROM eventos_itinerario 
            WHERE evento_id = ? AND activo = 1 
            ORDER BY orden ASC, fecha ASC, hora ASC
        ");
        $stmt->execute([$eventoId]);
        return $stmt->fetchAll(PDO::FETCH_ASSOC);
    } catch (Exception $e) {
        error_log("Error al obtener itinerario: " . $e->getMessage());
        return [];
    }
}

/**
 * Obtiene el dress code de un evento
 * 
 * @param int $eventoId ID del evento
 * @return array Array de opciones del dress code ordenadas por orden
 */
function obtenerDresscode($eventoId) {
    global $pdo;
    
    try {
        $stmt = $pdo->prepare("
            SELECT * FROM eventos_dresscode 
            WHERE evento_id = ? AND activo = 1 
            ORDER BY orden ASC
        ");
        $stmt->execute([$eventoId]);
        return $stmt->fetchAll(PDO::FETCH_ASSOC);
    } catch (Exception $e) {
        error_log("Error al obtener dresscode: " . $e->getMessage());
        return [];
    }
}

/**
 * Obtiene el obsequio de un evento (mantiene compatibilidad)
 * 
 * @param int $eventoId ID del evento
 * @return array|null Datos del obsequio o null si no existe
 */
function obtenerObsequio($eventoId) {
    global $pdo;
    
    try {
        $stmt = $pdo->prepare("
            SELECT * FROM eventos_obsequio 
            WHERE evento_id = ? AND activo = 1 
            LIMIT 1
        ");
        $stmt->execute([$eventoId]);
        $result = $stmt->fetch(PDO::FETCH_ASSOC);
        return $result ? $result : null;
    } catch (Exception $e) {
        error_log("Error al obtener obsequio: " . $e->getMessage());
        return null;
    }
}

/**
 * Obtiene todos los obsequios de un evento (múltiples cuentas y opciones)
 * 
 * @param int $eventoId ID del evento
 * @return array Array de obsequios ordenados por tipo y orden
 */
function obtenerObsequios($eventoId) {
    global $pdo;
    
    try {
        $stmt = $pdo->prepare("
            SELECT * FROM eventos_obsequio 
            WHERE evento_id = ? AND activo = 1 
            ORDER BY tipo ASC, id ASC
        ");
        $stmt->execute([$eventoId]);
        return $stmt->fetchAll(PDO::FETCH_ASSOC);
    } catch (Exception $e) {
        error_log("Error al obtener obsequios: " . $e->getMessage());
        return [];
    }
}

/**
 * Obtiene todas las fotos de la galería de un evento
 * 
 * @param int $eventoId ID del evento
 * @return array Array con las fotos de la galería
 */
function obtenerGaleria($eventoId) {
    global $pdo;
    
    try {
        $stmt = $pdo->prepare("
            SELECT * FROM eventos_galeria 
            WHERE evento_id = ? AND activo = 1 
            ORDER BY orden ASC, fecha_subida DESC
        ");
        $stmt->execute([$eventoId]);
        return $stmt->fetchAll(PDO::FETCH_ASSOC);
    } catch (Exception $e) {
        error_log("Error al obtener galería: " . $e->getMessage());
        return [];
    }
}

/**
 * Obtiene el video asignado a un invitado específico
 * Si no tiene video asignado, retorna null
 * 
 * @param int $eventoId ID del evento
 * @param int $invitadoId ID del invitado
 * @return array|null Video asignado o null si no tiene asignación
 */
function obtenerVideoPorInvitado($eventoId, $invitadoId) {
    global $pdo;
    
    try {
        $stmt = $pdo->prepare("
            SELECT v.* 
            FROM eventos_videos v
            INNER JOIN eventos_videos_invitados vi ON v.id = vi.video_id
            WHERE v.evento_id = ? 
            AND vi.invitado_id = ? 
            AND v.activo = 1
            ORDER BY v.orden ASC, v.fecha_subida DESC
            LIMIT 1
        ");
        $stmt->execute([$eventoId, $invitadoId]);
        $resultado = $stmt->fetch(PDO::FETCH_ASSOC);
        return $resultado ?: null;
    } catch (Exception $e) {
        error_log("Error al obtener video por invitado: " . $e->getMessage());
        return null;
    }
}

/**
 * Asigna un video a múltiples invitados
 * 
 * @param int $videoId ID del video
 * @param array $invitadoIds Array de IDs de invitados
 * @param bool $useTransaction Si true, maneja su propia transacción. Si false, asume que ya hay una transacción activa.
 * @return bool True si se asignó correctamente
 */
function asignarVideoAInvitados($videoId, $invitadoIds, $useTransaction = true) {
    global $pdo;
    
    try {
        $transactionStarted = false;
        if ($useTransaction) {
            $pdo->beginTransaction();
            $transactionStarted = true;
        }
        
        // Eliminar asignaciones existentes del video
        $stmtDelete = $pdo->prepare("DELETE FROM eventos_videos_invitados WHERE video_id = ?");
        $stmtDelete->execute([$videoId]);
        
        // Insertar nuevas asignaciones
        if (!empty($invitadoIds)) {
            $stmtInsert = $pdo->prepare("INSERT INTO eventos_videos_invitados (video_id, invitado_id) VALUES (?, ?)");
            foreach ($invitadoIds as $invitadoId) {
                $stmtInsert->execute([$videoId, $invitadoId]);
            }
        }
        
        if ($transactionStarted) {
            $pdo->commit();
        }
        return true;
    } catch (Exception $e) {
        if ($transactionStarted) {
            // Solo hacer rollBack si iniciamos la transacción
            try {
                $pdo->rollBack();
            } catch (PDOException $rollbackException) {
                // Si falla el rollBack, solo loguear
                error_log("Error al hacer rollBack: " . $rollbackException->getMessage());
            }
        }
        error_log("Error al asignar video a invitados: " . $e->getMessage());
        return false;
    }
}

/**
 * Obtiene los invitados asignados a un video
 * 
 * @param int $videoId ID del video
 * @return array Array con los IDs de los invitados asignados
 */
function obtenerInvitadosPorVideo($videoId) {
    global $pdo;
    
    try {
        $stmt = $pdo->prepare("
            SELECT invitado_id 
            FROM eventos_videos_invitados 
            WHERE video_id = ?
        ");
        $stmt->execute([$videoId]);
        return $stmt->fetchAll(PDO::FETCH_COLUMN);
    } catch (Exception $e) {
        error_log("Error al obtener invitados por video: " . $e->getMessage());
        return [];
    }
}

/**
 * Obtiene todos los videos con información de asignaciones de invitados
 * 
 * @param int $eventoId ID del evento
 * @return array Array con videos y sus invitados asignados
 */
function obtenerVideosConAsignaciones($eventoId) {
    global $pdo;
    
    try {
        $stmt = $pdo->prepare("
            SELECT v.*,
                   GROUP_CONCAT(vi.invitado_id) as invitados_asignados,
                   COUNT(vi.invitado_id) as total_asignaciones
            FROM eventos_videos v
            LEFT JOIN eventos_videos_invitados vi ON v.id = vi.video_id
            WHERE v.evento_id = ? AND v.activo = 1
            GROUP BY v.id
            ORDER BY v.orden ASC, v.fecha_subida DESC
        ");
        $stmt->execute([$eventoId]);
        $videos = $stmt->fetchAll(PDO::FETCH_ASSOC);
        
        // Convertir invitados_asignados a array
        foreach ($videos as &$video) {
            if (!empty($video['invitados_asignados'])) {
                $video['invitados_asignados'] = explode(',', $video['invitados_asignados']);
            } else {
                $video['invitados_asignados'] = [];
            }
            $video['total_asignaciones'] = (int)$video['total_asignaciones'];
        }
        
        return $videos;
    } catch (Exception $e) {
        error_log("Error al obtener videos con asignaciones: " . $e->getMessage());
        return [];
    }
}

/**
 * Obtiene los videos del evento
 * 
 * @param int $eventoId ID del evento
 * @return array Array con los videos del evento
 */
function obtenerVideos($eventoId) {
    global $pdo;
    
    try {
        $stmt = $pdo->prepare("
            SELECT * FROM eventos_videos 
            WHERE evento_id = ? AND activo = 1 
            ORDER BY orden ASC, fecha_subida DESC
        ");
        $stmt->execute([$eventoId]);
        return $stmt->fetchAll(PDO::FETCH_ASSOC);
    } catch (Exception $e) {
        error_log("Error al obtener videos: " . $e->getMessage());
        return [];
    }
}


/**
 * Obtiene los colores del evento desde eventos_colores
 * Si no existen en eventos_colores, usa los valores de eventos_base como fallback
 * 
 * @param int $eventoId ID del evento
 * @param array $evento Array con datos del evento (para obtener colores de eventos_base como fallback)
 * @return array Array con los colores del evento
 */
function obtenerColores($eventoId, $evento = null) {
    global $pdo;
    
    try {
        $stmt = $pdo->prepare("
            SELECT color_principal, color_secundario, color_texto, color_resalte, color_1, color_2, color_3, color_4
            FROM eventos_colores
            WHERE evento_id = ?
        ");
        $stmt->execute([$eventoId]);
        $colores = $stmt->fetch(PDO::FETCH_ASSOC);
        
        if (!$colores) {
            // Si no existe registro en eventos_colores, retornar valores por defecto (NO usar eventos_base)
            return [
                'color_principal' => '#667eea',
                'color_secundario' => '#764ba2',
                'color_texto' => '#000000',
                'color_resalte' => '#FF0000',
                'color_1' => '#FFFFFF',
                'color_2' => '#FFFFFF',
                'color_3' => '#FFFFFF',
                'color_4' => '#FFFFFF'
            ];
        }
        
        // Asegurar valores por defecto si son NULL (NO usar eventos_base como fallback)
        $colores['color_principal'] = !empty($colores['color_principal']) ? $colores['color_principal'] : '#667eea';
        $colores['color_secundario'] = !empty($colores['color_secundario']) ? $colores['color_secundario'] : '#764ba2';
        $colores['color_texto'] = !empty($colores['color_texto']) ? $colores['color_texto'] : '#000000';
        $colores['color_resalte'] = !empty($colores['color_resalte']) ? $colores['color_resalte'] : '#FF0000';
        $colores['color_1'] = !empty($colores['color_1']) ? $colores['color_1'] : '#FFFFFF';
        $colores['color_2'] = !empty($colores['color_2']) ? $colores['color_2'] : '#FFFFFF';
        $colores['color_3'] = !empty($colores['color_3']) ? $colores['color_3'] : '#FFFFFF';
        $colores['color_4'] = !empty($colores['color_4']) ? $colores['color_4'] : '#FFFFFF';
        
        return $colores;
    } catch (Exception $e) {
        error_log("Error al obtener colores: " . $e->getMessage());
        // Retornar valores por defecto en caso de error (NO usar eventos_base)
        return [
            'color_principal' => '#667eea',
            'color_secundario' => '#764ba2',
            'color_texto' => '#000000',
            'color_resalte' => '#FF0000',
            'color_1' => '#FFFFFF',
            'color_2' => '#FFFFFF',
            'color_3' => '#FFFFFF',
            'color_4' => '#FFFFFF'
        ];
    }
}

/**
 * Obtiene los datos del sobre del evento
 * 
 * @param int $eventoId ID del evento
 * @return array Array con los datos del sobre (sobre_principal, sobre_moviles)
 */
function obtenerSobre($eventoId) {
    global $pdo;
    
    try {
        $stmt = $pdo->prepare("
            SELECT sobre_principal, sobre_moviles
            FROM eventos_sobre
            WHERE evento_id = ? AND activo = 1
        ");
        $stmt->execute([$eventoId]);
        $sobre = $stmt->fetch(PDO::FETCH_ASSOC);
        
        if (!$sobre) {
            return [
                'sobre_principal' => null,
                'sobre_moviles' => null
            ];
        }
        
        return $sobre;
    } catch (Exception $e) {
        error_log("Error al obtener sobre: " . $e->getMessage());
        return [
            'sobre_principal' => null,
            'sobre_moviles' => null
        ];
    }
}

/**
 * Obtiene las tipografías del evento
 * 
 * @param int $eventoId ID del evento
 * @return array Array con las tipografías del evento
 */
function obtenerTipografias($eventoId) {
    global $pdo;
    
    try {
        $stmt = $pdo->prepare("
            SELECT 
                letra_cursiva_1_archivo, letra_cursiva_1_nombre,
                letra_cursiva_2_archivo, letra_cursiva_2_nombre,
                letra_sans_1_archivo, letra_sans_1_nombre,
                letra_sans_2_archivo, letra_sans_2_nombre,
                letra_sans_3_archivo, letra_sans_3_nombre
            FROM eventos_tipografias
            WHERE evento_id = ? AND activo = 1
        ");
        $stmt->execute([$eventoId]);
        $tipografias = $stmt->fetch(PDO::FETCH_ASSOC);
        
        if (!$tipografias) {
            return [
                'letra_cursiva_1_archivo' => null,
                'letra_cursiva_1_nombre' => null,
                'letra_cursiva_2_archivo' => null,
                'letra_cursiva_2_nombre' => null,
                'letra_sans_1_archivo' => null,
                'letra_sans_1_nombre' => null,
                'letra_sans_2_archivo' => null,
                'letra_sans_2_nombre' => null,
                'letra_sans_3_archivo' => null,
                'letra_sans_3_nombre' => null
            ];
        }
        
        return $tipografias;
    } catch (Exception $e) {
        error_log("Error al obtener tipografías: " . $e->getMessage());
        return [
            'letra_cursiva_1_archivo' => null,
            'letra_cursiva_1_nombre' => null,
            'letra_cursiva_2_archivo' => null,
            'letra_cursiva_2_nombre' => null,
            'letra_sans_1_archivo' => null,
            'letra_sans_1_nombre' => null,
            'letra_sans_2_archivo' => null,
            'letra_sans_2_nombre' => null,
            'letra_sans_3_archivo' => null,
            'letra_sans_3_nombre' => null
        ];
    }
}

/**
 * Obtiene el tipo de evento permitido para esta instalación
 * 
 * @return string Tipo de evento permitido
 */
function obtenerTipoEventoPermitido() {
    global $pdo;
    
    try {
        $stmt = $pdo->query("SELECT tipo_evento_permitido FROM configuracion_sistema WHERE activo = 1 LIMIT 1");
        $result = $stmt->fetch(PDO::FETCH_ASSOC);
        return $result ? $result['tipo_evento_permitido'] : 'boda';
    } catch (Exception $e) {
        // Si la tabla no existe, retornar 'boda' por defecto
        return 'boda';
    }
}

/**
 * Obtiene los datos del evento (alias para compatibilidad)
 * @deprecated Usar obtenerEvento() en su lugar
 */
function obtenerBoda($bodaId) {
    return obtenerEvento($bodaId);
}

/**
 * Sanitiza entrada de datos para almacenar en BD
 * Solo elimina tags HTML y espacios. NO aplica htmlspecialchars.
 * El escape HTML (htmlspecialchars) debe aplicarse al MOSTRAR en la vista.
 * 
 * @param string $data Datos a sanitizar
 * @return string Datos sanitizados
 */
function sanitize($data) {
    return strip_tags(trim($data));
}

/**
 * Decodifica entidades HTML en texto que pudo haberse guardado doble-escaped.
 * Usar antes de htmlspecialchars al mostrar en formularios.
 * 
 * @param string $str Texto posiblemente con entidades
 * @return string Texto decodificado
 */
function decodeEntidadesTexto($str) {
    if (empty($str)) return '';
    $prev = '';
    while ($prev !== $str) {
        $prev = $str;
        $str = html_entity_decode($str, ENT_QUOTES | ENT_HTML5, 'UTF-8');
    }
    return $str;
}

/**
 * Valida formato de email
 * 
 * @param string $email Email a validar
 * @return bool True si es válido
 */
function validarEmail($email) {
    return filter_var($email, FILTER_VALIDATE_EMAIL) !== false;
}

/**
 * Valida formato de teléfono (básico)
 * 
 * @param string $telefono Teléfono a validar
 * @return bool True si es válido
 */
function validarTelefono($telefono) {
    // Eliminar espacios y caracteres especiales
    $telefono = preg_replace('/[^0-9+]/', '', $telefono);
    // Validar que tenga entre 9 y 15 dígitos
    return strlen($telefono) >= 9 && strlen($telefono) <= 15;
}

/**
 * Formatea fecha para mostrar
 * 
 * @param string $fecha Fecha en formato MySQL
 * @param string $formato Formato de salida (por defecto 'd/m/Y')
 * @return string Fecha formateada
 */
function formatearFecha($fecha, $formato = 'd/m/Y') {
    if (empty($fecha)) return '';
    $date = new DateTime($fecha);
    return $date->format($formato);
}

/**
 * Formatea fecha y hora para mostrar
 * 
 * @param string $fecha Fecha en formato MySQL
 * @param string $hora Hora en formato MySQL
 * @return string Fecha y hora formateada
 */
function formatearFechaHora($fecha, $hora) {
    if (empty($fecha)) return '';
    $datetime = new DateTime($fecha . ' ' . $hora);
    return $datetime->format('d/m/Y H:i');
}

/**
 * Formatea fecha en español legible (ej: "16 de Diciembre/2025")
 * 
 * @param string $fecha Fecha en formato MySQL
 * @return string Fecha formateada en español
 */
function formatearFechaEspanol($fecha) {
    if (empty($fecha)) return '';
    
    $meses = [
        1 => 'Enero', 2 => 'Febrero', 3 => 'Marzo', 4 => 'Abril',
        5 => 'Mayo', 6 => 'Junio', 7 => 'Julio', 8 => 'Agosto',
        9 => 'Septiembre', 10 => 'Octubre', 11 => 'Noviembre', 12 => 'Diciembre'
    ];
    
    $date = new DateTime($fecha);
    $dia = $date->format('d');
    $mes = (int)$date->format('m');
    $anio = $date->format('Y');
    
    return $dia . ' de ' . $meses[$mes] . '/' . $anio;
}

/**
 * Obtiene el texto del tipo de evento con nombre según configuración
 * 
 * @param array $evento Datos del evento
 * @return string Texto formateado (ej: "Boda de María y Juan")
 */
function obtenerTituloEvento($evento) {
    if (!$evento) return '';
    
    $tipo = $evento['tipo_evento'] ?? '';
    $tiposNombres = [
        'boda' => 'Boda',
        'cumpleaños' => 'Cumpleaños',
        'quinceañera' => 'Quinceañera',
        'bautizo' => 'Bautizo',
        'reunion' => 'Reunión',
        'evento_social' => 'Evento Social'
    ];
    
    $tipoNombre = $tiposNombres[$tipo] ?? 'Evento';
    
    switch ($tipo) {
        case 'boda':
            if (!empty($evento['nombre_novia']) && !empty($evento['nombre_novio'])) {
                $nombreNovia = explode(' ', trim($evento['nombre_novia']))[0];
                $nombreNovio = explode(' ', trim($evento['nombre_novio']))[0];
                return $tipoNombre . ' de ' . $nombreNovia . ' y ' . $nombreNovio;
            }
            break;
        case 'cumpleaños':
            if (!empty($evento['nombre_homenajeado'])) {
                $nombre = explode(' ', trim($evento['nombre_homenajeado']))[0];
                return $tipoNombre . ' de ' . $nombre;
            }
            break;
        case 'quinceañera':
            if (!empty($evento['nombre_quinceanera'])) {
                $nombre = explode(' ', trim($evento['nombre_quinceanera']))[0];
                return $tipoNombre . ' de ' . $nombre;
            }
            break;
        case 'bautizo':
            if (!empty($evento['nombre_bautizado'])) {
                $nombre = $evento['nombre_bautizado'];
                return $tipoNombre . ' de ' . $nombre;
            }
            break;
        case 'reunion':
        case 'evento_social':
            if (!empty($evento['nombre_evento_social'])) {
                return $evento['nombre_evento_social'];
            }
            if (!empty($evento['nombre_organizador'])) {
                return $tipoNombre . ' - ' . $evento['nombre_organizador'];
            }
            break;
    }
    
    return $tipoNombre;
}

/**
 * Obtiene las opciones de "invitación de" según el tipo de evento
 * 
 * @param array $evento Datos del evento
 * @return array Array con opciones ['valor' => 'etiqueta']
 */
function obtenerOpcionesInvitacionDe($evento) {
    if (!$evento) return [];
    
    $tipo = $evento['tipo_evento'] ?? '';
    $opciones = [];
    
    switch ($tipo) {
        case 'boda':
            $opciones['Novios'] = 'Novios';
            if (!empty($evento['nombre_novia'])) {
                $opciones['Novia'] = 'Novia (' . $evento['nombre_novia'] . ')';
            }
            if (!empty($evento['nombre_novio'])) {
                $opciones['Novio'] = 'Novio (' . $evento['nombre_novio'] . ')';
            }
            if (!empty($evento['padre_novia'])) {
                $opciones['Padre Novia'] = 'Padre de la Novia (' . $evento['padre_novia'] . ')';
            }
            if (!empty($evento['madre_novia'])) {
                $opciones['Madre Novia'] = 'Madre de la Novia (' . $evento['madre_novia'] . ')';
            }
            if (!empty($evento['padre_novio'])) {
                $opciones['Padre Novio'] = 'Padre del Novio (' . $evento['padre_novio'] . ')';
            }
            if (!empty($evento['madre_novio'])) {
                $opciones['Madre Novio'] = 'Madre del Novio (' . $evento['madre_novio'] . ')';
            }
            break;
            
        case 'cumpleaños':
            if (!empty($evento['nombre_homenajeado'])) {
                $opciones['Homenajeado'] = $evento['nombre_homenajeado'];
            }
            break;
            
        case 'quinceañera':
            if (!empty($evento['nombre_quinceanera'])) {
                $opciones['Quinceañera'] = 'Quinceañera (' . $evento['nombre_quinceanera'] . ')';
            }
            if (!empty($evento['padre_quinceanera'])) {
                $opciones['Padre Quinceañera'] = 'Padre (' . $evento['padre_quinceanera'] . ')';
            }
            if (!empty($evento['madre_quinceanera'])) {
                $opciones['Madre Quinceañera'] = 'Madre (' . $evento['madre_quinceanera'] . ')';
            }
            break;
            
        case 'bautizo':
            if (!empty($evento['padre_bautizo'])) {
                $opciones['Padre Bautizo'] = 'Padre (' . $evento['padre_bautizo'] . ')';
            }
            if (!empty($evento['madre_bautizo'])) {
                $opciones['Madre Bautizo'] = 'Madre (' . $evento['madre_bautizo'] . ')';
            }
            break;
            
        case 'reunion':
        case 'evento_social':
            if (!empty($evento['nombre_organizador'])) {
                $opciones['Organizador'] = 'Organizador (' . $evento['nombre_organizador'] . ')';
            }
            $opciones['Personalizado'] = 'Personalizado'; // Permite texto libre
            break;
    }
    
    return $opciones;
}

/**
 * Registra una acción en el log (solo acciones, no navegación).
 * Quién hizo la acción: si no se pasa $usuarioId y existe getAdminId(), se usa el admin logueado.
 *
 * @param int|null $eventoId ID del evento (null si la acción no es de un evento)
 * @param string $tipo Tipo: invitado, usuario, configuracion, confirmacion, etc.
 * @param string $accion Acción: Crear, Actualizar, Eliminar, Regenerar Token, etc.
 * @param string $descripcion Descripción legible de lo que se hizo
 * @param int|null $usuarioId ID del administrador que realizó la acción (null = usar getAdminId() si existe)
 */
function registrarLog($eventoId, $tipo, $accion, $descripcion = '', $usuarioId = null) {
    global $pdo;
    
    if ($usuarioId === null && function_exists('getAdminId')) {
        $usuarioId = getAdminId();
    }
    
    $userAgent = isset($_SERVER['HTTP_USER_AGENT']) ? substr($_SERVER['HTTP_USER_AGENT'], 0, 255) : null;
    
    $stmt = $pdo->prepare("
        INSERT INTO logs (evento_id, usuario_id, tipo, accion, descripcion, ip, user_agent)
        VALUES (?, ?, ?, ?, ?, ?, ?)
    ");
    $stmt->execute([
        $eventoId,
        $usuarioId,
        $tipo,
        $accion,
        $descripcion,
        $_SERVER['REMOTE_ADDR'] ?? null,
        $userAgent
    ]);
}

/**
 * Obtiene la información del sistema / licencia (tabla informacion_sistema, id=1).
 * Solo lectura; no es gestionable desde el panel.
 *
 * @return array|null Fila única o null si la tabla no existe o no hay registro
 */
function obtenerInformacionSistema() {
    global $pdo;
    try {
        $stmt = $pdo->query("SELECT * FROM informacion_sistema WHERE id = 1 LIMIT 1");
        $row = $stmt ? $stmt->fetch(PDO::FETCH_ASSOC) : null;
        return $row ?: null;
    } catch (Exception $e) {
        return null;
    }
}

/**
 * Obtiene los detalles adicionales de un evento por tipo
 * 
 * @param int $eventoId ID del evento base
 * @param string $tipoEventoEspecifico Tipo del evento específico (boda, cumpleanos, etc.)
 * @param string $tipoDetalle Tipo de detalle (hospedaje, alimentacion, pdf, transporte, otro)
 * @return array Array de detalles ordenados
 */
function obtenerDetallesAdicionales($eventoId, $tipoEventoEspecifico, $tipoDetalle) {
    global $pdo;
    
    try {
        // Obtener el ID del evento específico
        $eventoEspecificoId = null;
        switch ($tipoEventoEspecifico) {
            case 'boda':
                $stmt = $pdo->prepare("SELECT id FROM eventos_boda WHERE evento_id = ? LIMIT 1");
                $stmt->execute([$eventoId]);
                $result = $stmt->fetch(PDO::FETCH_ASSOC);
                $eventoEspecificoId = $result['id'] ?? null;
                break;
            case 'cumpleanos':
                $stmt = $pdo->prepare("SELECT id FROM eventos_cumpleanos WHERE evento_id = ? LIMIT 1");
                $stmt->execute([$eventoId]);
                $result = $stmt->fetch(PDO::FETCH_ASSOC);
                $eventoEspecificoId = $result['id'] ?? null;
                break;
            case 'quinceanera':
                $stmt = $pdo->prepare("SELECT id FROM eventos_quinceanera WHERE evento_id = ? LIMIT 1");
                $stmt->execute([$eventoId]);
                $result = $stmt->fetch(PDO::FETCH_ASSOC);
                $eventoEspecificoId = $result['id'] ?? null;
                break;
            case 'bautizo':
                $stmt = $pdo->prepare("SELECT id FROM eventos_bautizo WHERE evento_id = ? LIMIT 1");
                $stmt->execute([$eventoId]);
                $result = $stmt->fetch(PDO::FETCH_ASSOC);
                $eventoEspecificoId = $result['id'] ?? null;
                break;
            case 'evento_social':
                $stmt = $pdo->prepare("SELECT id FROM eventos_evento_social WHERE evento_id = ? LIMIT 1");
                $stmt->execute([$eventoId]);
                $result = $stmt->fetch(PDO::FETCH_ASSOC);
                $eventoEspecificoId = $result['id'] ?? null;
                break;
        }
        
        if (!$eventoEspecificoId) {
            return [];
        }
        
        // Obtener las relaciones de detalles adicionales
        $stmt = $pdo->prepare("
            SELECT eda.*, eda.detalle_id as detalle_real_id
            FROM eventos_detalles_adicionales eda
            WHERE eda.evento_id = ? 
              AND eda.evento_especifico_id = ?
              AND eda.tipo_evento_especifico = ?
              AND eda.tipo_detalle = ?
              AND eda.activo = 1
            ORDER BY eda.orden ASC
        ");
        $stmt->execute([$eventoId, $eventoEspecificoId, $tipoEventoEspecifico, $tipoDetalle]);
        $relaciones = $stmt->fetchAll(PDO::FETCH_ASSOC);
        
        if (empty($relaciones)) {
            return [];
        }
        
        $detalles = [];
        foreach ($relaciones as $relacion) {
            $detalleId = $relacion['detalle_real_id'];
            $detalle = null;
            
            switch ($tipoDetalle) {
                case 'hospedaje':
                    $stmt = $pdo->prepare("SELECT * FROM eventos_hospedaje WHERE id = ? AND activo = 1 LIMIT 1");
                    $stmt->execute([$detalleId]);
                    $detalle = $stmt->fetch(PDO::FETCH_ASSOC);
                    if ($detalle) {
                        $detalle['botones'] = obtenerHospedajeBotones($detalleId);
                    }
                    break;
                case 'alimentacion':
                    $stmt = $pdo->prepare("SELECT * FROM eventos_alimentacion WHERE id = ? AND activo = 1 LIMIT 1");
                    $stmt->execute([$detalleId]);
                    $detalle = $stmt->fetch(PDO::FETCH_ASSOC);
                    break;
                case 'pdf':
                    $stmt = $pdo->prepare("SELECT * FROM eventos_pdf_detalles WHERE id = ? AND activo = 1 LIMIT 1");
                    $stmt->execute([$detalleId]);
                    $detalle = $stmt->fetch(PDO::FETCH_ASSOC);
                    break;
                case 'transporte':
                    $stmt = $pdo->prepare("SELECT * FROM eventos_transporte WHERE id = ? AND activo = 1 LIMIT 1");
                    $stmt->execute([$detalleId]);
                    $detalle = $stmt->fetch(PDO::FETCH_ASSOC);
                    break;
                case 'otro':
                    $stmt = $pdo->prepare("SELECT * FROM eventos_otros_detalles WHERE id = ? AND activo = 1 LIMIT 1");
                    $stmt->execute([$detalleId]);
                    $detalle = $stmt->fetch(PDO::FETCH_ASSOC);
                    break;
            }
            
            if ($detalle) {
                $detalle['relacion_id'] = $relacion['id'];
                $detalle['activo'] = $relacion['activo'];
                $detalle['orden'] = $relacion['orden'];
                $detalles[] = $detalle;
            }
        }
        
        return $detalles;
    } catch (Exception $e) {
        error_log("Error al obtener detalles adicionales: " . $e->getMessage());
        return [];
    }
}

/**
 * Obtiene los botones de un hotel
 * 
 * @param int $hospedajeId ID del hotel
 * @return array Array de botones ordenados
 */
function obtenerHospedajeBotones($hospedajeId) {
    global $pdo;
    
    try {
        $stmt = $pdo->prepare("
            SELECT * FROM eventos_hospedaje_botones 
            WHERE hospedaje_id = ? AND activo = 1 
            ORDER BY orden ASC
        ");
        $stmt->execute([$hospedajeId]);
        return $stmt->fetchAll(PDO::FETCH_ASSOC);
    } catch (Exception $e) {
        error_log("Error al obtener botones de hospedaje: " . $e->getMessage());
        return [];
    }
}

/**
 * Obtiene la lista de templates disponibles
 * 
 * @param string $tipo Tipo de template: 'default' o 'custom'
 * @return array Array de templates con sus metadatos
 */
function obtenerTemplatesDisponibles($tipo = 'default') {
    $templates = [];
    $basePath = __DIR__ . '/../templates/invitacion/' . $tipo;
    
    if (!is_dir($basePath)) {
        return [];
    }
    
    $directorios = scandir($basePath);
    foreach ($directorios as $dir) {
        if ($dir === '.' || $dir === '..') {
            continue;
        }
        
        $templatePath = $basePath . '/' . $dir;
        if (!is_dir($templatePath)) {
            continue;
        }
        
        $jsonPath = $templatePath . '/template.json';
        if (file_exists($jsonPath)) {
            $jsonContent = file_get_contents($jsonPath);
            $templateData = json_decode($jsonContent, true);
            
            if ($templateData) {
                $templateData['directorio'] = $dir;
                $templateData['tipo'] = $tipo;
                $templateData['ruta'] = $templatePath;
                
                // Verificar si existe preview
                $previewPath = $templatePath . '/preview.jpg';
                if (file_exists($previewPath)) {
                    $templateData['preview'] = 'templates/invitacion/' . $tipo . '/' . $dir . '/preview.jpg';
                }
                
                $templates[] = $templateData;
            }
        }
    }
    
    return $templates;
}

/**
 * Obtiene la configuración del template para un evento
 * 
 * @param int $eventoId ID del evento
 * @return array|null Configuración del template o null si no existe
 */
function obtenerConfiguracionTemplate($eventoId) {
    global $pdo;
    
    try {
        $stmt = $pdo->prepare("
            SELECT template_id, template_tipo, configuracion 
            FROM eventos_template_config 
            WHERE evento_id = ? 
            LIMIT 1
        ");
        $stmt->execute([$eventoId]);
        $result = $stmt->fetch(PDO::FETCH_ASSOC);
        
        if ($result) {
            $config = json_decode($result['configuracion'], true);
            return [
                'template_id' => $result['template_id'],
                'template_tipo' => $result['template_tipo'],
                'config' => $config ?: []
            ];
        }
        
        return null;
    } catch (Exception $e) {
        error_log("Error al obtener configuración de template: " . $e->getMessage());
        return null;
    }
}

/**
 * Carga un template y lo renderiza con los datos proporcionados
 * 
 * @param string $templateId ID del template (ej: 'template-1')
 * @param string $templateTipo Tipo del template ('default' o 'custom')
 * @param array $evento Datos del evento
 * @param array $invitado Datos del invitado
 * @param array $config Configuración del template
 * @param bool $isPreview Si es vista previa
 * @param array $admisiones Array de acompañantes
 * @param array $tracklist Array de canciones
 * @param string $token Token de la invitación
 * @param int $invitadoId ID del invitado
 * @return string HTML renderizado del template
 */
function cargarTemplate($templateId, $templateTipo, $evento, $invitado, $config = [], $isPreview = false, $admisiones = [], $tracklist = [], $token = '', $invitadoId = 0) {
    $basePath = __DIR__ . '/../templates/invitacion/' . $templateTipo . '/' . $templateId;
    
    if (!is_dir($basePath)) {
        error_log("Template no encontrado: $templateId en $templateTipo");
        return '<div style="padding: 20px; text-align: center; color: red;">Error: Template no encontrado</div>';
    }
    
    $templateFile = $basePath . '/template.php';
    if (!file_exists($templateFile)) {
        error_log("Archivo template.php no encontrado en: $basePath");
        return '<div style="padding: 20px; text-align: center; color: red;">Error: Archivo del template no encontrado</div>';
    }
    
    // Preparar variables para el template
    // Ruta relativa desde public/invitacion/ hacia templates/
    $templatePath = '../../templates/invitacion/' . $templateTipo . '/' . $templateId;
    
    // Cargar valores por defecto del template.json si no hay configuración
    $jsonPath = $basePath . '/template.json';
    $defaultConfig = [];
    if (file_exists($jsonPath)) {
        $jsonContent = file_get_contents($jsonPath);
        $templateData = json_decode($jsonContent, true);
        if ($templateData && isset($templateData['campos_editables'])) {
            // Extraer valores por defecto
            foreach ($templateData['campos_editables'] as $categoria => $campos) {
                foreach ($campos as $campoId => $campoData) {
                    if (isset($campoData['default'])) {
                        if (!isset($defaultConfig[$categoria])) {
                            $defaultConfig[$categoria] = [];
                        }
                        $defaultConfig[$categoria][$campoId] = $campoData['default'];
                    }
                }
            }
        }
    }
    
    // Combinar configuración: primero defaults, luego la config personalizada
    $finalConfig = array_merge_recursive($defaultConfig, $config);
    
    // Preparar todas las variables para el template
    // El template espera $config, no $finalConfig
    $config = $finalConfig;
    
    // Capturar la salida del template
    ob_start();
    include $templateFile;
    $output = ob_get_clean();
    
    return $output;
}

/**
 * Guarda o actualiza la configuración del template para un evento
 * 
 * @param int $eventoId ID del evento
 * @param string $templateId ID del template
 * @param string $templateTipo Tipo del template ('default' o 'custom')
 * @param array $config Configuración del template (se serializa a JSON)
 * @return bool True si se guardó correctamente
 */
function guardarConfiguracionTemplate($eventoId, $templateId, $templateTipo, $config = []) {
    global $pdo;
    
    try {
        $configJson = json_encode($config, JSON_UNESCAPED_UNICODE);
        
        // Verificar si ya existe configuración
        $stmt = $pdo->prepare("SELECT id FROM eventos_template_config WHERE evento_id = ?");
        $stmt->execute([$eventoId]);
        $existe = $stmt->fetch();
        
        if ($existe) {
            // Actualizar
            $stmt = $pdo->prepare("
                UPDATE eventos_template_config 
                SET template_id = ?, template_tipo = ?, configuracion = ? 
                WHERE evento_id = ?
            ");
            $stmt->execute([$templateId, $templateTipo, $configJson, $eventoId]);
        } else {
            // Insertar
            $stmt = $pdo->prepare("
                INSERT INTO eventos_template_config (evento_id, template_id, template_tipo, configuracion) 
                VALUES (?, ?, ?, ?)
            ");
            $stmt->execute([$eventoId, $templateId, $templateTipo, $configJson]);
        }
        
        return true;
    } catch (Exception $e) {
        error_log("Error al guardar configuración de template: " . $e->getMessage());
        return false;
    }
}


/**
 * Procesa y valida un template personalizado subido como ZIP
 * 
 * @param array $archivo Array $_FILES del archivo ZIP
 * @return array Resultado con 'success' y 'message'
 */
function procesarTemplatePersonalizado($archivo) {
    // Validar tipo de archivo
    $tipoArchivo = mime_content_type($archivo['tmp_name']);
    $extension = strtolower(pathinfo($archivo['name'], PATHINFO_EXTENSION));
    
    if ($extension !== 'zip' && $tipoArchivo !== 'application/zip' && $tipoArchivo !== 'application/x-zip-compressed') {
        return ['success' => false, 'message' => 'El archivo debe ser un ZIP válido.'];
    }
    
    // Validar tamaño (máximo 10MB)
    $tamañoMaximo = 10 * 1024 * 1024; // 10MB
    if ($archivo['size'] > $tamañoMaximo) {
        return ['success' => false, 'message' => 'El archivo es demasiado grande. Máximo 10MB.'];
    }
    
    // Crear directorio temporal
    $tempDir = sys_get_temp_dir() . '/template_upload_' . uniqid();
    if (!mkdir($tempDir, 0755, true)) {
        return ['success' => false, 'message' => 'Error al crear directorio temporal.'];
    }
    
    try {
        // Extraer ZIP
        $zip = new ZipArchive();
        if ($zip->open($archivo['tmp_name']) !== TRUE) {
            return ['success' => false, 'message' => 'Error al abrir el archivo ZIP.'];
        }
        
        $zip->extractTo($tempDir);
        $zip->close();
        
        // Validar estructura del template
        $validacion = validarEstructuraTemplate($tempDir);
        if (!$validacion['valid']) {
            eliminarDirectorio($tempDir);
            return ['success' => false, 'message' => $validacion['error']];
        }
        
        // Leer template.json para obtener ID
        $jsonPath = $tempDir . '/template.json';
        $jsonContent = file_get_contents($jsonPath);
        $templateData = json_decode($jsonContent, true);
        
        if (!$templateData || !isset($templateData['id'])) {
            eliminarDirectorio($tempDir);
            return ['success' => false, 'message' => 'El template.json no es válido o no contiene un ID.'];
        }
        
        $templateId = sanitize($templateData['id']);
        
        // Validar que el ID sea válido (solo letras, números, guiones y guiones bajos)
        if (!preg_match('/^[a-z0-9_-]+$/i', $templateId)) {
            eliminarDirectorio($tempDir);
            return ['success' => false, 'message' => 'El ID del template solo puede contener letras, números, guiones y guiones bajos.'];
        }
        
        // Directorio destino
        $destinoDir = __DIR__ . '/../templates/invitacion/custom/' . $templateId;
        
        // Si ya existe, eliminar versión anterior
        if (is_dir($destinoDir)) {
            eliminarDirectorio($destinoDir);
        }
        
        // Crear directorio destino
        if (!mkdir($destinoDir, 0755, true)) {
            eliminarDirectorio($tempDir);
            return ['success' => false, 'message' => 'Error al crear el directorio de destino.'];
        }
        
        // Mover archivos
        if (!copiarDirectorio($tempDir, $destinoDir)) {
            eliminarDirectorio($tempDir);
            eliminarDirectorio($destinoDir);
            return ['success' => false, 'message' => 'Error al copiar los archivos del template.'];
        }
        
        // Limpiar directorio temporal
        eliminarDirectorio($tempDir);
        
        return ['success' => true, 'message' => 'Template personalizado subido correctamente. ID: ' . $templateId];
        
    } catch (Exception $e) {
        eliminarDirectorio($tempDir);
        error_log("Error al procesar template personalizado: " . $e->getMessage());
        return ['success' => false, 'message' => 'Error inesperado al procesar el template: ' . $e->getMessage()];
    }
}

/**
 * Valida la estructura de un template
 * 
 * @param string $directorio Directorio del template extraído
 * @return array Resultado con 'valid' y opcionalmente 'error'
 */
function validarEstructuraTemplate($directorio) {
    $archivosRequeridos = ['template.php', 'template.css', 'template.json'];
    
    foreach ($archivosRequeridos as $archivo) {
        $ruta = $directorio . '/' . $archivo;
        if (!file_exists($ruta)) {
            return ['valid' => false, 'error' => "Archivo requerido no encontrado: $archivo"];
        }
    }
    
    // Validar que template.json sea JSON válido
    $jsonPath = $directorio . '/template.json';
    $jsonContent = file_get_contents($jsonPath);
    $templateData = json_decode($jsonContent, true);
    
    if (json_last_error() !== JSON_ERROR_NONE) {
        return ['valid' => false, 'error' => 'El archivo template.json no es un JSON válido.'];
    }
    
    // Validar campos mínimos en template.json
    $camposRequeridos = ['id', 'nombre'];
    foreach ($camposRequeridos as $campo) {
        if (!isset($templateData[$campo])) {
            return ['valid' => false, 'error' => "El template.json debe contener el campo: $campo"];
        }
    }
    
    return ['valid' => true];
}

/**
 * Elimina un template personalizado
 * 
 * @param string $templateId ID del template a eliminar
 * @return array Resultado con 'success' y 'message'
 */
function eliminarTemplatePersonalizado($templateId) {
    $directorio = __DIR__ . '/../templates/invitacion/custom/' . $templateId;
    
    // Validar que el template existe y es personalizado
    if (!is_dir($directorio)) {
        return ['success' => false, 'message' => 'El template no existe.'];
    }
    
    // Validar que no esté en uso
    global $pdo;
    try {
        $stmt = $pdo->prepare("SELECT COUNT(*) as count FROM eventos_template_config WHERE template_id = ? AND template_tipo = 'custom'");
        $stmt->execute([$templateId]);
        $result = $stmt->fetch(PDO::FETCH_ASSOC);
        
        if ($result && $result['count'] > 0) {
            return ['success' => false, 'message' => 'No se puede eliminar el template porque está en uso por uno o más eventos.'];
        }
    } catch (Exception $e) {
        error_log("Error al verificar uso del template: " . $e->getMessage());
    }
    
    // Eliminar directorio
    if (eliminarDirectorio($directorio)) {
        return ['success' => true, 'message' => 'Template eliminado correctamente.'];
    } else {
        return ['success' => false, 'message' => 'Error al eliminar el template.'];
    }
}

/**
 * Elimina un directorio y todo su contenido recursivamente
 * 
 * @param string $directorio Directorio a eliminar
 * @return bool True si se eliminó correctamente
 */
function eliminarDirectorio($directorio) {
    if (!is_dir($directorio)) {
        return false;
    }
    
    $archivos = array_diff(scandir($directorio), ['.', '..']);
    
    foreach ($archivos as $archivo) {
        $ruta = $directorio . '/' . $archivo;
        if (is_dir($ruta)) {
            eliminarDirectorio($ruta);
        } else {
            unlink($ruta);
        }
    }
    
    return rmdir($directorio);
}

/**
 * Copia un directorio y todo su contenido recursivamente
 * 
 * @param string $origen Directorio origen
 * @param string $destino Directorio destino
 * @return bool True si se copió correctamente
 */
function copiarDirectorio($origen, $destino) {
    if (!is_dir($origen)) {
        return false;
    }
    
    if (!is_dir($destino)) {
        if (!mkdir($destino, 0755, true)) {
            return false;
        }
    }
    
    $archivos = array_diff(scandir($origen), ['.', '..']);
    
    foreach ($archivos as $archivo) {
        $origenRuta = $origen . '/' . $archivo;
        $destinoRuta = $destino . '/' . $archivo;
        
        if (is_dir($origenRuta)) {
            if (!copiarDirectorio($origenRuta, $destinoRuta)) {
                return false;
            }
        } else {
            if (!copy($origenRuta, $destinoRuta)) {
                return false;
            }
        }
    }
    
    return true;
}

// ============================================
// FUNCIONES DE PERSONALIZACIÓN DE TEMPLATES
// ============================================

/**
 * Obtiene todas las personalizaciones del template para un evento
 * 
 * @param int $eventoId ID del evento
 * @return array Array asociativo con las personalizaciones [seccion][elemento] = valor
 */
function obtenerPersonalizacionesTemplate($eventoId) {
    global $pdo;
    
    try {
        $stmt = $pdo->prepare("
            SELECT seccion, elemento, tipo, valor 
            FROM eventos_template_personalizacion 
            WHERE evento_id = ? AND activo = 1
        ");
        $stmt->execute([$eventoId]);
        $resultados = $stmt->fetchAll(PDO::FETCH_ASSOC);
        
        $personalizaciones = [];
        foreach ($resultados as $row) {
            if (!isset($personalizaciones[$row['seccion']])) {
                $personalizaciones[$row['seccion']] = [];
            }
            $personalizaciones[$row['seccion']][$row['elemento']] = [
                'tipo' => $row['tipo'],
                'valor' => $row['valor']
            ];
        }
        
        return $personalizaciones;
    } catch (Exception $e) {
        error_log("Error al obtener personalizaciones template: " . $e->getMessage());
        return [];
    }
}

/**
 * Obtiene una personalización específica del template
 * 
 * @param int $eventoId ID del evento
 * @param string $seccion Nombre de la sección
 * @param string $elemento Nombre del elemento
 * @param mixed $valorDefecto Valor por defecto si no existe personalización
 * @return mixed El valor personalizado o el valor por defecto
 */
function obtenerPersonalizacionElemento($eventoId, $seccion, $elemento, $valorDefecto = null) {
    global $pdo;
    
    try {
        $stmt = $pdo->prepare("
            SELECT valor 
            FROM eventos_template_personalizacion 
            WHERE evento_id = ? AND seccion = ? AND elemento = ? AND activo = 1
            LIMIT 1
        ");
        $stmt->execute([$eventoId, $seccion, $elemento]);
        $resultado = $stmt->fetch(PDO::FETCH_ASSOC);
        
        return $resultado ? $resultado['valor'] : $valorDefecto;
    } catch (Exception $e) {
        error_log("Error al obtener personalización elemento: " . $e->getMessage());
        return $valorDefecto;
    }
}

/**
 * Guarda o actualiza una personalización del template
 * 
 * @param int $eventoId ID del evento
 * @param string $seccion Nombre de la sección
 * @param string $elemento Nombre del elemento
 * @param string $tipo Tipo de personalización (texto, imagen, color)
 * @param string $valor Valor de la personalización
 * @return bool True si se guardó correctamente
 */
function guardarPersonalizacionTemplate($eventoId, $seccion, $elemento, $tipo, $valor) {
    global $pdo;
    
    try {
        // Usar INSERT ... ON DUPLICATE KEY UPDATE para insertar o actualizar
        $stmt = $pdo->prepare("
            INSERT INTO eventos_template_personalizacion 
                (evento_id, seccion, elemento, tipo, valor, activo) 
            VALUES (?, ?, ?, ?, ?, 1)
            ON DUPLICATE KEY UPDATE 
                tipo = VALUES(tipo),
                valor = VALUES(valor),
                activo = 1,
                fecha_actualizacion = CURRENT_TIMESTAMP
        ");
        return $stmt->execute([$eventoId, $seccion, $elemento, $tipo, $valor]);
    } catch (Exception $e) {
        error_log("Error al guardar personalización template: " . $e->getMessage());
        return false;
    }
}

/**
 * Guarda múltiples personalizaciones de una vez
 * 
 * @param int $eventoId ID del evento
 * @param array $personalizaciones Array de personalizaciones [['seccion' => ..., 'elemento' => ..., 'tipo' => ..., 'valor' => ...], ...]
 * @return bool True si se guardaron todas correctamente
 */
function guardarPersonalizacionesTemplate($eventoId, $personalizaciones) {
    global $pdo;
    
    try {
        $pdo->beginTransaction();
        
        foreach ($personalizaciones as $p) {
            if (!guardarPersonalizacionTemplate(
                $eventoId,
                $p['seccion'],
                $p['elemento'],
                $p['tipo'] ?? 'texto',
                $p['valor']
            )) {
                $pdo->rollBack();
                return false;
            }
        }
        
        $pdo->commit();
        return true;
    } catch (Exception $e) {
        $pdo->rollBack();
        error_log("Error al guardar personalizaciones template: " . $e->getMessage());
        return false;
    }
}

/**
 * Elimina una personalización específica (la desactiva)
 * 
 * @param int $eventoId ID del evento
 * @param string $seccion Nombre de la sección
 * @param string $elemento Nombre del elemento
 * @return bool True si se eliminó correctamente
 */
function eliminarPersonalizacionTemplate($eventoId, $seccion, $elemento) {
    global $pdo;
    
    try {
        $stmt = $pdo->prepare("
            UPDATE eventos_template_personalizacion 
            SET activo = 0, fecha_actualizacion = CURRENT_TIMESTAMP
            WHERE evento_id = ? AND seccion = ? AND elemento = ?
        ");
        return $stmt->execute([$eventoId, $seccion, $elemento]);
    } catch (Exception $e) {
        error_log("Error al eliminar personalización template: " . $e->getMessage());
        return false;
    }
}

/**
 * Elimina todas las personalizaciones de una sección
 * 
 * @param int $eventoId ID del evento
 * @param string $seccion Nombre de la sección
 * @return bool True si se eliminaron correctamente
 */
function eliminarPersonalizacionesSeccion($eventoId, $seccion) {
    global $pdo;
    
    try {
        $stmt = $pdo->prepare("
            UPDATE eventos_template_personalizacion 
            SET activo = 0, fecha_actualizacion = CURRENT_TIMESTAMP
            WHERE evento_id = ? AND seccion = ?
        ");
        return $stmt->execute([$eventoId, $seccion]);
    } catch (Exception $e) {
        error_log("Error al eliminar personalizaciones de sección: " . $e->getMessage());
        return false;
    }
}

/**
 * Restaura todas las personalizaciones a valores por defecto (elimina todas)
 * 
 * @param int $eventoId ID del evento
 * @return bool True si se restauraron correctamente
 */
function restaurarPersonalizacionesTemplate($eventoId) {
    global $pdo;
    
    try {
        $stmt = $pdo->prepare("
            UPDATE eventos_template_personalizacion 
            SET activo = 0, fecha_actualizacion = CURRENT_TIMESTAMP
            WHERE evento_id = ?
        ");
        return $stmt->execute([$eventoId]);
    } catch (Exception $e) {
        error_log("Error al restaurar personalizaciones template: " . $e->getMessage());
        return false;
    }
}

/**
 * Obtiene la lista de elementos editables por sección para un template específico
 * 
 * @param string $templateId ID del template (ej: 'template-1')
 * @param string $tipo Tipo de template: 'default' o 'custom'
 * @return array Array con la definición de elementos editables por sección
 */
function obtenerElementosEditablesTemplate($templateId = 'template-1', $tipo = 'default') {
    // Construir ruta al archivo de configuración del template
    $basePath = __DIR__ . '/../templates/invitacion/' . $tipo . '/' . $templateId . '/template.json';
    
    // Si existe el archivo JSON del template, leer desde ahí
    if (file_exists($basePath)) {
        $jsonContent = file_get_contents($basePath);
        $templateConfig = json_decode($jsonContent, true);
        
        if ($templateConfig && isset($templateConfig['elementos_editables'])) {
            // Convertir formato JSON a formato esperado por el sistema
            $elementos = [];
            foreach ($templateConfig['elementos_editables'] as $seccion => $config) {
                $elementos[$seccion] = [
                    'titulo' => $config['titulo'] ?? $seccion,
                    'elementos' => []
                ];
                
                if (isset($config['elementos'])) {
                    foreach ($config['elementos'] as $elemId => $elemConfig) {
                        $elementos[$seccion]['elementos'][$elemId] = [
                            'tipo' => $elemConfig['tipo'] ?? 'texto',
                            'label' => $elemConfig['label'] ?? $elemId,
                            'descripcion' => $elemConfig['descripcion'] ?? ''
                        ];
                    }
                }
            }
            return $elementos;
        }
    }
    
    // Fallback: devolver elementos por defecto si no se encuentra el JSON
    return obtenerElementosEditablesTemplateDefault();
}

/**
 * Elementos editables por defecto (fallback)
 * 
 * @return array Array con la definición de elementos editables por defecto
 */
function obtenerElementosEditablesTemplateDefault() {
    return [
        'section-1' => [
            'titulo' => 'Sección 1 - Portada',
            'elementos' => [
                'background-image' => ['tipo' => 'imagen', 'label' => 'Imagen de fondo', 'descripcion' => 'Imagen principal de la portada']
            ]
        ],
        'section-2' => [
            'titulo' => 'Sección 2 - Información Principal',
            'elementos' => [
                'titulo-principal' => ['tipo' => 'texto', 'label' => 'Título principal', 'descripcion' => 'Texto del título (ej: nombres de los novios)'],
                'subtitulo' => ['tipo' => 'texto', 'label' => 'Subtítulo', 'descripcion' => 'Texto debajo del título'],
                'mensaje' => ['tipo' => 'texto', 'label' => 'Mensaje', 'descripcion' => 'Mensaje o frase especial']
            ]
        ],
        'section-3' => [
            'titulo' => 'Sección 3 - Galería/Video',
            'elementos' => [
                'video-principal' => ['tipo' => 'imagen', 'label' => 'Video principal', 'descripcion' => 'Video que se muestra en esta sección']
            ]
        ],
        'section-5' => [
            'titulo' => 'Sección 5 - Imagen Destacada',
            'elementos' => [
                'imagen' => ['tipo' => 'imagen', 'label' => 'Imagen destacada', 'descripcion' => 'Imagen en formato 16:9']
            ]
        ],
        'section-6' => [
            'titulo' => 'Sección 6 - Dress Code',
            'elementos' => [
                'titulo' => ['tipo' => 'texto', 'label' => 'Título', 'descripcion' => 'Título de la sección (ej: DressCode)'],
                'subtitulo' => ['tipo' => 'texto', 'label' => 'Subtítulo', 'descripcion' => 'Texto descriptivo debajo del título'],
                'texto-colores' => ['tipo' => 'texto', 'label' => 'Texto de colores reservados', 'descripcion' => 'Mensaje sobre los colores a evitar']
            ]
        ],
        'section-7' => [
            'titulo' => 'Sección 7 - Ubicación',
            'elementos' => [
                'titulo' => ['tipo' => 'texto', 'label' => 'Título', 'descripcion' => 'Título de la sección (ej: Ubicación)'],
                'subtitulo' => ['tipo' => 'texto', 'label' => 'Subtítulo', 'descripcion' => 'Texto descriptivo (ej: Descubre cómo llegar...)'],
                'texto-boton' => ['tipo' => 'texto', 'label' => 'Texto del botón', 'descripcion' => 'Texto del botón de Google Maps']
            ]
        ],
        'section-9' => [
            'titulo' => 'Sección 9 - Obsequio',
            'elementos' => [
                'titulo' => ['tipo' => 'texto', 'label' => 'Título', 'descripcion' => 'Título de la sección (ej: Agradecemos tu obsequio)']
            ]
        ],
        'section-10' => [
            'titulo' => 'Sección 10 - Playlist Musical',
            'elementos' => [
                'titulo' => ['tipo' => 'texto', 'label' => 'Título', 'descripcion' => 'Título de la sección (ej: Hagamos de esta noche, la mejor)'],
                'subtitulo' => ['tipo' => 'texto', 'label' => 'Subtítulo', 'descripcion' => 'Texto descriptivo (ej: Qué música te gustaría que no falte)']
            ]
        ],
        'section-11' => [
            'titulo' => 'Sección 11 - Información Adicional',
            'elementos' => [
                'titulo' => ['tipo' => 'texto', 'label' => 'Título', 'descripcion' => 'Título de la sección (ej: Información Adicional)']
            ]
        ],
        'footer' => [
            'titulo' => 'Footer',
            'elementos' => [
                'nombres' => ['tipo' => 'texto', 'label' => 'Nombres', 'descripcion' => 'Nombres en el footer (ej: María & Juan)']
            ]
        ]
    ];
}

/**
 * Obtiene la información de un template específico desde su archivo JSON
 */
function obtenerTemplateJson($templateId, $tipo = 'default') {
    $basePath = __DIR__ . '/../templates/invitacion/' . $tipo . '/' . $templateId . '/template.json';
    
    if (file_exists($basePath)) {
        $jsonContent = file_get_contents($basePath);
        return json_decode($jsonContent, true);
    }
    
    return null;
}

// ============================================
// FUNCIONES DE COLORES RESERVADOS DRESS CODE
// ============================================

/**
 * Obtiene los colores reservados del dress code de un evento
 * 
 * @param int $eventoId ID del evento
 * @return array Array de colores reservados
 */
function obtenerColoresReservadosDressCode($eventoId) {
    global $pdo;
    
    try {
        $stmt = $pdo->prepare("
            SELECT id, color, nombre, orden 
            FROM eventos_dresscode_colores 
            WHERE evento_id = ? AND activo = 1 
            ORDER BY orden ASC, id ASC
        ");
        $stmt->execute([$eventoId]);
        return $stmt->fetchAll(PDO::FETCH_ASSOC);
    } catch (Exception $e) {
        error_log("Error al obtener colores reservados dress code: " . $e->getMessage());
        return [];
    }
}

/**
 * Agrega un color reservado al dress code de un evento
 * 
 * @param int $eventoId ID del evento
 * @param string $color Código de color en formato #RRGGBB
 * @param string $nombre Nombre opcional del color
 * @param int $orden Orden de visualización
 * @return int|false ID del color insertado o false si falla
 */
function agregarColorReservadoDressCode($eventoId, $color, $nombre = null, $orden = 0) {
    global $pdo;
    
    try {
        // Validar formato del color
        if (!preg_match('/^#[0-9A-Fa-f]{6}$/', $color)) {
            error_log("Color inválido: $color");
            return false;
        }
        
        // Si no se especifica orden, obtener el siguiente
        if ($orden === 0) {
            $stmt = $pdo->prepare("SELECT COALESCE(MAX(orden), 0) + 1 as next_orden FROM eventos_dresscode_colores WHERE evento_id = ?");
            $stmt->execute([$eventoId]);
            $result = $stmt->fetch(PDO::FETCH_ASSOC);
            $orden = $result['next_orden'];
        }
        
        $stmt = $pdo->prepare("
            INSERT INTO eventos_dresscode_colores (evento_id, color, nombre, orden, activo) 
            VALUES (?, ?, ?, ?, 1)
        ");
        $stmt->execute([$eventoId, strtoupper($color), $nombre, $orden]);
        return $pdo->lastInsertId();
    } catch (Exception $e) {
        error_log("Error al agregar color reservado dress code: " . $e->getMessage());
        return false;
    }
}

/**
 * Actualiza un color reservado del dress code
 * 
 * @param int $colorId ID del color
 * @param string $color Código de color en formato #RRGGBB
 * @param string $nombre Nombre opcional del color
 * @return bool True si se actualizó correctamente
 */
function actualizarColorReservadoDressCode($colorId, $color, $nombre = null) {
    global $pdo;
    
    try {
        // Validar formato del color
        if (!preg_match('/^#[0-9A-Fa-f]{6}$/', $color)) {
            error_log("Color inválido: $color");
            return false;
        }
        
        $stmt = $pdo->prepare("
            UPDATE eventos_dresscode_colores 
            SET color = ?, nombre = ?, fecha_actualizacion = CURRENT_TIMESTAMP
            WHERE id = ?
        ");
        return $stmt->execute([strtoupper($color), $nombre, $colorId]);
    } catch (Exception $e) {
        error_log("Error al actualizar color reservado dress code: " . $e->getMessage());
        return false;
    }
}

/**
 * Elimina un color reservado del dress code
 * 
 * @param int $colorId ID del color
 * @return bool True si se eliminó correctamente
 */
function eliminarColorReservadoDressCode($colorId) {
    global $pdo;
    
    try {
        $stmt = $pdo->prepare("DELETE FROM eventos_dresscode_colores WHERE id = ?");
        return $stmt->execute([$colorId]);
    } catch (Exception $e) {
        error_log("Error al eliminar color reservado dress code: " . $e->getMessage());
        return false;
    }
}

/**
 * Reordena los colores reservados del dress code
 * 
 * @param int $eventoId ID del evento
 * @param array $ordenColores Array de IDs de colores en el nuevo orden
 * @return bool True si se reordenó correctamente
 */
function reordenarColoresReservadosDressCode($eventoId, $ordenColores) {
    global $pdo;
    
    try {
        $pdo->beginTransaction();
        
        foreach ($ordenColores as $orden => $colorId) {
            $stmt = $pdo->prepare("
                UPDATE eventos_dresscode_colores 
                SET orden = ?, fecha_actualizacion = CURRENT_TIMESTAMP
                WHERE id = ? AND evento_id = ?
            ");
            $stmt->execute([$orden + 1, $colorId, $eventoId]);
        }
        
        $pdo->commit();
        return true;
    } catch (Exception $e) {
        $pdo->rollBack();
        error_log("Error al reordenar colores reservados dress code: " . $e->getMessage());
        return false;
    }
}

