<?php
// concurso_nobel/includes/importar_participantes_excel.php
// Lectura y validación de archivos XLSX para importación de participantes.

declare(strict_types=1);

require_once __DIR__ . '/importar_participantes_common.php';

function ip_xlsx_shared_strings(ZipArchive $zip): array
{
    $strings = [];
    $contenido = $zip->getFromName('xl/sharedStrings.xml');

    if ($contenido === false) {
        return $strings;
    }

    $xml = simplexml_load_string($contenido);

    if (!$xml) {
        return $strings;
    }

    $xml->registerXPathNamespace('a', 'http://schemas.openxmlformats.org/spreadsheetml/2006/main');

    foreach ($xml->si as $si) {
        $texto = '';
        $si->registerXPathNamespace('a', 'http://schemas.openxmlformats.org/spreadsheetml/2006/main');
        $partes = $si->xpath('.//a:t');

        if ($partes) {
            foreach ($partes as $t) {
                $texto .= (string)$t;
            }
        }

        $strings[] = $texto;
    }

    return $strings;
}

function ip_xlsx_valor_celda(SimpleXMLElement $cell, array $sharedStrings): string
{
    $tipo = (string)$cell['t'];

    if ($tipo === 's') {
        $indice = (int)((string)$cell->v);
        return trim((string)($sharedStrings[$indice] ?? ''));
    }

    if ($tipo === 'inlineStr') {
        $cell->registerXPathNamespace('a', 'http://schemas.openxmlformats.org/spreadsheetml/2006/main');
        $partes = $cell->xpath('.//a:t');
        $texto = '';

        if ($partes) {
            foreach ($partes as $t) {
                $texto .= (string)$t;
            }
        }

        return trim($texto);
    }

    return trim((string)$cell->v);
}

function ip_xlsx_columna(string $ref): string
{
    if (preg_match('/^([A-Z]+)/i', $ref, $m)) {
        return strtoupper($m[1]);
    }

    return '';
}

function ip_columna_excel_a_numero(string $col): int
{
    $col = strtoupper($col);
    $num = 0;
    $len = strlen($col);

    for ($i = 0; $i < $len; $i++) {
        $num = ($num * 26) + (ord($col[$i]) - ord('A') + 1);
    }

    return $num;
}

function ip_leer_xlsx(string $ruta): array
{
    if (!class_exists('ZipArchive')) {
        throw new RuntimeException('El servidor no tiene habilitada la extensión ZipArchive para leer archivos .xlsx.');
    }

    $zip = new ZipArchive();
    $open = $zip->open($ruta);

    if ($open !== true) {
        throw new RuntimeException('No se pudo abrir el archivo XLSX. Verifica que sea un Excel válido.');
    }

    $sheetName = 'xl/worksheets/sheet1.xml';

    if ($zip->getFromName($sheetName) === false) {
        $zip->close();
        throw new RuntimeException('No se encontró la primera hoja del Excel.');
    }

    $sharedStrings = ip_xlsx_shared_strings($zip);
    $sheet = $zip->getFromName($sheetName);
    $zip->close();

    if ($sheet === false) {
        throw new RuntimeException('No se pudo leer la primera hoja del Excel.');
    }

    $xml = simplexml_load_string($sheet);

    if (!$xml) {
        throw new RuntimeException('No se pudo interpretar el XML interno del Excel.');
    }

    $filas = [];

    foreach ($xml->sheetData->row as $row) {
        $fila = [];
        $maxCol = 0;

        foreach ($row->c as $cell) {
            $col = ip_xlsx_columna((string)$cell['r']);

            if ($col === '') {
                continue;
            }

            $numeroCol = ip_columna_excel_a_numero($col);
            $maxCol = max($maxCol, $numeroCol);
            $fila[$numeroCol] = ip_xlsx_valor_celda($cell, $sharedStrings);
        }

        if ($maxCol <= 0) {
            continue;
        }

        $ordenada = [];

        for ($i = 1; $i <= $maxCol; $i++) {
            $ordenada[] = (string)($fila[$i] ?? '');
        }

        $filas[] = $ordenada;
    }

    return $filas;
}

function ip_archivo_parece_xlsx(string $tmp): bool
{
    $handle = @fopen($tmp, 'rb');

    if (!$handle) {
        return false;
    }

    $firma = fread($handle, 4);
    fclose($handle);

    return $firma === "PK\x03\x04";
}

function ip_leer_archivo_importado(array $file): array
{
    if (($file['error'] ?? UPLOAD_ERR_NO_FILE) !== UPLOAD_ERR_OK) {
        throw new RuntimeException('No se pudo subir el archivo. Código de error: ' . (int)($file['error'] ?? 0));
    }

    $size = (int)($file['size'] ?? 0);

    if ($size <= 0) {
        throw new RuntimeException('El archivo está vacío.');
    }

    if ($size > IP_MAX_BYTES_ARCHIVO) {
        throw new RuntimeException('El archivo supera el tamaño máximo permitido para esta importación.');
    }

    $tmp = (string)$file['tmp_name'];
    $nombre = strtolower((string)($file['name'] ?? ''));
    $extension = pathinfo($nombre, PATHINFO_EXTENSION);

    if ($extension !== 'xlsx') {
        throw new RuntimeException('Formato no permitido. Sube únicamente un archivo Excel .xlsx.');
    }

    if (!is_uploaded_file($tmp) && !is_file($tmp)) {
        throw new RuntimeException('No se encontró el archivo temporal subido.');
    }

    if (!ip_archivo_parece_xlsx($tmp)) {
        throw new RuntimeException('El archivo no parece ser un XLSX válido.');
    }

    return ip_leer_xlsx($tmp);
}

function ip_convertir_filas_a_registros(array $filas): array
{
    if (empty($filas)) {
        throw new RuntimeException('El archivo no contiene filas.');
    }

    $encabezado = array_shift($filas);
    $mapa = [];

    foreach ($encabezado as $idx => $columna) {
        $normalizada = ip_norm_header((string)$columna);

        if ($normalizada !== '') {
            $mapa[$normalizada] = $idx;
        }
    }

    $requeridas = ['CODIGO_MODULAR', 'DNI', 'APELLIDO_PATERNO', 'APELLIDO_MATERNO', 'NOMBRES'];
    $faltantes = [];

    foreach ($requeridas as $req) {
        if (!array_key_exists($req, $mapa)) {
            $faltantes[] = $req;
        }
    }

    if (!empty($faltantes)) {
        throw new RuntimeException('La plantilla no tiene estas columnas obligatorias: ' . implode(', ', $faltantes));
    }

    $registros = [];
    $numeroFila = 2;

    foreach ($filas as $fila) {
        $vacia = true;

        foreach ($fila as $valor) {
            if (trim((string)$valor) !== '') {
                $vacia = false;
                break;
            }
        }

        if ($vacia) {
            $numeroFila++;
            continue;
        }

        if (count($registros) >= IP_MAX_FILAS_IMPORTAR) {
            throw new RuntimeException('El archivo supera el máximo de ' . IP_MAX_FILAS_IMPORTAR . ' filas para importar.');
        }

        $registros[] = [
            'fila' => $numeroFila,
            'codigo_modular' => (string)($fila[$mapa['CODIGO_MODULAR']] ?? ''),
            'dni' => (string)($fila[$mapa['DNI']] ?? ''),
            'apellido_paterno' => (string)($fila[$mapa['APELLIDO_PATERNO']] ?? ''),
            'apellido_materno' => (string)($fila[$mapa['APELLIDO_MATERNO']] ?? ''),
            'nombres' => (string)($fila[$mapa['NOMBRES']] ?? '')
        ];

        $numeroFila++;
    }

    return $registros;
}