<?php

namespace App\Utils;

/**
 * Procesador de archivos CSV por chunks para optimizar memoria
 */
class ChunkProcessor
{
    private $chunk_size;
    private $memory_limit;

    public function __construct($chunk_size = 1000, $memory_limit_mb = 64)
    {
        $this->chunk_size = $chunk_size;
        $this->memory_limit = $memory_limit_mb * 1024 * 1024; // Convertir a bytes
    }

    /**
     * Procesa un archivo CSV en chunks para optimizar memoria
     */
    public function processCSVInChunks($file_path, $expected_header, $processor_callback)
    {
        if (!is_callable($processor_callback)) {
            throw new \InvalidArgumentException('El procesador debe ser una función callable');
        }

        $handle = fopen($file_path, 'r');
        if (!$handle) {
            throw new \RuntimeException('No se pudo abrir el archivo CSV');
        }

        // Leer y validar encabezado
        $header = array_map('trim', fgetcsv($handle, 2000, ','));
        if ($header !== $expected_header) {
            fclose($handle);
            throw new \InvalidArgumentException('Encabezado del CSV no coincide con el esperado');
        }

        $total_processed = 0;
        $chunk_data = [];
        $row_number = 1;

        while (($data = fgetcsv($handle, 2000, ',')) !== FALSE) {
            $row_number++;

            // Saltar filas vacías
            if (empty(array_filter($data))) {
                continue;
            }

            // Añadir fila al chunk actual
            $chunk_data[] = [
                'data' => $data,
                'row_number' => $row_number,
                'combined' => array_combine($expected_header, $data)
            ];

            // Procesar chunk cuando alcance el tamaño límite
            if (count($chunk_data) >= $this->chunk_size) {
                $processed = $processor_callback($chunk_data, $total_processed);
                $total_processed += $processed;
                $chunk_data = []; // Limpiar chunk

                // Verificar uso de memoria
                if (memory_get_usage() > $this->memory_limit) {
                    gc_collect_cycles(); // Forzar garbage collection
                }
            }
        }

        // Procesar último chunk si tiene datos
        if (!empty($chunk_data)) {
            $processed = $processor_callback($chunk_data, $total_processed);
            $total_processed += $processed;
        }

        fclose($handle);
        return $total_processed;
    }

    /**
     * Obtiene estadísticas del archivo CSV sin cargarlo completamente en memoria
     */
    public function getCSVStats($file_path)
    {
        $handle = fopen($file_path, 'r');
        if (!$handle) {
            return null;
        }

        $stats = [
            'total_rows' => 0,
            'estimated_size' => filesize($file_path),
            'encoding' => 'UTF-8' // Por defecto
        ];

        // Saltar encabezado
        fgetcsv($handle, 2000, ',');

        // Contar filas de manera eficiente
        while (fgetcsv($handle, 2000, ',') !== FALSE) {
            $stats['total_rows']++;
        }

        fclose($handle);
        return $stats;
    }

    /**
     * Crea un procesador de chunk específico para importación de alumnos
     */
    public static function createAlumnosChunkProcessor($pdo, $result_manager)
    {
        return function ($chunk_data, $offset) use ($pdo, $result_manager) {
            $processed = 0;

            foreach ($chunk_data as $row_info) {
                $data = $row_info['combined'];
                $row_number = $row_info['row_number'];

                // Procesar cada fila (similar a la lógica existente)
                $run = $data['run'] ?? '';
                $fecha_nac = $data['fecha_nac'] ?? '';

                // Validaciones
                if (!\App\Utils\validarRUN($run)) {
                    $result_manager->addError("RUN inválido: " . htmlspecialchars($run), $row_number);
                    continue;
                }

                if (!ImportValidator::validateBirthDate($fecha_nac)) {
                    $result_manager->addError("Fecha de nacimiento inválida", $row_number);
                    continue;
                }

                // Formatear RUN
                $run_cleaned = preg_replace('/[^0-9kK]/', '', strtoupper($run));
                $run_formatted = strlen($run_cleaned) > 1
                    ? substr($run_cleaned, 0, -1) . '-' . substr($run_cleaned, -1)
                    : $run_cleaned;

                // Preparar datos para guardar
                $data_to_save = array_filter($data, fn($value) => $value !== null && $value !== '');
                unset($data_to_save['run']);

                // Usar upsert
                \App\DB\upsert($pdo, 'alumnos', $data_to_save, 'run', $run_formatted);
                $processed++;
                $result_manager->incrementProcessed();
            }

            return $processed;
        };
    }

    /**
     * Crea un procesador de chunk específico para importación de familiares
     */
    public static function createFamiliaresChunkProcessor($pdo, $result_manager)
    {
        return function ($chunk_data, $offset) use ($pdo, $result_manager) {
            $processed = 0;

            foreach ($chunk_data as $row_info) {
                $data = $row_info['combined'];
                $row_number = $row_info['row_number'];

                $run = $data['run'] ?? '';

                // Validaciones
                if (!\App\Utils\validarRUN($run)) {
                    $result_manager->addError("RUN inválido: " . htmlspecialchars($run), $row_number);
                    continue;
                }

                // Validaciones adicionales
                if (!empty($data['email']) && !ImportValidator::validateEmail($data['email'])) {
                    $result_manager->addWarning("Email inválido, se omitirá", $row_number);
                    unset($data['email']);
                }

                if (!empty($data['telefono']) && !ImportValidator::validatePhone($data['telefono'])) {
                    $result_manager->addWarning("Teléfono inválido, se omitirá", $row_number);
                    unset($data['telefono']);
                }

                // Formatear RUN
                $run_cleaned = preg_replace('/[^0-9kK]/', '', strtoupper($run));
                $run_formatted = strlen($run_cleaned) > 1
                    ? substr($run_cleaned, 0, -1) . '-' . substr($run_cleaned, -1)
                    : $run_cleaned;

                // Preparar datos para guardar
                $data_to_save = array_filter($data, fn($value) => $value !== null && $value !== '');
                unset($data_to_save['run']);

                // Usar upsert
                \App\DB\upsert($pdo, 'familiares', $data_to_save, 'run', $run_formatted);
                $processed++;
                $result_manager->incrementProcessed();
            }

            return $processed;
        };
    }
}
