Source: controllers/c_crudPruebasTO.js

/**
 * @fileoverview Gestión de pruebas: añadir, editar y borrar pruebas con validaciones y control de interfaz.
 * @author Mauricio Peña
 */

import M_crudPruebasTO from "/InscripcionesEVG/assets/js/models/m_crudPruebasTO.js";
import { renderizarPruebas } from "/InscripcionesEVG/assets/js/controllers/c_obtenerPruebas.js";
import { ErrorDialog } from "/InscripcionesEVG/assets/js/utils/errorHandler.js";
import { Loader } from "/InscripcionesEVG/assets/js/utils/loader.js";
import M_obtenerPruebas from "/InscripcionesEVG/assets/js/models/m_obtenerPruebas.js";

/** @type {ErrorDialog} */
const errorDialog = new ErrorDialog();

const btnAceptar = document.getElementById("aceptar");
const btnConfirmar = document.getElementById("btnConfirmar");

/**
 * Evento para añadir o editar una prueba.
 * Realiza validaciones de campos, controla duplicados y maneja la petición al backend.
 * @param {MouseEvent} event - Evento del clic en el botón aceptar.
 * @author Mauricio Peña
 */
btnAceptar?.addEventListener("click", async function (event) {
	event.preventDefault();

	const tipoAccion = btnAceptar.getAttribute("data-tipo");

	const modelo = new M_crudPruebasTO();

	switch (tipoAccion) {
		case "añadir":
		case "editar":
			/** @type {string} */
			const idPruebaM = document.getElementById("idPruebaM")?.value ?? "";

			/** @type {string} */
			const idPruebaF = document.getElementById("idPruebaF")?.value ?? "";

			const nombrePrueba = document.getElementById("nombrePrueba").value;
			const bases = document.getElementById("bases").value;
			const maxParticipantes =
				document.getElementById("maxParticipantes").value;
			const fechaPrueba = document.getElementById("fechaPrueba").value ?? "";
			const horaPrueba = document.getElementById("horaPrueba").value ?? "";

			if (
				!nombrePrueba ||
				!bases ||
				!maxParticipantes ||
				maxParticipantes === "0"
			) {
				errorDialog.show("Faltan campos por rellenar.");
				return;
			}
			if (
				contieneCaracteresPeligrosos(nombrePrueba) ||
				contieneTagsHTML(nombrePrueba) ||
				contieneCaracteresPeligrosos(bases) ||
				contieneTagsHTML(bases)
			) {
				errorDialog.show("No se permiten caracteres o etiquetas HTML.");
				return;
			}
			/**
			 * Objeto que representa una prueba.
			 * @type {{ idPruebaM: string, idPruebaF: string, nombre: string, bases: string, tipo: string, maxParticipantes: string, fecha: string, hora: string }}
			 */
			const prueba = {
				idPruebaM,
				idPruebaF,
				nombre: nombrePrueba,
				bases,
				maxParticipantes,
				fecha: fechaPrueba,
				hora: horaPrueba,
			};

			if (bases.length > 255) {
				errorDialog.show(
					"La descripción no puede tener más de 255 caracteres.",
				);
				return;
			} else if (nombrePrueba.length > 50) {
				errorDialog.show("El nombre no puede tener más de 50 caracteres.");
				return;
			}

			console.log(prueba);
			const loader = new Loader("Cargando...");

			// Validación de hora: debe estar entre 09:00 y 15:00
			if (horaPrueba) {
				const horaMinima = "09:00";
				const horaMaxima = "15:00";

				if (horaPrueba < horaMinima || horaPrueba > horaMaxima) {
					errorDialog.show("La hora debe estar entre las 09:00 y las 15:00.");
					loader.ocultar();
					return;
				}

				// Validación de duplicados por hora (independientemente de la fecha)
				const modeloPruebas = new M_obtenerPruebas();
				const pruebas = await modeloPruebas.obtenerPruebas();

				const existeMismaHora = pruebas.some((p) => {
					if (p.categoria !== "M") {
						return false;
					}

					if (
						tipoAccion === "editar" &&
						String(p.idPrueba) === String(idPruebaM)
					) {
						return false; // Ignorar la prueba que estamos editando
					}

					return p.hora === horaPrueba;
				});

				if (existeMismaHora) {
					errorDialog.show("Ya existe una prueba programada a esa hora.");
					loader.ocultar();
					return;
				}
			}
			try {
				if (tipoAccion === "añadir") {
					const resultado = await modelo.insertPrueba(JSON.stringify(prueba));
					if (!resultado.error) {
						await renderizarPruebas();
					}
				} else if (tipoAccion === "editar") {
					const resultado = await modelo.modificarPrueba(
						JSON.stringify(prueba),
					);
					if (!resultado.error) {
						await renderizarPruebas();
					}
				}
			} catch (error) {
				console.error("Error al insertar/editar las inscripciones", error);
			} finally {
				loader.ocultar();
				cerrarModal();
			}
			break;

		default:
			errorDialog.show("Acción desconocida");
			break;
	}
});

/**
 * Evento para confirmar el borrado de una prueba.
 * @param {MouseEvent} event - Evento del clic en el botón confirmar.
 * @author Mauricio Peña
 */
btnConfirmar?.addEventListener("click", async function (event) {
	event.preventDefault();
	console.log("Borrar confirmado");

	/** @type {string} */
	const idPruebaM = document.getElementById("idPruebaM")?.value ?? "";

	/** @type {string} */
	const idPruebaF = document.getElementById("idPruebaF")?.value ?? "";

	const jsonIds = JSON.stringify({ idPruebaM, idPruebaF });
	console.log(jsonIds);

	try {
		const modelo = new M_crudPruebasTO();
		const resultado = await modelo.borrarPrueba(jsonIds);
		if (!resultado.error) {
			await renderizarPruebas();
		}
	} catch (error) {
		errorDialog.show(error);
	} finally {
		cerrarModalConfirmacion();
	}
});

/**
 * Muestra el modal con el loader (indicador de carga).
 * @function
 * @author Mauricio Peña
 */
function mostrarLoaderModal() {
	document.getElementById("loader-modal").style.display = "flex";
}

/**
 * Oculta el modal con el loader.
 * @function
 * @author Mauricio Peña
 */
function ocultarLoaderModal() {
	document.getElementById("loader-modal").style.display = "none";
}

/**
 * Cierra el modal principal.
 * @function
 * @author Mauricio Peña
 */
function cerrarModal() {
	document.getElementById("modal").style.display = "none";
}

/**
 * Valida si un texto contiene caracteres peligrosos.
 * @param {string} texto - Texto a validar.
 * @returns {boolean} True si contiene caracteres peligrosos, false si no.
 * @author Mauricio Peña
 */
function contieneCaracteresPeligrosos(texto) {
	const regex = /[<>{}"'`´\\]/;
	return regex.test(texto);
}

/**
 * Valida si un texto contiene etiquetas HTML.
 * @param {string} texto - Texto a validar.
 * @returns {boolean} True si contiene etiquetas HTML, false si no.
 * @author Mauricio Peña
 */
function contieneTagsHTML(texto) {
	const tagRegex = /<\/?[a-z][\s\S]*>/i; // detecta <algo> o </algo>
	return tagRegex.test(texto);
}

/**
 * Cierra el modal de confirmación.
 * @function
 * @author Mauricio Peña
 */
function cerrarModalConfirmacion() {
	document.getElementById("modalConfirmacion").style.display = "none";
}