<?php
// concurso_nobel/omr/includes/bootstrap.php

declare(strict_types=1);

require_once dirname(__DIR__) . '/config.php';
require_once dirname(__DIR__, 2) . '/includes/db.php';
require_once dirname(__DIR__, 2) . '/includes/concurso_contexto.php';

define('OMR_TEMPLATE', CN_OMR_TEMPLATE_PATH);
define('OMR_TEMPLATE_PATH', CN_OMR_TEMPLATE_PATH);

function omr_h($value): string
{
    return htmlspecialchars((string)$value, ENT_QUOTES, 'UTF-8');
}

function omr_redirect(string $url): void
{
    header('Location: ' . $url);
    exit;
}

function omr_ext(string $name): string
{
    return strtolower((string)pathinfo($name, PATHINFO_EXTENSION));
}

function omr_safe_filename(string $name): string
{
    $base = basename(str_replace('\\', '/', $name));
    $base = preg_replace('/[^A-Za-z0-9._-]+/', '_', $base);
    $base = trim((string)$base, '._-');

    return $base !== '' ? $base : 'archivo';
}

function omr_ensure_dir(string $dir): void
{
    if (is_dir($dir)) {
        return;
    }

    if (!mkdir($dir, 0775, true) && !is_dir($dir)) {
        throw new RuntimeException('No se pudo crear la carpeta: ' . $dir);
    }
}

function omr_col_exists(mysqli $conn, string $table, string $column): 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', $table, $column);
    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 omr_ensure_schema(mysqli $conn): void
{
    $queries = [];

    $queries[] = "
        CREATE TABLE IF NOT EXISTS concurso_omr_claves (
            id_clave INT UNSIGNED NOT NULL AUTO_INCREMENT,
            id_concurso INT NOT NULL,
            id_grupo INT NOT NULL,
            tipo CHAR(1) NOT NULL DEFAULT 'A',
            pregunta SMALLINT UNSIGNED NOT NULL,
            respuesta_correcta CHAR(1) NOT NULL,
            puntaje DECIMAL(8,2) NOT NULL DEFAULT 1.00,
            created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
            updated_at DATETIME NULL ON UPDATE CURRENT_TIMESTAMP,
            PRIMARY KEY (id_clave),
            UNIQUE KEY uk_concurso_grupo_tipo_pregunta (id_concurso, id_grupo, tipo, pregunta),
            KEY idx_clave_grupo (id_concurso, id_grupo, tipo)
        ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci
    ";

    $queries[] = "
        CREATE TABLE IF NOT EXISTS concurso_omr_procesamientos (
            id_procesamiento INT UNSIGNED NOT NULL AUTO_INCREMENT,
            id_concurso INT NOT NULL,
            id_grupo INT NULL,
            id_salon INT NULL,
            id_participante INT NULL,
            id_acceso INT NULL,
            lote VARCHAR(80) NULL,
            archivo_original VARCHAR(255) NOT NULL,
            archivo_guardado VARCHAR(255) NULL,
            pagina SMALLINT UNSIGNED NULL,
            dni_leido VARCHAR(20) NULL,
            nivel_leido TINYINT UNSIGNED NULL,
            grado_leido TINYINT UNSIGNED NULL,
            tipo_leido CHAR(1) NULL,
            total_preguntas SMALLINT UNSIGNED NOT NULL DEFAULT 0,
            correctas SMALLINT UNSIGNED NOT NULL DEFAULT 0,
            incorrectas SMALLINT UNSIGNED NOT NULL DEFAULT 0,
            blanco SMALLINT UNSIGNED NOT NULL DEFAULT 0,
            dobles SMALLINT UNSIGNED NOT NULL DEFAULT 0,
            puntaje DECIMAL(10,2) NOT NULL DEFAULT 0.00,
            estado ENUM('PROCESADO','OBSERVADO','ERROR') NOT NULL DEFAULT 'OBSERVADO',
            aplicado_resultado TINYINT(1) NOT NULL DEFAULT 0,
            observacion TEXT NULL,
            raw_json LONGTEXT NULL,
            created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
            PRIMARY KEY (id_procesamiento),
            KEY idx_omr_concurso (id_concurso, created_at),
            KEY idx_omr_participante (id_participante),
            KEY idx_omr_dni (dni_leido),
            KEY idx_omr_estado (estado)
        ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci
    ";

    $queries[] = "
        CREATE TABLE IF NOT EXISTS concurso_omr_respuestas (
            id_respuesta INT UNSIGNED NOT NULL AUTO_INCREMENT,
            id_procesamiento INT UNSIGNED NOT NULL,
            pregunta SMALLINT UNSIGNED NOT NULL,
            respuesta_leida VARCHAR(12) NOT NULL,
            respuesta_correcta CHAR(1) NULL,
            es_correcta TINYINT(1) NOT NULL DEFAULT 0,
            score_a DECIMAL(8,4) NULL,
            score_b DECIMAL(8,4) NULL,
            score_c DECIMAL(8,4) NULL,
            score_d DECIMAL(8,4) NULL,
            PRIMARY KEY (id_respuesta),
            KEY idx_omr_resp_proc (id_procesamiento)
        ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci
    ";

    foreach ($queries as $sql) {
        if (!mysqli_query($conn, $sql)) {
            throw new RuntimeException('No se pudo preparar tablas OMR: ' . mysqli_error($conn));
        }
    }
}

function omr_context(string $permission): array
{
    global $conn;

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

    if ($permission === 'admin' && !$isAdmin) {
        http_response_code(403);
        die('Acceso denegado. Se requiere permiso administrativo.');
    }

    if ($permission === 'procesar' && (int)$acceso['puede_registrar_resultados'] !== 1 && !$isAdmin) {
        http_response_code(403);
        die('Tu codigo no tiene permiso para registrar resultados.');
    }

    if ($permission === 'ver' && (int)$acceso['puede_ver_resultados'] !== 1 && !$isAdmin) {
        http_response_code(403);
        die('Tu codigo no tiene permiso para ver resultados.');
    }

    if ($permission === 'ficha'
        && (int)$acceso['puede_registrar_participantes'] !== 1
        && (int)$acceso['puede_registrar_resultados'] !== 1
        && !$isAdmin
    ) {
        http_response_code(403);
        die('Tu codigo no tiene permiso para imprimir fichas.');
    }

    if ($permission === 'modulo'
        && (int)$acceso['puede_registrar_resultados'] !== 1
        && (int)$acceso['puede_ver_resultados'] !== 1
        && (int)$acceso['puede_registrar_participantes'] !== 1
        && !$isAdmin
    ) {
        http_response_code(403);
        die('Tu codigo no tiene permiso para usar OMR.');
    }

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

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

    omr_ensure_schema($conn);

    $concurso = cn_datos_concurso($conn, $idConcurso);

    return [
        'conn' => $conn,
        'acceso' => $acceso,
        'is_admin' => $isAdmin,
        'id_concurso' => $idConcurso,
        'concurso' => $concurso,
        'id_grupo_fijo' => $isAdmin ? 0 : cn_id_grupo_fijo($acceso),
        'id_salon_fijo' => $isAdmin ? 0 : cn_id_salon_fijo($acceso),
        'ambito' => cn_ambito_texto($acceso, $concurso),
    ];
}

function omr_nivel_numero(string $nivel): int
{
    $nivel = strtoupper(trim($nivel));

    if ($nivel === 'INICIAL') {
        return 1;
    }

    if ($nivel === 'PRIMARIA') {
        return 2;
    }

    if ($nivel === 'SECUNDARIA') {
        return 3;
    }

    return 0;
}

function omr_nivel_nombre(int $nivel): string
{
    if ($nivel === 1) {
        return 'Inicial';
    }

    if ($nivel === 2) {
        return 'Primaria';
    }

    if ($nivel === 3) {
        return 'Secundaria';
    }

    return 'No leido';
}

function omr_tipo_valido(string $tipo): bool
{
    return in_array(strtoupper(trim($tipo)), ['A', 'B', 'C', 'D'], true);
}

function omr_grupos(mysqli $conn, int $idConcurso, int $idGrupoFijo = 0): array
{
    return cn_grupos_concurso($conn, $idConcurso, $idGrupoFijo);
}

function omr_grupo_por_id(mysqli $conn, int $idConcurso, int $idGrupo): ?array
{
    $stmt = mysqli_prepare($conn, "
        SELECT *
        FROM concurso_grupos
        WHERE id_concurso = ?
          AND id_grupo = ?
          AND estado = 1
        LIMIT 1
    ");

    if (!$stmt) {
        return null;
    }

    mysqli_stmt_bind_param($stmt, 'ii', $idConcurso, $idGrupo);
    mysqli_stmt_execute($stmt);
    $res = mysqli_stmt_get_result($stmt);
    $row = mysqli_fetch_assoc($res);
    mysqli_stmt_close($stmt);

    return $row ?: null;
}

function omr_clave(mysqli $conn, int $idConcurso, int $idGrupo, string $tipo): array
{
    $tipo = strtoupper(trim($tipo));
    $clave = [];

    $stmt = mysqli_prepare($conn, "
        SELECT pregunta, respuesta_correcta, puntaje
        FROM concurso_omr_claves
        WHERE id_concurso = ?
          AND id_grupo = ?
          AND tipo = ?
        ORDER BY pregunta ASC
    ");

    if (!$stmt) {
        return $clave;
    }

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

    while ($row = mysqli_fetch_assoc($res)) {
        $clave[(int)$row['pregunta']] = [
            'respuesta' => strtoupper((string)$row['respuesta_correcta']),
            'puntaje' => (float)$row['puntaje'],
        ];
    }

    mysqli_stmt_close($stmt);
    return $clave;
}

function omr_participante_por_dni(mysqli $conn, int $idConcurso, string $dni): ?array
{
    $stmt = mysqli_prepare($conn, "
        SELECT
            p.*,
            g.nivel AS grupo_nivel,
            g.grado_orden,
            g.nombre AS grupo_nombre,
            g.total_preguntas,
            g.puntos_correcta,
            g.penalizacion_incorrecta,
            g.puntos_blanco,
            s.nombre_salon
        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
        WHERE p.id_concurso = ?
          AND p.documento = ?
          AND p.estado = 'ACTIVO'
        LIMIT 1
    ");

    if (!$stmt) {
        return null;
    }

    mysqli_stmt_bind_param($stmt, 'is', $idConcurso, $dni);
    mysqli_stmt_execute($stmt);
    $res = mysqli_stmt_get_result($stmt);
    $row = mysqli_fetch_assoc($res);
    mysqli_stmt_close($stmt);

    return $row ?: null;
}

function omr_calculate_score(int $correctas, int $incorrectas, int $blanco, array $grupo): float
{
    return ($correctas * (float)$grupo['puntos_correcta'])
        - ($incorrectas * (float)$grupo['penalizacion_incorrecta'])
        + ($blanco * (float)$grupo['puntos_blanco']);
}

function omr_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 omr_recalcular_puestos_grupo(mysqli $conn, int $idConcurso, int $idGrupo): void
{
    omr_recalcular_puestos_categoria($conn, $idConcurso, $idGrupo, 'PRIVADA');
    omr_recalcular_puestos_categoria($conn, $idConcurso, $idGrupo, 'PUBLICA');
}

function omr_guardar_resultado_final(
    mysqli $conn,
    array $acceso,
    array $participante,
    int $correctas,
    int $incorrectas,
    int $blanco,
    float $puntaje,
    string $observacion
): void {
    $idConcurso = (int)$participante['id_concurso'];
    $idGrupo = (int)$participante['id_grupo'];
    $idSalon = $participante['id_salon'] !== null ? (int)$participante['id_salon'] : null;
    $idParticipante = (int)$participante['id_participante'];
    $idAcceso = (int)$acceso['id_acceso'];
    $totalPreguntas = (int)$participante['total_preguntas'];
    $pCorrecta = (float)$participante['puntos_correcta'];
    $pIncorrecta = (float)$participante['penalizacion_incorrecta'];
    $pBlanco = (float)$participante['puntos_blanco'];
    $tiempoSegundos = null;
    $tiempoTexto = null;

    $stmtExiste = mysqli_prepare($conn, "
        SELECT id_resultado
        FROM concurso_resultados
        WHERE id_participante = ?
        LIMIT 1
    ");

    $idResultado = 0;

    if ($stmtExiste) {
        mysqli_stmt_bind_param($stmtExiste, 'i', $idParticipante);
        mysqli_stmt_execute($stmtExiste);
        $res = mysqli_stmt_get_result($stmtExiste);
        $row = mysqli_fetch_assoc($res);
        $idResultado = $row ? (int)$row['id_resultado'] : 0;
        mysqli_stmt_close($stmtExiste);
    }

    if ($idResultado > 0) {
        $stmt = 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 = ?,
                estado = 'ACTIVO',
                updated_at = NOW()
            WHERE id_resultado = ?
            LIMIT 1
        ");

        if (!$stmt) {
            throw new RuntimeException('No se pudo preparar actualizacion de resultado final.');
        }

        mysqli_stmt_bind_param(
            $stmt,
            'iidddiiidissi',
            $idAcceso,
            $totalPreguntas,
            $pCorrecta,
            $pIncorrecta,
            $pBlanco,
            $correctas,
            $incorrectas,
            $blanco,
            $puntaje,
            $tiempoSegundos,
            $tiempoTexto,
            $observacion,
            $idResultado
        );
    } else {
        $stmt = 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,
                    estado,
                    created_at
                )
            VALUES
                (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, 'ACTIVO', NOW())
        ");

        if (!$stmt) {
            throw new RuntimeException('No se pudo preparar registro de resultado final.');
        }

        mysqli_stmt_bind_param(
            $stmt,
            'iiiiiidddiiidiss',
            $idConcurso,
            $idGrupo,
            $idSalon,
            $idParticipante,
            $idAcceso,
            $totalPreguntas,
            $pCorrecta,
            $pIncorrecta,
            $pBlanco,
            $correctas,
            $incorrectas,
            $blanco,
            $puntaje,
            $tiempoSegundos,
            $tiempoTexto,
            $observacion
        );
    }

    if (!mysqli_stmt_execute($stmt)) {
        $error = mysqli_stmt_error($stmt);
        mysqli_stmt_close($stmt);
        throw new RuntimeException('No se pudo guardar resultado final: ' . $error);
    }

    mysqli_stmt_close($stmt);
    omr_recalcular_puestos_grupo($conn, $idConcurso, $idGrupo);
}

function omr_extract_json(string $text): array
{
    $json = json_decode($text, true);

    if (is_array($json)) {
        return $json;
    }

    $start = strpos($text, '{');
    $end = strrpos($text, '}');

    if ($start !== false && $end !== false && $end > $start) {
        $json = json_decode(substr($text, $start, $end - $start + 1), true);

        if (is_array($json)) {
            return $json;
        }
    }

    throw new RuntimeException('El comando no devolvio JSON valido: ' . $text);
}

function omr_exec_json(string $command): array
{
    $output = [];
    $code = 0;
    exec($command, $output, $code);

    $text = implode("\n", $output);
    $json = omr_extract_json($text);

    if ($code !== 0) {
        throw new RuntimeException((string)($json['error'] ?? $text));
    }

    return $json;
}

function omr_python_command(array $args): string
{
    $cmd = CN_OMR_PYTHON_COMMAND;

    foreach ($args as $arg) {
        $cmd .= ' ' . escapeshellarg((string)$arg);
    }

    return $cmd . ' 2>&1';
}

function omr_page_header(string $title, array $ctx): void
{
    $concurso = $ctx['concurso'];
    $nombreConcurso = $concurso ? trim((string)$concurso['nombre'] . ' ' . (string)$concurso['subtitulo']) : 'Concurso activo';
    ?>
<!DOCTYPE html>
<html lang="es">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <title><?php echo omr_h($title); ?> | OMR</title>
    <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=5">
    <style>
        *{box-sizing:border-box}
        body{margin:0;font-family:Arial,Helvetica,sans-serif;background:#f4f6fb;color:#111827}
        .topbar{background:#c9151b;color:#fff;border-bottom:7px solid #f5bd16;padding:18px 22px}
        .topbar-inner{max-width:1280px;margin:0 auto;display:flex;justify-content:space-between;gap:16px;align-items:center}
        .topbar h1{margin:0;font-size:23px;font-weight:900}
        .topbar p{margin:5px 0 0;color:#ffe082;font-weight:700;font-size:13px}
        .topbar a{color:#fff;text-decoration:none;border:1px solid rgba(255,255,255,.55);border-radius:10px;padding:9px 12px;font-weight:800;white-space:nowrap}
        .container{max-width:1280px;margin:22px auto;padding:0 18px 36px}
        .card{background:#fff;border:1px solid #e5e7eb;border-radius:12px;padding:18px;margin-bottom:16px;box-shadow:0 10px 24px rgba(15,23,42,.08)}
        .grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(235px,1fr));gap:14px}
        .action{display:block;text-decoration:none;color:#111827;border:1px solid #e5e7eb;border-radius:12px;padding:18px;background:#fff}
        .action h3{margin:0 0 7px;font-size:18px}.action p{margin:0;color:#6b7280;line-height:1.45}
        .btn{display:inline-block;border:0;border-radius:10px;padding:10px 13px;background:#c9151b;color:#fff;text-decoration:none;font-weight:800;cursor:pointer}
        .btn.secondary{background:#1f2937}.btn.light{background:#e5e7eb;color:#111827}
        .row{display:flex;gap:12px;flex-wrap:wrap;align-items:end}
        label{font-weight:800;font-size:13px;display:block;margin-bottom:6px}
        select,input[type=text],input[type=file],input[type=number]{width:100%;border:1px solid #cbd5e1;border-radius:10px;padding:10px 11px;font-size:14px}
        .field{min-width:210px;flex:1}
        table{width:100%;border-collapse:collapse;background:#fff}
        th,td{border-bottom:1px solid #e5e7eb;padding:9px;text-align:left;font-size:13px;vertical-align:top}
        th{background:#f8fafc;color:#374151}
        .alert{border-radius:11px;padding:12px 14px;margin-bottom:14px;font-weight:700}
        .ok{background:#dcfce7;color:#166534;border:1px solid #bbf7d0}
        .err{background:#fee2e2;color:#991b1b;border:1px solid #fecaca}
        .info{background:#eff6ff;color:#1e40af;border:1px solid #bfdbfe}
        .badge{display:inline-block;border-radius:999px;padding:4px 8px;font-weight:800;font-size:12px}
        .b-ok{background:#dcfce7;color:#166534}.b-warn{background:#fef3c7;color:#92400e}.b-err{background:#fee2e2;color:#991b1b}.b-blue{background:#dbeafe;color:#1e40af}
        .muted{color:#6b7280;line-height:1.45}.small{font-size:12px}
        .answers{display:grid;grid-template-columns:repeat(auto-fill,minmax(115px,1fr));gap:10px}
        .qbox{border:1px solid #e5e7eb;border-radius:10px;padding:8px;background:#fafafa}
        .qbox strong{display:block;margin-bottom:6px}
    </style>
</head>
<body class="cn-con-sidebar">
<?php
    $conn = $ctx['conn'];
    $acceso = $ctx['acceso'];
    $idConcurso = (int)$ctx['id_concurso'];
    $idConcursoMenu = $idConcurso;
    include dirname(__DIR__, 2) . '/includes/sidebar_concurso.php';
?>
<div class="topbar">
    <div class="topbar-inner">
        <div>
            <h1><?php echo omr_h($title); ?></h1>
            <p><?php echo omr_h($nombreConcurso); ?> | <?php echo omr_h($ctx['ambito']); ?></p>
        </div>
        <div class="row">
            <a href="../panel.php">Panel</a>
            <a href="index.php?id_concurso=<?php echo (int)$ctx['id_concurso']; ?>">OMR</a>
        </div>
    </div>
</div>
<main class="container">
    <?php
}

function omr_page_footer(): void
{
    ?>
</main>
</body>
</html>
    <?php
}
