<?php
// concurso_nobel/calificaciones.php
// Registro de calificaciones con AUTOGUARDADO por fila.
// Ranking separado por PRIVADA y PÚBLICA.

declare(strict_types=1);

session_start();

require_once __DIR__ . '/includes/db.php';
require_once __DIR__ . '/includes/concurso_contexto.php';

function cal_json(bool $ok, string $mensaje, array $extra = []): void
{
    header('Content-Type: application/json; charset=utf-8');

    echo json_encode(array_merge([
        'ok' => $ok,
        'message' => $mensaje
    ], $extra), JSON_UNESCAPED_UNICODE);

    exit;
}

function cal_formato_tiempo(?int $segundos): ?string
{
    if ($segundos === null) {
        return null;
    }

    $h = intdiv($segundos, 3600);
    $m = intdiv($segundos % 3600, 60);
    $s = $segundos % 60;

    if ($h > 0) {
        return sprintf('%02d:%02d:%02d', $h, $m, $s);
    }

    return sprintf('%02d:%02d', $m, $s);
}

function cal_parse_tiempo(string $txt): ?int
{
    $txt = trim($txt);

    if ($txt === '') {
        return null;
    }

    if (preg_match('/^\d+$/', $txt)) {
        return (int)$txt;
    }

    if (preg_match('/^(\d{1,2}):(\d{2})$/', $txt, $m)) {
        return ((int)$m[1] * 60) + (int)$m[2];
    }

    if (preg_match('/^(\d{1,2}):(\d{2}):(\d{2})$/', $txt, $m)) {
        return ((int)$m[1] * 3600) + ((int)$m[2] * 60) + (int)$m[3];
    }

    return -1;
}

function cal_puntaje(int $correctas, int $incorrectas, int $blanco, float $pCorrecta, float $pIncorrecta, float $pBlanco): float
{
    return ($correctas * $pCorrecta) - ($incorrectas * $pIncorrecta) + ($blanco * $pBlanco);
}

function cal_recalcular_puestos_categoria(mysqli $conn, int $idConcurso, int $idGrupo, string $tipoInstitucion): void
{
    $stmt = mysqli_prepare($conn, "
        SELECT r.id_resultado
        FROM concurso_resultados r
        INNER JOIN concurso_participantes p
            ON p.id_participante = r.id_participante
        WHERE r.id_concurso = ?
          AND r.id_grupo = ?
          AND r.estado = 'ACTIVO'
          AND p.estado = 'ACTIVO'
          AND p.tipo_institucion = ?
        ORDER BY
          r.puntaje DESC,
          (r.tiempo_segundos IS NULL) ASC,
          r.tiempo_segundos ASC,
          r.correctas DESC,
          r.incorrectas ASC,
          r.id_resultado ASC
    ");

    if (!$stmt) {
        return;
    }

    mysqli_stmt_bind_param($stmt, 'iis', $idConcurso, $idGrupo, $tipoInstitucion);
    mysqli_stmt_execute($stmt);
    $res = mysqli_stmt_get_result($stmt);

    $puesto = 1;

    while ($row = mysqli_fetch_assoc($res)) {
        $idResultado = (int)$row['id_resultado'];

        $up = mysqli_prepare($conn, "
            UPDATE concurso_resultados
            SET puesto = ?
            WHERE id_resultado = ?
            LIMIT 1
        ");

        if ($up) {
            mysqli_stmt_bind_param($up, 'ii', $puesto, $idResultado);
            mysqli_stmt_execute($up);
            mysqli_stmt_close($up);
        }

        $puesto++;
    }

    mysqli_stmt_close($stmt);
}

function cal_recalcular_puestos_grupo(mysqli $conn, int $idConcurso, int $idGrupo): void
{
    cal_recalcular_puestos_categoria($conn, $idConcurso, $idGrupo, 'PRIVADA');
    cal_recalcular_puestos_categoria($conn, $idConcurso, $idGrupo, 'PUBLICA');
}

function cal_columna_existe(mysqli $conn, string $tabla, string $columna): bool
{
    $stmt = mysqli_prepare($conn, "
        SELECT COUNT(*) AS total
        FROM INFORMATION_SCHEMA.COLUMNS
        WHERE TABLE_SCHEMA = DATABASE()
          AND TABLE_NAME = ?
          AND COLUMN_NAME = ?
    ");

    if (!$stmt) {
        return false;
    }

    mysqli_stmt_bind_param($stmt, 'ss', $tabla, $columna);
    mysqli_stmt_execute($stmt);
    $res = mysqli_stmt_get_result($stmt);
    $row = mysqli_fetch_assoc($res);
    mysqli_stmt_close($stmt);

    return (int)($row['total'] ?? 0) > 0;
}

function cal_asegurar_columna_modificaciones(mysqli $conn): void
{
    if (cal_columna_existe($conn, 'concurso_resultados', 'modificacion_contador')) {
        return;
    }

    @mysqli_query($conn, "
        ALTER TABLE concurso_resultados
        ADD COLUMN modificacion_contador INT UNSIGNED NOT NULL DEFAULT 0 AFTER observacion
    ");
}

function cal_clase_modificacion_php(int $contador, bool $tieneResultado): string
{
    if (!$tieneResultado) {
        return '';
    }

    if ($contador <= 0) {
        return 'row-ok';
    }

    return 'row-mod-' . min($contador, 6);
}

function cal_estado_texto_php(int $contador, bool $tieneResultado): string
{
    if (!$tieneResultado) {
        return 'Nuevo';
    }

    if ($contador <= 0) {
        return 'Guardado';
    }

    return 'Mod. ' . $contador;
}

function cal_estado_clase_php(int $contador, bool $tieneResultado): string
{
    if (!$tieneResultado) {
        return 'pending';
    }

    return 'saved';
}

$acceso = cn_validar_acceso($conn, 'index.php');

if ((int)$acceso['puede_registrar_resultados'] !== 1 && !cn_es_admin($acceso)) {
    http_response_code(403);
    die('Tu código no tiene permiso para registrar calificaciones.');
}

$esAdmin = cn_es_admin($acceso);
$idConcurso = cn_concurso_trabajo($conn, $acceso);

if ($idConcurso <= 0) {
    http_response_code(500);
    die('No hay concurso activo disponible.');
}

cal_asegurar_columna_modificaciones($conn);

$concursoTrabajo = cn_datos_concurso($conn, $idConcurso);
$concursosActivos = $esAdmin ? cn_concursos_activos($conn) : [];
$idGrupoFijo = $esAdmin ? 0 : cn_id_grupo_fijo($acceso);
$idSalonFijo = $esAdmin ? 0 : cn_id_salon_fijo($acceso);

$grupos = cn_grupos_concurso($conn, $idConcurso, $idGrupoFijo);

$idGrupoSeleccionado = $idGrupoFijo > 0 ? $idGrupoFijo : (int)($_GET['id_grupo'] ?? ($_POST['id_grupo'] ?? 0));

if ($idGrupoSeleccionado <= 0 && !empty($grupos)) {
    $idGrupoSeleccionado = (int)$grupos[0]['id_grupo'];
}

$grupoActual = null;

foreach ($grupos as $g) {
    if ((int)$g['id_grupo'] === $idGrupoSeleccionado) {
        $grupoActual = $g;
        break;
    }
}

if (!$grupoActual && !empty($grupos)) {
    $grupoActual = $grupos[0];
    $idGrupoSeleccionado = (int)$grupoActual['id_grupo'];
}

if (empty($_SESSION['csrf_calificaciones_nobex'])) {
    $_SESSION['csrf_calificaciones_nobex'] = bin2hex(random_bytes(32));
}

$csrf = (string)$_SESSION['csrf_calificaciones_nobex'];

/*
|--------------------------------------------------------------------------
| AUTOGUARDADO AJAX
|--------------------------------------------------------------------------
*/
if ($_SERVER['REQUEST_METHOD'] === 'POST' && (string)($_POST['ajax_autosave'] ?? '') === '1') {
    try {
        $csrfPost = (string)($_POST['csrf_token'] ?? '');

        if ($csrf === '' || !hash_equals($csrf, $csrfPost)) {
            cal_json(false, 'Solicitud no válida. Recarga la página.');
        }

        $idParticipante = (int)($_POST['id_participante'] ?? 0);
        $idGrupoPost = (int)($_POST['id_grupo'] ?? 0);

        $correctasRaw = trim((string)($_POST['correctas'] ?? ''));
        $incorrectasRaw = trim((string)($_POST['incorrectas'] ?? ''));
        $tiempoRaw = trim((string)($_POST['tiempo'] ?? ''));
        $observacion = trim((string)($_POST['observacion'] ?? ''));

        if ($idParticipante <= 0) {
            cal_json(false, 'Participante inválido.');
        }

        if ($correctasRaw === '' && $incorrectasRaw === '') {
            cal_json(false, 'Ingrese correctas o incorrectas para guardar.');
        }

        if ($correctasRaw !== '' && !preg_match('/^\d+$/', $correctasRaw)) {
            cal_json(false, 'Correctas debe ser un número entero.');
        }

        if ($incorrectasRaw !== '' && !preg_match('/^\d+$/', $incorrectasRaw)) {
            cal_json(false, 'Incorrectas debe ser un número entero.');
        }

        $correctas = $correctasRaw === '' ? 0 : (int)$correctasRaw;
        $incorrectas = $incorrectasRaw === '' ? 0 : (int)$incorrectasRaw;

        $stmtPart = mysqli_prepare($conn, "
            SELECT
                p.id_participante,
                p.id_concurso,
                p.id_grupo,
                p.id_salon,
                p.apellidos_nombres,
                p.tipo_institucion,
                g.total_preguntas,
                g.puntos_correcta,
                g.penalizacion_incorrecta,
                g.puntos_blanco
            FROM concurso_participantes p
            INNER JOIN concurso_grupos g ON g.id_grupo = p.id_grupo
            WHERE p.id_participante = ?
              AND p.id_concurso = ?
              AND p.estado = 'ACTIVO'
            LIMIT 1
        ");

        if (!$stmtPart) {
            cal_json(false, 'No se pudo validar el participante.');
        }

        mysqli_stmt_bind_param($stmtPart, 'ii', $idParticipante, $idConcurso);
        mysqli_stmt_execute($stmtPart);
        $resPart = mysqli_stmt_get_result($stmtPart);
        $part = mysqli_fetch_assoc($resPart);
        mysqli_stmt_close($stmtPart);

        if (!$part) {
            cal_json(false, 'Participante inválido o pertenece a otro concurso.');
        }

        if ($idGrupoFijo > 0 && (int)$part['id_grupo'] !== $idGrupoFijo) {
            cal_json(false, $part['apellidos_nombres'] . ': no pertenece a tu grado asignado.');
        }

        if ($idSalonFijo > 0 && (int)$part['id_salon'] !== $idSalonFijo) {
            cal_json(false, $part['apellidos_nombres'] . ': no pertenece a tu salón asignado.');
        }

        if ($idGrupoPost > 0 && (int)$part['id_grupo'] !== $idGrupoPost) {
            cal_json(false, $part['apellidos_nombres'] . ': no pertenece al grado seleccionado.');
        }

        $totalPreguntas = (int)$part['total_preguntas'];

        if ($correctas < 0 || $incorrectas < 0) {
            cal_json(false, 'Correctas e incorrectas no pueden ser negativas.');
        }

        if (($correctas + $incorrectas) > $totalPreguntas) {
            cal_json(false, 'Correctas + incorrectas no puede superar ' . $totalPreguntas . '.');
        }

        $blanco = $totalPreguntas - $correctas - $incorrectas;
        $tiempoSegundos = cal_parse_tiempo($tiempoRaw);

        if ($tiempoSegundos === -1) {
            cal_json(false, 'Tiempo inválido. Usa MM:SS, HH:MM:SS o segundos.');
        }

        $pCorrecta = (float)$part['puntos_correcta'];
        $pIncorrecta = (float)$part['penalizacion_incorrecta'];
        $pBlanco = (float)$part['puntos_blanco'];

        $puntaje = cal_puntaje($correctas, $incorrectas, $blanco, $pCorrecta, $pIncorrecta, $pBlanco);
        $tiempoTexto = cal_formato_tiempo($tiempoSegundos);

        $idGrupoResultado = (int)$part['id_grupo'];
        $idSalonResultado = (int)$part['id_salon'];
        $idAcceso = (int)$acceso['id_acceso'];

        mysqli_begin_transaction($conn);

        $stmtExiste = mysqli_prepare($conn, "
            SELECT
                id_resultado,
                correctas,
                incorrectas,
                tiempo_texto,
                observacion,
                COALESCE(modificacion_contador, 0) AS modificacion_contador
            FROM concurso_resultados
            WHERE id_participante = ?
            LIMIT 1
        ");

        $idResultadoExiste = 0;
        $modificacionContadorActual = 0;
        $modificacionContadorNuevo = 0;
        $esCambioReal = true;

        if ($stmtExiste) {
            mysqli_stmt_bind_param($stmtExiste, 'i', $idParticipante);
            mysqli_stmt_execute($stmtExiste);
            $resExiste = mysqli_stmt_get_result($stmtExiste);
            $rowExiste = mysqli_fetch_assoc($resExiste);

            if ($rowExiste) {
                $idResultadoExiste = (int)$rowExiste['id_resultado'];
                $modificacionContadorActual = (int)($rowExiste['modificacion_contador'] ?? 0);

                $tiempoTextoComparar = $tiempoTexto ?? '';
                $observacionComparar = $observacion;
                $tiempoAnterior = (string)($rowExiste['tiempo_texto'] ?? '');
                $observacionAnterior = (string)($rowExiste['observacion'] ?? '');

                $esCambioReal = ((int)$rowExiste['correctas'] !== $correctas)
                    || ((int)$rowExiste['incorrectas'] !== $incorrectas)
                    || ($tiempoAnterior !== $tiempoTextoComparar)
                    || ($observacionAnterior !== $observacionComparar);
            }

            mysqli_stmt_close($stmtExiste);
        }

        if ($idResultadoExiste > 0) {
            $modificacionContadorNuevo = $modificacionContadorActual + ($esCambioReal ? 1 : 0);
            $stmtSave = mysqli_prepare($conn, "
                UPDATE concurso_resultados
                SET
                    id_acceso_registro = ?,
                    total_preguntas_usada = ?,
                    puntos_correcta_usada = ?,
                    penalizacion_incorrecta_usada = ?,
                    puntos_blanco_usada = ?,
                    correctas = ?,
                    incorrectas = ?,
                    blanco = ?,
                    puntaje = ?,
                    tiempo_segundos = ?,
                    tiempo_texto = ?,
                    observacion = ?,
                    modificacion_contador = ?,
                    estado = 'ACTIVO',
                    updated_at = NOW()
                WHERE id_resultado = ?
                LIMIT 1
            ");

            if (!$stmtSave) {
                throw new Exception('No se pudo preparar la actualización.');
            }

            mysqli_stmt_bind_param(
                $stmtSave,
                'iidddiiidissii',
                $idAcceso,
                $totalPreguntas,
                $pCorrecta,
                $pIncorrecta,
                $pBlanco,
                $correctas,
                $incorrectas,
                $blanco,
                $puntaje,
                $tiempoSegundos,
                $tiempoTexto,
                $observacion,
                $modificacionContadorNuevo,
                $idResultadoExiste
            );
        } else {
            $stmtSave = mysqli_prepare($conn, "
                INSERT INTO concurso_resultados
                    (
                        id_concurso,
                        id_grupo,
                        id_salon,
                        id_participante,
                        id_acceso_registro,
                        total_preguntas_usada,
                        puntos_correcta_usada,
                        penalizacion_incorrecta_usada,
                        puntos_blanco_usada,
                        correctas,
                        incorrectas,
                        blanco,
                        puntaje,
                        tiempo_segundos,
                        tiempo_texto,
                        observacion,
                        modificacion_contador,
                        estado,
                        created_at
                    )
                VALUES
                    (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, 'ACTIVO', NOW())
            ");

            if (!$stmtSave) {
                throw new Exception('No se pudo preparar el registro.');
            }

            $modificacionContadorNuevo = 0;

            mysqli_stmt_bind_param(
                $stmtSave,
                'iiiiiidddiiidissi',
                $idConcurso,
                $idGrupoResultado,
                $idSalonResultado,
                $idParticipante,
                $idAcceso,
                $totalPreguntas,
                $pCorrecta,
                $pIncorrecta,
                $pBlanco,
                $correctas,
                $incorrectas,
                $blanco,
                $puntaje,
                $tiempoSegundos,
                $tiempoTexto,
                $observacion,
                $modificacionContadorNuevo
            );
        }

        if (!mysqli_stmt_execute($stmtSave)) {
            mysqli_stmt_close($stmtSave);
            throw new Exception('No se pudo guardar la calificación.');
        }

        mysqli_stmt_close($stmtSave);

        cal_recalcular_puestos_grupo($conn, $idConcurso, $idGrupoResultado);

        cn_auditar(
            $conn,
            $idConcurso,
            $idGrupoResultado,
            $idAcceso,
            $idParticipante,
            'CALIFICACION_AUTOGUARDADA',
            'Calificación autoguardada para ' . $part['apellidos_nombres'] . ' | ' . $part['tipo_institucion'] . ' | puntaje ' . $puntaje
        );

        $puestos = [];

        $stmtPuestos = mysqli_prepare($conn, "
            SELECT r.id_participante, r.puesto
            FROM concurso_resultados r
            INNER JOIN concurso_participantes p
                ON p.id_participante = r.id_participante
            WHERE r.id_concurso = ?
              AND r.id_grupo = ?
              AND r.estado = 'ACTIVO'
              AND p.estado = 'ACTIVO'
        ");

        if ($stmtPuestos) {
            mysqli_stmt_bind_param($stmtPuestos, 'ii', $idConcurso, $idGrupoResultado);
            mysqli_stmt_execute($stmtPuestos);
            $resPuestos = mysqli_stmt_get_result($stmtPuestos);

            while ($rowP = mysqli_fetch_assoc($resPuestos)) {
                $puestos[(int)$rowP['id_participante']] = (int)$rowP['puesto'];
            }

            mysqli_stmt_close($stmtPuestos);
        }

        mysqli_commit($conn);

        cal_json(true, 'Guardado automáticamente.', [
            'id_participante' => $idParticipante,
            'correctas' => $correctas,
            'incorrectas' => $incorrectas,
            'blanco' => $blanco,
            'puntaje' => number_format($puntaje, 2, '.', ''),
            'tiempo_texto' => $tiempoTexto ?? '',
            'observacion' => $observacion,
            'modificacion_contador' => $modificacionContadorNuevo,
            'es_cambio_real' => $esCambioReal,
            'puestos' => $puestos
        ]);
    } catch (Throwable $e) {
        if (isset($conn) && $conn instanceof mysqli) {
            @mysqli_rollback($conn);
        }

        cal_json(false, 'Error al autoguardar: ' . $e->getMessage());
    }
}

/*
|--------------------------------------------------------------------------
| CARGA NORMAL DE LA PÁGINA
|--------------------------------------------------------------------------
*/

$participantes = [];

if ($idGrupoSeleccionado > 0) {
    $sqlParticipantes = "
        SELECT
            p.id_participante,
            p.codigo_participante,
            p.apellidos_nombres,
            p.institucion_educativa,
            p.tipo_institucion,
            p.documento,
            s.nombre_salon,
            g.total_preguntas,
            g.puntos_correcta,
            g.penalizacion_incorrecta,
            g.puntos_blanco,
            r.id_resultado,
            r.correctas,
            r.incorrectas,
            r.blanco,
            r.puntaje,
            r.puesto,
            r.tiempo_texto,
            r.observacion AS resultado_observacion,
            COALESCE(r.modificacion_contador, 0) AS modificacion_contador
        FROM concurso_participantes p
        INNER JOIN concurso_grupos g ON g.id_grupo = p.id_grupo
        LEFT JOIN concurso_salones s ON s.id_salon = p.id_salon
        LEFT JOIN concurso_resultados r ON r.id_participante = p.id_participante AND r.estado = 'ACTIVO'
        WHERE p.id_concurso = ?
          AND p.id_grupo = ?
          AND p.estado = 'ACTIVO'
    ";

    if ($idSalonFijo > 0) {
        $sqlParticipantes .= " AND p.id_salon = ? ";
    }

    $sqlParticipantes .= "
        ORDER BY
            p.apellidos_nombres ASC,
            FIELD(p.tipo_institucion, 'PRIVADA', 'PUBLICA'),
            s.orden ASC
    ";

    $stmtP = mysqli_prepare($conn, $sqlParticipantes);

    if ($stmtP) {
        if ($idSalonFijo > 0) {
            mysqli_stmt_bind_param($stmtP, 'iii', $idConcurso, $idGrupoSeleccionado, $idSalonFijo);
        } else {
            mysqli_stmt_bind_param($stmtP, 'ii', $idConcurso, $idGrupoSeleccionado);
        }

        mysqli_stmt_execute($stmtP);
        $resP = mysqli_stmt_get_result($stmtP);

        while ($row = mysqli_fetch_assoc($resP)) {
            $participantes[] = $row;
        }

        mysqli_stmt_close($stmtP);
    }
}

$ambito = cn_ambito_texto($acceso, $concursoTrabajo);
$totalPreguntasActual = $grupoActual ? (int)$grupoActual['total_preguntas'] : 0;
?>
<!DOCTYPE html>
<html lang="es">
<head>
    <meta charset="UTF-8">
    <title>Calificaciones | NOBEX</title>
    <meta name="viewport" content="width=device-width, initial-scale=1">

    <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.2/css/all.min.css">
    <link rel="stylesheet" href="assets/css/menu_concurso.css?v=4">

    <style>
        *{box-sizing:border-box}

        body{
            margin:0;
            font-family:Arial,Helvetica,sans-serif;
            background:#f3f6fb;
            color:#111827;
        }

        .topbar{
            background:#d90000;
            color:#fff;
            border-bottom:8px solid #ffcc00;
            padding:18px 22px;
        }

        .topbar-inner{
            max-width:1450px;
            margin:0 auto;
            display:flex;
            justify-content:space-between;
            gap:16px;
            align-items:center;
        }

        .topbar h1{
            margin:0;
            font-size:24px;
            font-weight:900;
        }

        .topbar p{
            margin:5px 0 0;
            color:#ffcc00;
            font-weight:800;
            font-size:14px;
        }

        .topbar a{
            color:#fff;
            text-decoration:none;
            border:1px solid rgba(255,255,255,.55);
            border-radius:12px;
            padding:10px 14px;
            font-weight:800;
            white-space:nowrap;
        }

        .container{
            max-width:1450px;
            margin:24px auto;
            padding:0 18px 36px;
        }

        .alert-ok,
        .alert-error,
        .alert-info{
            border-radius:14px;
            padding:13px 15px;
            margin-bottom:16px;
            font-weight:800;
            line-height:1.5;
        }

        .alert-ok{
            background:#dcfce7;
            color:#166534;
            border:1px solid #bbf7d0;
        }

        .alert-error{
            background:#fee2e2;
            color:#991b1b;
            border:1px solid #fecaca;
        }

        .alert-info{
            background:#eff6ff;
            color:#1e40af;
            border:1px solid #bfdbfe;
        }

        .card{
            background:#fff;
            border:1px solid #e5e7eb;
            border-radius:18px;
            padding:20px;
            box-shadow:0 12px 30px rgba(15,23,42,.08);
            margin-bottom:18px;
        }

        .card h2{
            margin:0 0 14px;
            font-size:21px;
        }

        .muted{
            color:#6b7280;
            line-height:1.5;
            font-size:14px;
        }

        label{
            display:block;
            font-weight:800;
            margin:12px 0 6px;
            color:#374151;
        }

        input,
        select{
            width:100%;
            border:1px solid #d1d5db;
            border-radius:10px;
            padding:9px 10px;
            font-size:14px;
            outline:none;
            background:#fff;
        }

        input:focus,
        select:focus{
            border-color:#1d4ed8;
            box-shadow:0 0 0 4px rgba(29,78,216,.13);
        }

        input[readonly]{
            background:#f9fafb;
            font-weight:900;
            color:#1e40af;
        }

        /* Quita las flechitas internas de los campos numéricos */
        input[type=number]::-webkit-inner-spin-button,
        input[type=number]::-webkit-outer-spin-button{
            -webkit-appearance:none;
            margin:0;
        }

        input[type=number]{
            -moz-appearance:textfield;
            appearance:textfield;
        }

        .filtros{
            display:grid;
            grid-template-columns:330px 330px 1fr;
            gap:18px;
            align-items:end;
        }

        .tabla-wrap{
            width:100%;
            overflow-x:auto;
        }

        table{
            width:100%;
            border-collapse:collapse;
            font-size:13px;
            min-width:1350px;
        }

        th,
        td{
            padding:8px;
            border-bottom:1px solid #e5e7eb;
            text-align:left;
            vertical-align:middle;
        }

        th{
            background:#f9fafb;
            color:#374151;
            position:sticky;
            top:0;
            z-index:1;
        }

        .col-num{
            width:54px;
            text-align:center;
        }

        .col-small{
            width:95px;
        }

        .col-tiempo{
            width:120px;
        }

        .col-check{
            width:105px;
            text-align:center;
        }

        .inp-num{
            text-align:center;
            font-weight:800;
        }

        .participante{
            font-weight:800;
            color:#111827;
        }

        .codigo{
            font-weight:900;
            color:#1e40af;
        }

        .tipo{
            font-weight:900;
            color:#92400e;
        }

        .puesto{
            font-weight:900;
            color:#b91c1c;
            text-align:center;
        }

        .row-error{
            background:#fee2e2!important;
        }

        .row-ok{
            background:#f0fdf4;
        }

        .row-modificado,
        .row-mod-1{
            background:#fff7ed!important;
            box-shadow:inset 5px 0 0 #f59e0b;
        }

        .row-mod-2{
            background:#fdf2f8!important;
            box-shadow:inset 5px 0 0 #ec4899;
        }

        .row-mod-3{
            background:#f5f3ff!important;
            box-shadow:inset 5px 0 0 #8b5cf6;
        }

        .row-mod-4{
            background:#ecfeff!important;
            box-shadow:inset 5px 0 0 #06b6d4;
        }

        .row-mod-5{
            background:#fefce8!important;
            box-shadow:inset 5px 0 0 #ca8a04;
        }

        .row-mod-6{
            background:#eef2ff!important;
            box-shadow:inset 5px 0 0 #4f46e5;
        }

        .buscador-calificaciones{
            margin:0 0 14px;
            display:grid;
            grid-template-columns:minmax(260px,420px) 1fr;
            gap:12px;
            align-items:end;
        }

        .buscador-calificaciones .muted{
            margin:0;
        }

        .resaltado-modificado{
            display:inline-block;
            border-radius:999px;
            padding:4px 8px;
            font-size:12px;
            font-weight:900;
            background:#fff7ed;
            color:#92400e;
            border:1px solid #fed7aa;
        }

        .btns{
            display:flex;
            gap:10px;
            flex-wrap:wrap;
            margin-top:16px;
        }

        .btn-link{
            border:0;
            border-radius:12px;
            background:#374151;
            color:#fff;
            font-weight:900;
            padding:12px 16px;
            cursor:pointer;
            font-size:15px;
            text-decoration:none;
            display:inline-block;
        }

        .btn-link:hover{
            background:#111827;
        }

        .empty{
            color:#6b7280;
            font-style:italic;
        }

        .badge{
            display:inline-block;
            border-radius:999px;
            padding:4px 8px;
            font-size:12px;
            font-weight:900;
            background:#eff6ff;
            color:#1e40af;
            border:1px solid #bfdbfe;
        }

        .auto-status{
            display:inline-block;
            border-radius:999px;
            padding:5px 8px;
            font-size:11px;
            font-weight:900;
            border:1px solid #d1d5db;
            background:#f9fafb;
            color:#374151;
            white-space:nowrap;
        }

        .auto-status.pending{
            background:#eff6ff;
            color:#1e40af;
            border-color:#bfdbfe;
        }

        .auto-status.saving{
            background:#fff7ed;
            color:#92400e;
            border-color:#fed7aa;
        }

        .auto-status.saved{
            background:#dcfce7;
            color:#166534;
            border-color:#bbf7d0;
        }

        .auto-status.error{
            background:#fee2e2;
            color:#991b1b;
            border-color:#fecaca;
        }

        @media(max-width:980px){
            .topbar-inner{
                flex-direction:column;
                align-items:flex-start;
            }

            .filtros,
            .buscador-calificaciones{
                grid-template-columns:1fr;
            }
        }
    </style>
</head>

<body class="cn-con-sidebar">

<?php include __DIR__ . '/includes/sidebar_concurso.php'; ?>

<header class="topbar">
    <div class="topbar-inner">
        <div>
            <h1>Registro de calificaciones</h1>
            <p><?php echo cn_h($ambito); ?> | Código: <?php echo cn_h($acceso['codigo']); ?></p>
        </div>
        <a href="panel.php">Volver al panel</a>
    </div>
</header>

<main class="container">

    <section class="card">
        <form method="get" class="filtros">
            <?php if ($esAdmin): ?>
                <div>
                    <label>Concurso</label>
                    <select name="id_concurso" onchange="this.form.submit()">
                        <?php foreach ($concursosActivos as $c): ?>
                            <option value="<?php echo cn_h($c['id_concurso']); ?>" <?php echo (int)$c['id_concurso'] === $idConcurso ? 'selected' : ''; ?>>
                                <?php echo cn_h($c['nombre'] . ' ' . $c['subtitulo']); ?>
                            </option>
                        <?php endforeach; ?>
                    </select>
                </div>
            <?php endif; ?>

            <div>
                <label>Grado</label>
                <?php if ($idGrupoFijo > 0): ?>
                    <select disabled>
                        <?php foreach ($grupos as $g): ?>
                            <option><?php echo cn_h($g['nombre']); ?></option>
                        <?php endforeach; ?>
                    </select>
                <?php else: ?>
                    <select name="id_grupo" onchange="this.form.submit()">
                        <?php foreach ($grupos as $g): ?>
                            <option value="<?php echo cn_h($g['id_grupo']); ?>" <?php echo (int)$g['id_grupo'] === $idGrupoSeleccionado ? 'selected' : ''; ?>>
                                <?php echo cn_h($g['nombre']); ?>
                            </option>
                        <?php endforeach; ?>
                    </select>
                <?php endif; ?>
            </div>

            <div>
                <p class="muted">
                    Total de preguntas: <strong><?php echo cn_h($totalPreguntasActual); ?></strong>.
                    El ranking se recalcula separado para <strong>PRIVADA</strong> y <strong>PÚBLICA</strong>.
                    El guardado ahora es <strong>automático</strong>, no necesitas presionar botón guardar.
                    <?php if ($idSalonFijo > 0): ?>
                        Este código solo muestra tu salón asignado.
                    <?php endif; ?>
                </p>
            </div>
        </form>
    </section>

    <section class="card">
        <h2>Lista de participantes</h2>

        <?php if (!empty($participantes)): ?>
            <div class="buscador-calificaciones">
                <div>
                    <label>Buscar participante</label>
                    <input
                        type="text"
                        id="txtBuscarParticipante"
                        placeholder="Buscar por apellidos, DNI, código, I.E., tipo o salón..."
                        autocomplete="off"
                    >
                </div>
                <p class="muted">
                    Ordenado alfabéticamente de <strong>A a Z</strong>.
                    <span class="resaltado-modificado">Colores = historial de modificaciones por estudiante</span>
                </p>
            </div>

            <form method="post" id="frmMasivo">
                <input type="hidden" name="csrf_token" value="<?php echo cn_h($csrf); ?>">
                <input type="hidden" name="id_concurso" value="<?php echo cn_h($idConcurso); ?>">
                <input type="hidden" name="id_grupo" value="<?php echo cn_h($idGrupoSeleccionado); ?>">

                <div class="tabla-wrap">
                    <table>
                        <thead>
                            <tr>
                                <th class="col-check">Estado</th>
                                <th class="col-num">N°</th>
                                <th>Participante</th>
                                <th>I.E</th>
                                <th>Tipo</th>
                                <th class="col-small">Salón</th>
                                <th class="col-small">Correctas</th>
                                <th class="col-small">Incorrectas</th>
                                <th class="col-small">Blanco</th>
                                <th class="col-small">Puntaje</th>
                                <th class="col-small">Puesto</th>
                                <th class="col-tiempo">Tiempo</th>
                                <th>Observación</th>
                            </tr>
                        </thead>
                        <tbody>
                            <?php
                            $n = 1;
                            foreach ($participantes as $p):
                                $idParticipante = (int)$p['id_participante'];
                                $total = (int)$p['total_preguntas'];
                                $pc = (float)$p['puntos_correcta'];
                                $pi = (float)$p['penalizacion_incorrecta'];
                                $pb = (float)$p['puntos_blanco'];
                                $tieneResultado = !empty($p['id_resultado']);

                                $correctasValor = $tieneResultado ? (int)$p['correctas'] : '';
                                $incorrectasValor = $tieneResultado ? (int)$p['incorrectas'] : '';
                                $blancoValor = $tieneResultado ? (int)$p['blanco'] : '';
                                $puntajeValor = $tieneResultado ? number_format((float)$p['puntaje'], 2, '.', '') : '';
                                $tiempoValor = $tieneResultado ? (string)($p['tiempo_texto'] ?? '') : '';
                                $observacionValor = $tieneResultado ? (string)($p['resultado_observacion'] ?? '') : '';
                                $modificacionContador = $tieneResultado ? (int)($p['modificacion_contador'] ?? 0) : 0;
                                $claseFilaModificacion = cal_clase_modificacion_php($modificacionContador, $tieneResultado);
                                $estadoTextoModificacion = cal_estado_texto_php($modificacionContador, $tieneResultado);
                                $estadoClaseModificacion = cal_estado_clase_php($modificacionContador, $tieneResultado);
                            ?>
                                <tr
                                    class="fila-resultado <?php echo cn_h($claseFilaModificacion); ?>"
                                    data-id-participante="<?php echo cn_h($idParticipante); ?>"
                                    data-mod-count="<?php echo cn_h($modificacionContador); ?>"
                                    data-total="<?php echo cn_h($total); ?>"
                                    data-pcorrecta="<?php echo cn_h($pc); ?>"
                                    data-pincorrecta="<?php echo cn_h($pi); ?>"
                                    data-pblanco="<?php echo cn_h($pb); ?>"
                                    data-original-correctas="<?php echo cn_h($correctasValor); ?>"
                                    data-original-incorrectas="<?php echo cn_h($incorrectasValor); ?>"
                                    data-original-tiempo="<?php echo cn_h($tiempoValor); ?>"
                                    data-original-observacion="<?php echo cn_h($observacionValor); ?>"
                                >
                                    <td class="col-check">
                                        <span class="auto-status <?php echo cn_h($estadoClaseModificacion); ?>">
                                            <?php echo cn_h($estadoTextoModificacion); ?>
                                        </span>
                                    </td>

                                    <td class="col-num"><?php echo cn_h($n); ?></td>

                                    <td>
                                        <div class="participante"><?php echo cn_h($p['apellidos_nombres']); ?></div>
                                        <div class="codigo">
                                            <?php echo cn_h($p['codigo_participante']); ?>
                                            <?php if (!empty($p['documento'])): ?>
                                                | DNI: <?php echo cn_h($p['documento']); ?>
                                            <?php endif; ?>
                                        </div>
                                    </td>

                                    <td><?php echo cn_h($p['institucion_educativa']); ?></td>
                                    <td class="tipo"><?php echo cn_h($p['tipo_institucion']); ?></td>
                                    <td><?php echo cn_h($p['nombre_salon'] ?: ''); ?></td>

                                    <td>
                                        <input
                                            type="text"
                                            inputmode="numeric"
                                            pattern="[0-9]*"
                                            min="0"
                                            max="<?php echo cn_h($total); ?>"
                                            class="inp-num inp-correctas"
                                            name="resultados[<?php echo cn_h($idParticipante); ?>][correctas]"
                                            value="<?php echo cn_h($correctasValor); ?>"
                                        >
                                    </td>

                                    <td>
                                        <input
                                            type="text"
                                            inputmode="numeric"
                                            pattern="[0-9]*"
                                            min="0"
                                            max="<?php echo cn_h($total); ?>"
                                            class="inp-num inp-incorrectas"
                                            name="resultados[<?php echo cn_h($idParticipante); ?>][incorrectas]"
                                            value="<?php echo cn_h($incorrectasValor); ?>"
                                        >
                                    </td>

                                    <td>
                                        <input
                                            type="text"
                                            inputmode="numeric"
                                            class="inp-num inp-blanco"
                                            value="<?php echo cn_h($blancoValor); ?>"
                                            readonly
                                        >
                                    </td>

                                    <td>
                                        <input
                                            type="text"
                                            class="inp-num inp-puntaje"
                                            value="<?php echo cn_h($puntajeValor); ?>"
                                            readonly
                                        >
                                    </td>

                                    <td class="puesto">
                                        <?php echo $tieneResultado ? cn_h($p['puesto']) : '<span class="badge">Nuevo</span>'; ?>
                                    </td>

                                    <td>
                                        <input
                                            type="text"
                                            class="inp-tiempo"
                                            name="resultados[<?php echo cn_h($idParticipante); ?>][tiempo]"
                                            value="<?php echo cn_h($tiempoValor); ?>"
                                            placeholder="08:35"
                                        >
                                    </td>

                                    <td>
                                        <input
                                            type="text"
                                            class="inp-observacion"
                                            name="resultados[<?php echo cn_h($idParticipante); ?>][observacion]"
                                            value="<?php echo cn_h($observacionValor); ?>"
                                            placeholder="Opcional"
                                        >
                                    </td>
                                </tr>
                            <?php
                                $n++;
                            endforeach;
                            ?>
                        </tbody>
                    </table>
                </div>

                <div class="alert-info" id="msgGlobal" style="display:none;"></div>

                <div class="btns">
                    <a class="btn-link" href="panel.php">Volver</a>
                </div>
            </form>
        <?php else: ?>
            <p class="empty">
                No hay participantes registrados para este concurso, grado o salón.
            </p>
        <?php endif; ?>
    </section>
</main>

<script>
document.addEventListener('DOMContentLoaded', function () {
    const filas = document.querySelectorAll('.fila-resultado');
    const msgGlobal = document.getElementById('msgGlobal');
    const txtBuscarParticipante = document.getElementById('txtBuscarParticipante');
    const frmMasivo = document.getElementById('frmMasivo');

    const csrfInput = document.querySelector('input[name="csrf_token"]');
    const idGrupoInput = document.querySelector('input[name="id_grupo"]');

    const csrfToken = csrfInput ? csrfInput.value : '';
    const idGrupo = idGrupoInput ? idGrupoInput.value : '';
    const timers = new WeakMap();

    if (frmMasivo) {
        frmMasivo.addEventListener('submit', function (e) {
            e.preventDefault();
        });
    }

    function mostrarGlobal(texto) {
        if (!msgGlobal) return;
        msgGlobal.textContent = texto || '';
        msgGlobal.style.display = texto ? 'block' : 'none';
    }

    function numero(valor) {
        const n = parseInt(valor || '0', 10);
        return Number.isNaN(n) ? 0 : n;
    }

    function decimal(valor) {
        const n = parseFloat(valor || '0');
        return Number.isNaN(n) ? 0 : n;
    }

    function normalizarTexto(valor) {
        return (valor || '').toString().trim();
    }

    function normalizarNumeroComparacion(valor) {
        const txt = normalizarTexto(valor);

        if (txt === '') {
            return '';
        }

        const n = parseInt(txt, 10);
        return Number.isNaN(n) ? '' : String(n);
    }

    function setEstado(fila, texto, clase) {
        const estado = fila.querySelector('.auto-status');

        if (!estado) return;

        estado.textContent = texto;
        estado.className = 'auto-status ' + clase;
    }

    function quitarClasesModificacion(fila) {
        fila.classList.remove(
            'row-modificado',
            'row-autosaved',
            'row-mod-1',
            'row-mod-2',
            'row-mod-3',
            'row-mod-4',
            'row-mod-5',
            'row-mod-6'
        );
    }

    function aplicarClaseModificacion(fila, numeroModificacion) {
        quitarClasesModificacion(fila);

        const n = parseInt(numeroModificacion || '0', 10);

        if (Number.isNaN(n) || n <= 0) {
            return;
        }

        const clase = 'row-mod-' + Math.min(n, 6);
        fila.classList.add(clase);
    }

    function limpiarSoloDigitos(input) {
        if (!input) {
            return;
        }

        input.value = input.value.replace(/[^0-9]/g, '');
    }

    function filaFueModificada(fila) {
        const inpCorrectas = fila.querySelector('.inp-correctas');
        const inpIncorrectas = fila.querySelector('.inp-incorrectas');
        const inpTiempo = fila.querySelector('.inp-tiempo');
        const inpObservacion = fila.querySelector('.inp-observacion');

        const actualCorrectas = normalizarNumeroComparacion(inpCorrectas ? inpCorrectas.value : '');
        const actualIncorrectas = normalizarNumeroComparacion(inpIncorrectas ? inpIncorrectas.value : '');
        const actualTiempo = normalizarTexto(inpTiempo ? inpTiempo.value : '');
        const actualObservacion = normalizarTexto(inpObservacion ? inpObservacion.value : '');

        return actualCorrectas !== normalizarNumeroComparacion(fila.dataset.originalCorrectas || '')
            || actualIncorrectas !== normalizarNumeroComparacion(fila.dataset.originalIncorrectas || '')
            || actualTiempo !== normalizarTexto(fila.dataset.originalTiempo || '')
            || actualObservacion !== normalizarTexto(fila.dataset.originalObservacion || '');
    }

    function pintarModificado(fila) {
        const modificacionesGuardadas = parseInt(fila.dataset.modCount || '0', 10);
        const base = Number.isNaN(modificacionesGuardadas) ? 0 : modificacionesGuardadas;

        if (filaFueModificada(fila)) {
            aplicarClaseModificacion(fila, base + 1);
        } else {
            aplicarClaseModificacion(fila, base);
        }
    }

    function recalcularNumeracionVisible() {
        let contador = 1;

        filas.forEach(function (fila) {
            const celdaNumero = fila.querySelector('.col-num');

            if (fila.style.display === 'none') {
                return;
            }

            if (celdaNumero) {
                celdaNumero.textContent = contador;
            }

            contador++;
        });
    }

    function aplicarBuscador() {
        if (!txtBuscarParticipante) {
            return;
        }

        const texto = txtBuscarParticipante.value.toLowerCase().trim();

        filas.forEach(function (fila) {
            const contenido = fila.textContent.toLowerCase();
            fila.style.display = contenido.includes(texto) ? '' : 'none';
        });

        recalcularNumeracionVisible();
    }

    function recalcularFila(fila) {
        const total = numero(fila.dataset.total);
        const pCorrecta = decimal(fila.dataset.pcorrecta);
        const pIncorrecta = decimal(fila.dataset.pincorrecta);
        const pBlanco = decimal(fila.dataset.pblanco);

        const inpCorrectas = fila.querySelector('.inp-correctas');
        const inpIncorrectas = fila.querySelector('.inp-incorrectas');
        const inpBlanco = fila.querySelector('.inp-blanco');
        const inpPuntaje = fila.querySelector('.inp-puntaje');

        const correctasTxt = inpCorrectas ? inpCorrectas.value.trim() : '';
        const incorrectasTxt = inpIncorrectas ? inpIncorrectas.value.trim() : '';

        if (correctasTxt === '' && incorrectasTxt === '') {
            if (inpBlanco) inpBlanco.value = '';
            if (inpPuntaje) inpPuntaje.value = '';
            fila.classList.remove('row-error');
            pintarModificado(fila);
            return true;
        }

        const correctas = numero(correctasTxt);
        const incorrectas = numero(incorrectasTxt);
        const blanco = total - correctas - incorrectas;

        if (blanco < 0) {
            if (inpBlanco) inpBlanco.value = 0;
            if (inpPuntaje) inpPuntaje.value = '';
            fila.classList.add('row-error');
            pintarModificado(fila);
            return false;
        }

        const puntaje = (correctas * pCorrecta) - (incorrectas * pIncorrecta) + (blanco * pBlanco);

        if (inpBlanco) inpBlanco.value = blanco;
        if (inpPuntaje) inpPuntaje.value = puntaje.toFixed(2);

        fila.classList.remove('row-error');
        pintarModificado(fila);

        return true;
    }

    function recalcularTodo() {
        let errores = 0;

        filas.forEach(function (fila) {
            const ok = recalcularFila(fila);
            if (!ok) errores++;
        });

        if (errores > 0) {
            mostrarGlobal('Hay filas con correctas + incorrectas mayor al total de preguntas. Corrige para que pueda autoguardar.');
        } else {
            mostrarGlobal('');
        }
    }

    function tieneDatosParaGuardar(fila) {
        const inpCorrectas = fila.querySelector('.inp-correctas');
        const inpIncorrectas = fila.querySelector('.inp-incorrectas');

        const correctasTxt = inpCorrectas ? inpCorrectas.value.trim() : '';
        const incorrectasTxt = inpIncorrectas ? inpIncorrectas.value.trim() : '';

        return !(correctasTxt === '' && incorrectasTxt === '');
    }

    function actualizarPuestos(puestos) {
        if (!puestos || typeof puestos !== 'object') {
            return;
        }

        Object.keys(puestos).forEach(function (idParticipante) {
            const fila = document.querySelector('.fila-resultado[data-id-participante="' + idParticipante + '"]');

            if (!fila) return;

            const celdaPuesto = fila.querySelector('.puesto');

            if (celdaPuesto) {
                celdaPuesto.textContent = puestos[idParticipante];
            }
        });
    }

    function guardarFila(fila) {
        const timerAnterior = timers.get(fila);

        if (timerAnterior) {
            clearTimeout(timerAnterior);
            timers.delete(fila);
        }

        const ok = recalcularFila(fila);

        if (!ok) {
            setEstado(fila, 'Error', 'error');
            mostrarGlobal('No se puede autoguardar: correctas + incorrectas supera el total de preguntas.');
            return;
        }

        if (!filaFueModificada(fila)) {
            return;
        }

        if (!tieneDatosParaGuardar(fila)) {
            setEstado(fila, 'Nuevo', 'pending');
            return;
        }

        const idParticipante = fila.dataset.idParticipante || '';
        const inpCorrectas = fila.querySelector('.inp-correctas');
        const inpIncorrectas = fila.querySelector('.inp-incorrectas');
        const inpTiempo = fila.querySelector('.inp-tiempo');
        const inpObservacion = fila.querySelector('.inp-observacion');

        const params = new URLSearchParams();
        params.append('ajax_autosave', '1');
        params.append('csrf_token', csrfToken);
        params.append('id_grupo', idGrupo);
        params.append('id_participante', idParticipante);
        params.append('correctas', inpCorrectas ? inpCorrectas.value.trim() : '');
        params.append('incorrectas', inpIncorrectas ? inpIncorrectas.value.trim() : '');
        params.append('tiempo', inpTiempo ? inpTiempo.value.trim() : '');
        params.append('observacion', inpObservacion ? inpObservacion.value.trim() : '');

        setEstado(fila, 'Guardando...', 'saving');

        fetch('calificaciones.php', {
            method: 'POST',
            headers: {
                'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8'
            },
            body: params.toString(),
            credentials: 'same-origin'
        })
        .then(function (res) {
            return res.json();
        })
        .then(function (data) {
            if (!data || !data.ok) {
                const mensaje = data && data.message ? data.message : 'No se pudo autoguardar.';
                setEstado(fila, 'Error', 'error');
                fila.classList.add('row-error');
                mostrarGlobal(mensaje);
                return;
            }

            const inpBlanco = fila.querySelector('.inp-blanco');
            const inpPuntaje = fila.querySelector('.inp-puntaje');

            if (inpBlanco) inpBlanco.value = data.blanco;
            if (inpPuntaje) inpPuntaje.value = data.puntaje;
            if (inpTiempo) inpTiempo.value = data.tiempo_texto || '';

            actualizarPuestos(data.puestos);

            fila.dataset.originalCorrectas = String(data.correctas);
            fila.dataset.originalIncorrectas = String(data.incorrectas);
            fila.dataset.originalTiempo = data.tiempo_texto || '';
            fila.dataset.originalObservacion = data.observacion || '';

            const nuevoConteo = parseInt(data.modificacion_contador || '0', 10);
            const contadorFinal = Number.isNaN(nuevoConteo) ? 0 : nuevoConteo;
            fila.dataset.modCount = String(contadorFinal);

            fila.classList.remove('row-error');

            if (contadorFinal > 0) {
                fila.classList.remove('row-ok');
            } else {
                fila.classList.add('row-ok');
            }

            aplicarClaseModificacion(fila, contadorFinal);
            setEstado(fila, contadorFinal > 0 ? ('Mod. ' + contadorFinal) : 'Guardado', 'saved');
            mostrarGlobal('');
        })
        .catch(function () {
            setEstado(fila, 'Error', 'error');
            fila.classList.add('row-error');
            mostrarGlobal('Error de conexión. No se pudo autoguardar.');
        });
    }

    function programarGuardado(fila) {
        const anterior = timers.get(fila);

        if (anterior) {
            clearTimeout(anterior);
        }

        const nuevoTimer = setTimeout(function () {
            guardarFila(fila);
        }, 3000);

        timers.set(fila, nuevoTimer);
    }

    filas.forEach(function (fila) {
        const inputs = fila.querySelectorAll('.inp-correctas, .inp-incorrectas, .inp-tiempo, .inp-observacion');

        inputs.forEach(function (input) {
            input.addEventListener('input', function () {
                if (input.classList.contains('inp-correctas') || input.classList.contains('inp-incorrectas')) {
                    limpiarSoloDigitos(input);
                }

                recalcularFila(fila);

                if (filaFueModificada(fila)) {
                    const modificacionesGuardadas = parseInt(fila.dataset.modCount || '0', 10);
                    const siguiente = (Number.isNaN(modificacionesGuardadas) ? 0 : modificacionesGuardadas) + 1;

                    aplicarClaseModificacion(fila, siguiente);
                    setEstado(fila, 'Editado ' + siguiente, 'saving');
                    programarGuardado(fila);
                }
            });

            input.addEventListener('blur', function () {
                guardarFila(fila);
            });
        });

        recalcularFila(fila);
        pintarModificado(fila);
    });

    if (txtBuscarParticipante) {
        txtBuscarParticipante.addEventListener('input', aplicarBuscador);
        aplicarBuscador();
    }

    recalcularTodo();
});
</script>

</body>
</html>
