<?php
/**
 * Script para generar un nuevo proyecto de Crono Time
 * Duplica la estructura base y personaliza la configuración
 */

header('Content-Type: application/json');

$response = [
    'success' => false,
    'message' => '',
    'path' => '',
    'url' => ''
];

try {
    // Validar datos del formulario
    if (!isset($_POST['raceName']) || empty($_POST['raceName'])) {
        throw new Exception('El nombre de la carrera es requerido');
    }
    
    if (!isset($_POST['raceSlug']) || empty($_POST['raceSlug'])) {
        throw new Exception('El slug del proyecto es requerido');
    }
    
    if (!isset($_POST['distances']) || empty($_POST['distances'])) {
        throw new Exception('Debes seleccionar al menos una distancia');
    }
    
    $raceName = trim($_POST['raceName']);
    $raceSlug = trim($_POST['raceSlug']);
    $raceDate = isset($_POST['raceDate']) ? trim($_POST['raceDate']) : '';
    $distances = $_POST['distances'];
    
    // Validar que cada distancia tenga su archivo
    foreach ($distances as $dist) {
        $fileKey = 'dataFiles'; // FormData crea arrays anidados
        if (!isset($_FILES[$fileKey]) || !isset($_FILES[$fileKey]['name'][$dist])) {
            throw new Exception("Falta el archivo JSON para la distancia {$dist}");
        }
        
        if ($_FILES[$fileKey]['error'][$dist] !== UPLOAD_ERR_OK) {
            throw new Exception("Error al cargar el archivo para la distancia {$dist}");
        }
    }
    
    // Validar formato del slug
    if (!preg_match('/^[a-z0-9-]+$/', $raceSlug)) {
        throw new Exception('El slug solo puede contener minúsculas, números y guiones');
    }
    
    // Directorio base (un nivel arriba del actual php/)
    $baseDir = dirname(__DIR__);
    $projectsDir = dirname($baseDir) . '/crono-projects';
    
    // Crear directorio de proyectos si no existe
    if (!file_exists($projectsDir)) {
        if (!mkdir($projectsDir, 0755, true)) {
            throw new Exception('No se pudo crear el directorio de proyectos');
        }
    }
    
    // Ruta del nuevo proyecto
    $newProjectPath = $projectsDir . '/' . $raceSlug;
    
    // Verificar si el proyecto ya existe
    if (file_exists($newProjectPath)) {
        throw new Exception('Ya existe un proyecto con ese slug. Usa otro identificador.');
    }
    
    // Crear directorio del proyecto
    if (!mkdir($newProjectPath, 0755, true)) {
        throw new Exception('No se pudo crear el directorio del proyecto');
    }
    
    // Lista de archivos y directorios a copiar (excluir generator y proyectos generados)
    $excludeDirs = ['crono-projects', '.git', 'node_modules'];
    $excludeFiles = [
        'generator.html',
        '.gitignore',
        '.DS_Store',
        'README.md',
        'README.old.md',
        'data_example_with_email.json'
    ];
    
    // Copiar estructura del proyecto
    copyDirectory($baseDir, $newProjectPath, $excludeDirs, $excludeFiles);
    
    // Procesar imagen de portada si existe
    $coverImagePath = null;
    if (isset($_FILES['coverImage']) && $_FILES['coverImage']['error'] === UPLOAD_ERR_OK) {
        $uploadFile = $_FILES['coverImage'];
        $ext = strtolower(pathinfo($uploadFile['name'], PATHINFO_EXTENSION));
        $allowedExts = ['jpg', 'jpeg', 'png', 'gif', 'webp'];
        
        if (in_array($ext, $allowedExts)) {
            $coverImageName = 'cover-image.' . $ext;
            $coverImagePath = $newProjectPath . '/assets/images/' . $coverImageName;
            
            if (move_uploaded_file($uploadFile['tmp_name'], $coverImagePath)) {
                $coverImagePath = 'assets/images/' . $coverImageName;
            } else {
                $coverImagePath = null;
            }
        }
    }
    
    // Personalizar archivos de configuración
    personalizeProject($newProjectPath, $raceName, $raceSlug, $raceDate, $distances, $coverImagePath);
    
    // Copiar archivos de datos JSON para cada distancia
    copyDataFiles($newProjectPath, $distances);
    
    // Crear archivo de metadata del proyecto
    createProjectMetadata($newProjectPath, $raceName, $raceSlug, $raceDate, $distances);
    
    $response['success'] = true;
    $response['message'] = 'Proyecto generado exitosamente';
    $response['path'] = $newProjectPath;
    $response['url'] = '../crono-projects/' . $raceSlug . '/index.html';
    
} catch (Exception $e) {
    $response['message'] = $e->getMessage();
}

echo json_encode($response);

/**
 * Copiar directorio recursivamente
 */
function copyDirectory($src, $dst, $excludeDirs = [], $excludeFiles = []) {
    $dir = opendir($src);
    @mkdir($dst, 0755, true);
    
    while (false !== ($file = readdir($dir))) {
        if ($file != '.' && $file != '..') {
            // Saltar directorios excluidos
            if (is_dir($src . '/' . $file) && in_array($file, $excludeDirs)) {
                continue;
            }
            
            // Saltar archivos excluidos
            if (is_file($src . '/' . $file) && in_array($file, $excludeFiles)) {
                continue;
            }
            
            if (is_dir($src . '/' . $file)) {
                copyDirectory($src . '/' . $file, $dst . '/' . $file, $excludeDirs, $excludeFiles);
            } else {
                copy($src . '/' . $file, $dst . '/' . $file);
            }
        }
    }
    
    closedir($dir);
}

/**
 * Personalizar archivos del proyecto
 */
function personalizeProject($projectPath, $raceName, $raceSlug, $raceDate, $distances, $coverImage) {
    // Actualizar index.html
    $indexPath = $projectPath . '/index.html';
    if (file_exists($indexPath)) {
        $content = file_get_contents($indexPath);
        
        // Reemplazar título
        $content = preg_replace('/<title>.*?<\/title>/', '<title>' . htmlspecialchars($raceName) . ' - Resultados</title>', $content);
        
        // Actualizar selector de distancias
        $distancesOptions = '';
        $firstDistance = true;
        foreach ($distances as $dist) {
            $selected = $firstDistance ? 'selected' : '';
            $distancesOptions .= "<option value=\"{$dist}\" {$selected}>" . strtoupper($dist) . "</option>\n                        ";
            $firstDistance = false;
        }
        
        $content = preg_replace(
            '/<select id="filter-distance">.*?<\/select>/s',
            "<select id=\"filter-distance\">\n                        {$distancesOptions}</select>",
            $content
        );
        
        // Actualizar imagen de portada si existe
        if ($coverImage) {
            $content = preg_replace(
                '/src="assets\/images\/[^"]*head[^"]*"/',
                'src="' . $coverImage . '"',
                $content
            );
        }
        
        file_put_contents($indexPath, $content);
    }
    
    // Actualizar admin.html
    $adminPath = $projectPath . '/admin.html';
    if (file_exists($adminPath)) {
        $content = file_get_contents($adminPath);
        
        // Actualizar título
        $content = preg_replace('/<title>.*?<\/title>/', '<title>' . htmlspecialchars($raceName) . ' - Admin</title>', $content);
        
        // Actualizar selector de distancias
        $distancesOptions = '<option value="">-- Selecciona una opción --</option>' . "\n";
        foreach ($distances as $dist) {
            $distancesOptions .= "                <option value=\"{$dist}\">" . strtoupper($dist) . "</option>\n";
        }
        
        $content = preg_replace(
            '/<select id="distance"[^>]*>.*?<\/select>/s',
            "<select id=\"distance\" required>\n{$distancesOptions}            </select>",
            $content
        );
        
        file_put_contents($adminPath, $content);
    }
    
    // Actualizar certificado.html
    $certificadoPath = $projectPath . '/certificado.html';
    if (file_exists($certificadoPath)) {
        $content = file_get_contents($certificadoPath);
        
        // Actualizar título
        $content = preg_replace('/<title>.*?<\/title>/', '<title>' . htmlspecialchars($raceName) . ' - Certificado</title>', $content);
        
        // Actualizar imagen de portada si existe
        if ($coverImage) {
            $content = preg_replace(
                '/<img[^>]+class="event-header-img"[^>]*>/',
                '<img src="' . $coverImage . '" alt="' . htmlspecialchars($raceName) . '" class="event-header-img">',
                $content
            );
        }
        
        file_put_contents($certificadoPath, $content);
    }
    
    // Actualizar config.js
    $configPath = $projectPath . '/config.js';
    if (file_exists($configPath)) {
        $content = file_get_contents($configPath);
        
        // Actualizar startTimes para incluir solo las distancias seleccionadas
        $startTimesObj = '';
        foreach ($distances as $dist) {
            $distUpper = strtoupper($dist);
            $startTimesObj .= "        '{$distUpper}': \"1:00:00\",\n";
        }
        
        $content = preg_replace(
            '/startTimes:\s*\{[^}]*\}/s',
            "startTimes: {\n{$startTimesObj}    }",
            $content
        );
        
        // Actualizar nombre de la app
        $content = preg_replace(
            "/appName:\s*'[^']*'/",
            "appName: '" . addslashes($raceName) . "'",
            $content
        );
        
        file_put_contents($configPath, $content);
    }
    
    // Actualizar data.js
    $dataJsPath = $projectPath . '/assets/js/data.js';
    if (file_exists($dataJsPath)) {
        $content = file_get_contents($dataJsPath);
        
        // Actualizar reloadResultsData para las distancias seleccionadas
        $firstDist = $distances[0];
        $filesArray = implode(', ', array_map(function($d) { return "'data_{$d}.json'"; }, $distances));
        
        $switchCases = '';
        foreach ($distances as $dist) {
            $switchCases .= "    } else if (distance === '{$dist}') {\n";
            $switchCases .= "        files = ['data_{$dist}.json'];\n";
        }
        
        $newReloadFunction = "window.reloadResultsData = async function(distance) {
    console.log('Recargando datos para distancia:', distance);
    let files = [];
    if (!distance) {
        files = [{$filesArray}];
{$switchCases}    } else {
        files = ['data_{$firstDist}.json'];
    }
    
    console.log('Archivos a cargar:', files);
    
    let allData = [];
    for (const file of files) {
        console.log('Cargando archivo:', file);
        const data = await loadResultsData(file);
        console.log('Datos cargados desde', file, ':', data.length, 'registros');
        allData = allData.concat(data);
    }
    
    console.log('Total de datos cargados:', allData.length, 'registros');
    resultsData = allData;
    currentFilteredResults = [...allData];
    document.dispatchEvent(new CustomEvent('resultsDataLoaded'));
}";
        
        // Reemplazar la función completa
        $content = preg_replace(
            '/window\.reloadResultsData\s*=\s*async\s*function\(distance\)\s*\{.*?document\.dispatchEvent\(new CustomEvent\(\'resultsDataLoaded\'\)\);\s*\}/s',
            $newReloadFunction,
            $content
        );
        
        // Actualizar carga inicial
        $content = preg_replace(
            "/loadResultsData\('data_[^']+\.json'\)/",
            "loadResultsData('data_{$firstDist}.json')",
            $content
        );
        
        file_put_contents($dataJsPath, $content);
    }
    
    // Actualizar certificate-qr.js
    $certPath = $projectPath . '/assets/js/certificate-qr.js';
    if (file_exists($certPath)) {
        $content = file_get_contents($certPath);
        
        // Actualizar array de distancias
        $distancesArray = "[" . implode(', ', array_map(function($d) { return "'{$d}'"; }, $distances)) . "]";
        $content = preg_replace(
            '/const\s+distances\s*=\s*\[[^\]]*\];/',
            "const distances = {$distancesArray};",
            $content
        );
        
        file_put_contents($certPath, $content);
    }
    
    // Actualizar verify-certificate.php y get-runner-by-id.php
    $phpFiles = ['verify-certificate.php', 'get-runner-by-id.php'];
    foreach ($phpFiles as $phpFile) {
        $phpPath = $projectPath . '/php/' . $phpFile;
        if (file_exists($phpPath)) {
            $content = file_get_contents($phpPath);
            
            $distancesArray = "[" . implode(', ', array_map(function($d) { return "'{$d}'"; }, $distances)) . "]";
            $content = preg_replace(
                '/\$distances\s*=\s*\[[^\]]*\];/',
                "\$distances = {$distancesArray};",
                $content
            );
            
            file_put_contents($phpPath, $content);
        }
    }
    
    // Actualizar upload-json.php
    $uploadPhpPath = $projectPath . '/php/upload-json.php';
    if (file_exists($uploadPhpPath)) {
        $content = file_get_contents($uploadPhpPath);
        
        // Generar switch cases para todas las distancias
        $switchCases = '';
        foreach ($distances as $dist) {
            $switchCases .= "        case '{$dist}':\n";
            $switchCases .= "            \$targetFilename = 'data_{$dist}.json';\n";
            $switchCases .= "            break;\n";
        }
        
        // Reemplazar el switch completo
        $newSwitch = "    // Determinar el nombre de archivo según la distancia\n" .
                    "    \$targetFilename = '';\n" .
                    "    switch (\$distance) {\n" .
                    $switchCases .
                    "        default:\n" .
                    "            \$response['message'] = 'Distancia no válida.';\n" .
                    "            echo json_encode(\$response);\n" .
                    "            exit;\n" .
                    "    }";
        
        $content = preg_replace(
            '/\/\/\s*Determinar el nombre de archivo según la distancia.*?switch\s*\(\$distance\)\s*\{.*?default:.*?exit;\s*\}/s',
            $newSwitch,
            $content
        );
        
        file_put_contents($uploadPhpPath, $content);
    }
    
    // Personalizar carpeta /analisis/
    $analisisPath = $projectPath . '/analisis';
    if (file_exists($analisisPath)) {
        // Actualizar analisis/index.html
        $analisisIndexPath = $analisisPath . '/index.html';
        if (file_exists($analisisIndexPath)) {
            $content = file_get_contents($analisisIndexPath);
            $content = preg_replace('/<title>.*?<\/title>/', '<title>' . htmlspecialchars($raceName) . ' - Resultados</title>', $content);
            
            // Actualizar selector de distancias
            $distancesOptions = '';
            $firstDistance = true;
            foreach ($distances as $dist) {
                $selected = $firstDistance ? 'selected' : '';
                $distancesOptions .= "<option value=\"{$dist}\" {$selected}>" . strtoupper($dist) . "</option>\n                        ";
                $firstDistance = false;
            }
            
            $content = preg_replace(
                '/<select id="filter-distance">.*?<\/select>/s',
                "<select id=\"filter-distance\">\n                        {$distancesOptions}</select>",
                $content
            );
            
            if ($coverImage) {
                $content = preg_replace(
                    '/<img[^>]+class="header-image"[^>]*>/',
                    '<img src="../' . $coverImage . '" alt="' . htmlspecialchars($raceName) . '" class="header-image">',
                    $content
                );
            }
            
            file_put_contents($analisisIndexPath, $content);
        }
        
        // Actualizar analisis/analisis.html
        $analisisHtmlPath = $analisisPath . '/analisis.html';
        if (file_exists($analisisHtmlPath)) {
            $content = file_get_contents($analisisHtmlPath);
            
            if ($coverImage) {
                // Actualizar imagen del banner
                $content = preg_replace(
                    '/<img[^>]+class="race-banner-img"[^>]*>/',
                    '<img src="../' . $coverImage . '" alt="' . htmlspecialchars($raceName) . '" class="race-banner-img">',
                    $content
                );
            }
            
            file_put_contents($analisisHtmlPath, $content);
        }
        
        // Actualizar analisis/config.js
        $analisisConfigPath = $analisisPath . '/config.js';
        if (file_exists($analisisConfigPath)) {
            $content = file_get_contents($analisisConfigPath);
            
            // Actualizar startTimes
            $startTimesObj = '';
            foreach ($distances as $dist) {
                $distUpper = strtoupper($dist);
                $startTimesObj .= "        '{$distUpper}': \"1:00:00\",\n";
            }
            
            $content = preg_replace(
                '/startTimes:\s*\{[^}]*\}/s',
                "startTimes: {\n{$startTimesObj}    }",
                $content
            );
            
            $content = preg_replace(
                "/appName:\s*'[^']*'/",
                "appName: '" . addslashes($raceName) . "'",
                $content
            );
            
            file_put_contents($analisisConfigPath, $content);
        }
        
        // Actualizar analisis/certificado.html
        $analisisCertificadoPath = $analisisPath . '/certificado.html';
        if (file_exists($analisisCertificadoPath)) {
            $content = file_get_contents($analisisCertificadoPath);
            
            // Actualizar título
            $content = preg_replace('/<title>.*?<\/title>/', '<title>' . htmlspecialchars($raceName) . ' - Certificado</title>', $content);
            
            // Actualizar imagen de portada si existe
            if ($coverImage) {
                $content = preg_replace(
                    '/<img[^>]+class="event-header-img"[^>]*>/',
                    '<img src="../' . $coverImage . '" alt="' . htmlspecialchars($raceName) . '" class="event-header-img">',
                    $content
                );
            }
            
            file_put_contents($analisisCertificadoPath, $content);
        }
        
        // Actualizar analisis/assets/js/analisis.js con las distancias seleccionadas
        $analisisJsPath = $analisisPath . '/assets/js/analisis.js';
        if (file_exists($analisisJsPath)) {
            $content = file_get_contents($analisisJsPath);
            
            // Actualizar Promise.all con loadResultsData según distancias seleccionadas
            $dataFiles = array_map(function($d) { 
                return "loadResultsData('data_{$d}.json')"; 
            }, $distances);
            $dataFilesStr = implode(",\n            ", $dataFiles);
            
            $content = preg_replace(
                '/const allData = await Promise\.all\(\[\s*loadResultsData\([^\]]+\]\);/s',
                "const allData = await Promise.all([\n            {$dataFilesStr}\n        ]);",
                $content
            );
            
            file_put_contents($analisisJsPath, $content);
        }
        
        // Optimizar carpeta analisis: eliminar archivos duplicados
        optimizeAnalisisFolder($projectPath . '/analisis');
    }
}

/**
 * Optimizar carpeta /analisis/ eliminando carpetas data y php (usará las de la raíz)
 */
function optimizeAnalisisFolder($analisisPath) {
    if (!file_exists($analisisPath)) {
        return;
    }
    
    // Eliminar solo carpetas data y php (usará las del directorio padre)
    $dirsToRemove = ['data', 'php'];
    foreach ($dirsToRemove as $dir) {
        $dirPath = $analisisPath . '/' . $dir;
        if (file_exists($dirPath)) {
            removeDirectory($dirPath);
        }
    }
    
    // NO eliminar assets/ - analisis mantiene sus propios CSS, JS e imágenes
    // Solo se actualizan las rutas de datos en los archivos JS (ya hecho en personalizeProject)
}

/**
 * Eliminar directorio recursivamente
 */
function removeDirectory($dir) {
    if (!file_exists($dir)) {
        return;
    }
    
    $files = array_diff(scandir($dir), ['.', '..']);
    foreach ($files as $file) {
        $path = $dir . '/' . $file;
        is_dir($path) ? removeDirectory($path) : unlink($path);
    }
    rmdir($dir);
}

/**
 * Convertir CSV a JSON
 */
function csvToJson($csvFile) {
    $rows = [];
    $headers = [];
    
    // Verificar que el archivo existe
    if (!file_exists($csvFile)) {
        throw new Exception("El archivo CSV no existe");
    }
    
    // Verificar que el archivo no esté vacío
    if (filesize($csvFile) == 0) {
        throw new Exception("El archivo CSV está vacío");
    }
    
    // Abrir archivo CSV
    if (($handle = fopen($csvFile, 'r')) === false) {
        throw new Exception("No se pudo abrir el archivo CSV. Verifica que el archivo no esté corrupto.");
    }
    
    $lineNumber = 0;
    $dataLines = 0;
    $headerLineNumber = 0;
    
    while (($data = fgetcsv($handle, 10000, ',')) !== false) {
        $lineNumber++;
        
        // Saltar líneas vacías
        if (empty($data) || (count($data) == 1 && trim($data[0]) == '')) {
            continue;
        }
        
        // Saltar línea que contiene "All races" en el primer elemento
        $firstCell = isset($data[0]) ? trim($data[0]) : '';
        if ($firstCell == 'All races' || strpos($firstCell, 'All races') !== false) {
            continue;
        }
        
        // Primera línea con datos = encabezados
        if (empty($headers)) {
            $headers = array_map('trim', $data);
            $headerLineNumber = $lineNumber;
            
            // Validar que hay encabezados
            if (empty($headers) || count(array_filter($headers)) == 0) {
                fclose($handle);
                throw new Exception("El CSV no contiene encabezados válidos en la línea {$lineNumber}");
            }
            
            // Validar que tenga al menos los campos básicos
            $hasPos = in_array('Pos', $headers);
            $hasGender = in_array('Gender', $headers);
            $hasAthlete = in_array('Athlete', $headers) || in_array('Name', $headers);
            $hasBib = in_array('Bib', $headers) || in_array('Bib#', $headers);
            $hasFinish = in_array('Chip Time', $headers) || in_array('FinishTime', $headers);
            
            $missingFields = [];
            if (!$hasPos) $missingFields[] = 'Pos';
            if (!$hasGender) $missingFields[] = 'Gender';
            if (!$hasAthlete) $missingFields[] = 'Athlete o Name';
            if (!$hasBib) $missingFields[] = 'Bib o Bib#';
            if (!$hasFinish) $missingFields[] = 'Chip Time o FinishTime';
            
            if (!empty($missingFields)) {
                fclose($handle);
                throw new Exception("El CSV no contiene los campos requeridos: " . implode(', ', $missingFields) . ". Encabezados encontrados en línea {$headerLineNumber}: " . implode(', ', array_filter(array_slice($headers, 0, 15))));
            }
            
            continue;
        }
        
        // Crear objeto asociativo para cada fila
        $row = [];
        $hasData = false;
        
        foreach ($headers as $index => $header) {
            if (!empty($header)) {
                $value = isset($data[$index]) ? trim($data[$index]) : '';
                
                // Convertir "Pos" a número
                if ($header === 'Pos') {
                    if (is_numeric($value)) {
                        $row[$header] = (int)$value;
                        $hasData = true;
                    } else {
                        // Si Pos no es numérico, esta fila no es válida
                        continue 2;
                    }
                }
                // Convertir "Bib" o "Bib#" a número
                elseif (($header === 'Bib' || $header === 'Bib#') && is_numeric($value)) {
                    $row[$header] = (int)$value;
                }
                // Guardar otros valores como string
                else {
                    $row[$header] = $value;
                }
            }
        }
        
        // Solo agregar si tiene datos relevantes (al menos Pos debe existir)
        if ($hasData && isset($row['Pos'])) {
            $rows[] = $row;
            $dataLines++;
        }
    }
    
    fclose($handle);
    
    // Verificar que se leyeron datos
    if (empty($rows)) {
        throw new Exception("El CSV no contiene filas de datos válidas. Total de líneas procesadas: {$lineNumber}, Encabezados en línea: {$headerLineNumber}, Filas con datos: {$dataLines}. Verifica que el archivo tenga el formato correcto con encabezados en la línea después de 'All races'.");
    }
    
    return $rows;
}

/**
 * Copiar archivos de datos CSV y convertirlos a JSON para cada distancia
 */
function copyDataFiles($projectPath, $distances) {
    $dataDir = $projectPath . '/data';
    
    if (!file_exists($dataDir)) {
        mkdir($dataDir, 0755, true);
    }
    
    // Crear subdirectorio de backups
    $backupDir = $dataDir . '/backups';
    if (!file_exists($backupDir)) {
        mkdir($backupDir, 0755, true);
    }
    
    // Procesar cada archivo de distancia
    foreach ($distances as $dist) {
        if (isset($_FILES['dataFiles']) && isset($_FILES['dataFiles']['tmp_name'][$dist])) {
            $tmpName = $_FILES['dataFiles']['tmp_name'][$dist];
            $fileName = $_FILES['dataFiles']['name'][$dist];
            $fileError = $_FILES['dataFiles']['error'][$dist];
            
            if ($fileError === UPLOAD_ERR_OK) {
                // Validar extensión del archivo
                $ext = strtolower(pathinfo($fileName, PATHINFO_EXTENSION));
                
                if ($ext !== 'csv') {
                    throw new Exception("El archivo para {$dist} debe ser un CSV (se recibió .{$ext})");
                }
                
                try {
                    // Convertir CSV a JSON
                    $jsonData = csvToJson($tmpName);
                    
                    if (empty($jsonData)) {
                        throw new Exception("No se pudieron extraer datos del CSV");
                    }
                    
                    // Agregar UniqueId a cada corredor si no lo tiene
                    $processedData = addUniqueIdsToRunners($jsonData);
                    
                    // Guardar archivo procesado con el nombre correcto
                    $targetFile = $dataDir . '/data_' . $dist . '.json';
                    
                    // Asegurar que el JSON sea válido y esté bien formateado
                    $jsonOutput = json_encode($processedData, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
                    
                    if ($jsonOutput === false) {
                        throw new Exception("Error al codificar JSON: " . json_last_error_msg());
                    }
                    
                    if (file_put_contents($targetFile, $jsonOutput) === false) {
                        throw new Exception("No se pudo guardar el archivo de datos");
                    }
                    
                    // Verificar que el archivo se guardó correctamente
                    if (!file_exists($targetFile) || filesize($targetFile) == 0) {
                        throw new Exception("El archivo de datos no se guardó correctamente (archivo vacío)");
                    }
                    
                } catch (Exception $e) {
                    throw new Exception("Error procesando CSV para {$dist}: " . $e->getMessage());
                }
            } else {
                $errorMessages = [
                    UPLOAD_ERR_INI_SIZE => 'El archivo excede el tamaño máximo permitido por el servidor',
                    UPLOAD_ERR_FORM_SIZE => 'El archivo excede el tamaño máximo permitido',
                    UPLOAD_ERR_PARTIAL => 'El archivo se subió parcialmente',
                    UPLOAD_ERR_NO_FILE => 'No se subió ningún archivo',
                    UPLOAD_ERR_NO_TMP_DIR => 'Falta la carpeta temporal',
                    UPLOAD_ERR_CANT_WRITE => 'Error al escribir el archivo en el disco',
                    UPLOAD_ERR_EXTENSION => 'La subida fue detenida por una extensión de PHP'
                ];
                
                $errorMsg = isset($errorMessages[$fileError]) ? $errorMessages[$fileError] : "Error desconocido ({$fileError})";
                throw new Exception("Error al subir archivo para {$dist}: {$errorMsg}");
            }
        }
    }
}

/**
 * Agregar UniqueId a cada corredor si no lo tiene
 */
function addUniqueIdsToRunners($runners) {
    if (!is_array($runners)) {
        return $runners;
    }
    
    $processedRunners = [];
    
    foreach ($runners as $runner) {
        // Si no tiene UniqueId, generarlo
        if (!isset($runner['UniqueId']) || empty($runner['UniqueId'])) {
            // Generar ID único basado en varios campos
            $uniqueString = '';
            
            // Usar campos disponibles para generar un ID único
            if (isset($runner['Athlete'])) {
                $uniqueString .= $runner['Athlete'];
            } elseif (isset($runner['name'])) {
                $uniqueString .= $runner['name'];
            }
            
            if (isset($runner['Bib'])) {
                $uniqueString .= '-' . $runner['Bib'];
            } elseif (isset($runner['bib'])) {
                $uniqueString .= '-' . $runner['bib'];
            }
            
            if (isset($runner['Race'])) {
                $uniqueString .= '-' . $runner['Race'];
            } elseif (isset($runner['distance'])) {
                $uniqueString .= '-' . $runner['distance'];
            }
            
            if (isset($runner['Category'])) {
                $uniqueString .= '-' . $runner['Category'];
            } elseif (isset($runner['category'])) {
                $uniqueString .= '-' . $runner['category'];
            }
            
            // Si tenemos algo para generar el ID
            if (!empty($uniqueString)) {
                // Generar hash MD5 y tomar los primeros 9 caracteres
                $runner['UniqueId'] = strtoupper(substr(md5($uniqueString . microtime(true)), 0, 9));
            } else {
                // Si no hay datos, generar ID aleatorio de 9 caracteres hexadecimales
                $runner['UniqueId'] = strtoupper(substr(bin2hex(random_bytes(5)), 0, 9));
            }
        }
        
        // Normalizar estructura de datos
        $normalizedRunner = normalizeRunnerData($runner);
        $processedRunners[] = $normalizedRunner;
    }
    
    return $processedRunners;
}

/**
 * Normalizar datos del corredor al formato esperado por el sistema
 * IMPORTANTE: El orden de los campos es crítico para que data.js detecte correctamente los parciales
 * Orden esperado: UniqueId → Pos → Athlete → Bib → Category → Race → Gender → Parciales → FinishTime → GunTime
 */
function normalizeRunnerData($runner) {
    $normalized = [];
    
    // 1. UniqueId (siempre primero)
    if (isset($runner['UniqueId'])) {
        $normalized['UniqueId'] = $runner['UniqueId'];
    }
    
    // 2. Posición
    if (isset($runner['Pos'])) {
        $normalized['Pos'] = is_numeric($runner['Pos']) ? (int)$runner['Pos'] : 0;
    }
    
    // 3. Normalizar nombre -> Athlete
    $normalized['Athlete'] = $runner['Athlete'] ?? $runner['name'] ?? '';
    
    // 4. Normalizar número de corredor (puede ser Bib o Bib#) -> Bib
    $normalized['Bib'] = $runner['Bib#'] ?? $runner['Bib'] ?? $runner['bib'] ?? $runner['NumeroCorredor'] ?? '';
    
    // 5. Normalizar categoría -> Category
    $normalized['Category'] = $runner['Category'] ?? $runner['category'] ?? $runner['Class'] ?? '';
    
    // 6. Normalizar distancia -> Race
    $distance = $runner['Race'] ?? $runner['distance'] ?? '';
    $normalized['Race'] = $distance;
    
    // 7. Normalizar género -> Gender
    $normalized['Gender'] = $runner['Gender'] ?? $runner['gender'] ?? '';
    
    // 8. PARCIALES - Detectar y copiar en orden
    // data.js detecta parciales como campos entre Bib y GunTime que no sean Category, Race, Gender
    // Por eso es crítico el orden
    $parciales = [];
    foreach ($runner as $key => $value) {
        // Detectar parciales por formato "X.X km" o "XX km"
        if (preg_match('/^(\d+(?:\.\d+)?)\s*km$/i', $key)) {
            $parciales[$key] = normalizeTimeString($value);
        }
        // Detectar parciales ya nombrados como "Parcial 1", "MP1", "mp1"
        elseif (preg_match('/^(Parcial\s+\d+|MP\d+)$/i', $key)) {
            $parciales[$key] = normalizeTimeString($value);
        }
    }
    
    // Ordenar parciales por distancia si tienen formato "X.X km"
    uksort($parciales, function($a, $b) {
        $distA = 0;
        $distB = 0;
        if (preg_match('/(\d+(?:\.\d+)?)/', $a, $matchA)) {
            $distA = floatval($matchA[1]);
        }
        if (preg_match('/(\d+(?:\.\d+)?)/', $b, $matchB)) {
            $distB = floatval($matchB[1]);
        }
        return $distA <=> $distB;
    });
    
    // Agregar parciales al array normalizado
    foreach ($parciales as $key => $value) {
        $normalized[$key] = $value;
    }
    
    // 9. Tiempo Chip -> FinishTime (como string)
    if (isset($runner['Chip Time'])) {
        $normalized['FinishTime'] = normalizeTimeString($runner['Chip Time']);
    } elseif (isset($runner['FinishTime'])) {
        $normalized['FinishTime'] = normalizeTimeString($runner['FinishTime']);
    } elseif (isset($runner['finishTime'])) {
        // Si ya es un objeto, extraer el time
        if (is_array($runner['finishTime']) && isset($runner['finishTime']['time'])) {
            $normalized['FinishTime'] = $runner['finishTime']['time'];
        } else {
            $normalized['FinishTime'] = normalizeTimeString($runner['finishTime']);
        }
    }
    
    // 10. Tiempo Oficial -> GunTime (como string, siempre al final)
    if (isset($runner['Gun Time'])) {
        $normalized['GunTime'] = normalizeTimeString($runner['Gun Time']);
    } elseif (isset($runner['GunTime'])) {
        $normalized['GunTime'] = normalizeTimeString($runner['GunTime']);
    } elseif (isset($runner['gunTime'])) {
        // Si ya es un objeto, extraer el time
        if (is_array($runner['gunTime']) && isset($runner['gunTime']['time'])) {
            $normalized['GunTime'] = $runner['gunTime']['time'];
        } else {
            $normalized['GunTime'] = normalizeTimeString($runner['gunTime']);
        }
    }
    
    // Si no hay GunTime pero hay FinishTime, copiar
    if (!isset($normalized['GunTime']) && isset($normalized['FinishTime'])) {
        $normalized['GunTime'] = $normalized['FinishTime'];
    }
    
    // Si no hay FinishTime pero hay GunTime, copiar
    if (!isset($normalized['FinishTime']) && isset($normalized['GunTime'])) {
        $normalized['FinishTime'] = $normalized['GunTime'];
    }
    
    return $normalized;
}

/**
 * Normalizar string de tiempo a formato HH:MM:SS
 */
function normalizeTimeString($timeValue) {
    // Si es un array con 'time', extraerlo
    if (is_array($timeValue)) {
        if (isset($timeValue['time'])) {
            $timeValue = $timeValue['time'];
        } else {
            $timeValue = reset($timeValue);
        }
    }
    
    // Si no es string, convertir
    if (!is_string($timeValue)) {
        return '';
    }
    
    // Limpiar el valor
    $timeValue = trim($timeValue);
    
    // Si está vacío, retornar cadena vacía
    if (empty($timeValue)) {
        return '';
    }
    
    // Formato HH:MM:SS o H:MM:SS
    if (preg_match('/^(\d{1,2}):(\d{2}):(\d{2})$/', $timeValue, $matches)) {
        $hours = str_pad($matches[1], 2, '0', STR_PAD_LEFT);
        $minutes = $matches[2];
        $seconds = $matches[3];
        return "{$hours}:{$minutes}:{$seconds}";
    }
    // Formato MM:SS (sin horas) - agregar hora 00
    elseif (preg_match('/^(\d{1,2}):(\d{2})$/', $timeValue, $matches)) {
        $minutes = str_pad($matches[1], 2, '0', STR_PAD_LEFT);
        $seconds = $matches[2];
        return "00:{$minutes}:{$seconds}";
    }
    
    return '';
}

/**
 * Crear archivo de metadata del proyecto
 */
function createProjectMetadata($projectPath, $raceName, $raceSlug, $raceDate, $distances) {
    $metadata = [
        'name' => $raceName,
        'slug' => $raceSlug,
        'date' => $raceDate,
        'distances' => $distances,
        'created' => date('Y-m-d H:i:s'),
        'version' => '1.0.0'
    ];
    
    $metadataPath = $projectPath . '/project-metadata.json';
    file_put_contents($metadataPath, json_encode($metadata, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE));
}
?>
