<?php
declare(strict_types=1);

header('Content-Type: application/json; charset=utf-8');

$autoload = __DIR__ . '/../vendor/autoload.php';
if (is_file($autoload)) {
    require_once $autoload;
}

if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
    http_response_code(405);
    echo json_encode(['error' => 'Metodo no permitido']);
    exit;
}

$uploadDir = __DIR__ . '/../storage/uploads';
if (!is_dir($uploadDir)) {
    mkdir($uploadDir, 0755, true);
}

function save_upload(string $field, string $uploadDir): ?array
{
    if (!isset($_FILES[$field]) || $_FILES[$field]['error'] === UPLOAD_ERR_NO_FILE) {
        return null;
    }

    if ($_FILES[$field]['error'] !== UPLOAD_ERR_OK) {
        throw new RuntimeException("No se pudo subir el archivo {$field}");
    }

    $originalName = basename((string) $_FILES[$field]['name']);
    $safeName = preg_replace('/[^A-Za-z0-9._-]+/', '_', $originalName) ?: $field;
    $target = $uploadDir . '/' . date('Ymd_His') . '_' . $field . '_' . $safeName;

    if (!move_uploaded_file((string) $_FILES[$field]['tmp_name'], $target)) {
        throw new RuntimeException("No se pudo guardar el archivo {$field}");
    }

    return [
        'name' => $originalName,
        'path' => $target,
        'size' => filesize($target),
    ];
}

function decode_pdf_string(string $value): string
{
    $value = preg_replace_callback('/\\\\([0-7]{1,3})/', function (array $match): string {
        return chr(octdec($match[1]));
    }, $value);

    $value = str_replace(
        ['\\\\', '\\(', '\\)', '\\n', '\\r', '\\t', '\\b', '\\f'],
        ['\\', '(', ')', "\n", "\r", "\t", "\b", "\f"],
        $value
    );

    if (str_starts_with($value, "\xFE\xFF")) {
        $converted = @iconv('UTF-16BE', 'UTF-8//IGNORE', substr($value, 2));
        if ($converted !== false) {
            return $converted;
        }
    }

    $converted = @iconv('Windows-1252', 'UTF-8//IGNORE', $value);
    return $converted !== false ? $converted : $value;
}

function extract_pdf_text_from_streams(string $path): string
{
    $pdf = file_get_contents($path);
    if ($pdf === false) {
        throw new RuntimeException('No se pudo leer el PDF');
    }

    $parts = [];
    if (!preg_match_all('/<<(.*?)>>\s*stream\s*\r?\n(.*?)\r?\nendstream/s', $pdf, $matches, PREG_SET_ORDER)) {
        return '';
    }

    foreach ($matches as $match) {
        $dict = $match[1];
        $stream = $match[2];
        if (str_contains($dict, '/FlateDecode')) {
            $decoded = @gzuncompress($stream);
            if ($decoded === false) {
                $decoded = @gzdecode($stream);
            }
            if ($decoded !== false) {
                $stream = $decoded;
            }
        }

        preg_match_all('/\((?:\\\\.|[^\\\\)])*\)\s*Tj/s', $stream, $tjMatches);
        foreach ($tjMatches[0] as $item) {
            if (preg_match('/\(((?:\\\\.|[^\\\\)])*)\)\s*Tj/s', $item, $textMatch)) {
                $parts[] = decode_pdf_string($textMatch[1]);
            }
        }

        preg_match_all('/\[(.*?)\]\s*TJ/s', $stream, $arrayMatches);
        foreach ($arrayMatches[1] as $arrayText) {
            preg_match_all('/\((?:\\\\.|[^\\\\)])*\)/s', $arrayText, $stringMatches);
            foreach ($stringMatches[0] as $raw) {
                $parts[] = decode_pdf_string(substr($raw, 1, -1));
            }
            $parts[] = "\n";
        }
    }

    return implode(' ', $parts);
}

function extract_pdf_text(string $path): string
{
    if (class_exists(\Smalot\PdfParser\Parser::class)) {
        $parser = new \Smalot\PdfParser\Parser();
        $pdf = $parser->parseFile($path);
        $text = $pdf->getText();
        if (trim($text) !== '') {
            return $text;
        }
    }

    if (function_exists('shell_exec')) {
        $cmd = 'pdftotext -layout ' . escapeshellarg($path) . ' - 2>NUL';
        $text = @shell_exec($cmd);
        if (is_string($text) && trim($text) !== '') {
            return $text;
        }
    }

    return extract_pdf_text_from_streams($path);
}

function minutes_from_time(?string $time): ?int
{
    if ($time === null || trim($time) === '') {
        return null;
    }
    if (!preg_match('/^(\d{1,2}):(\d{2})$/', trim($time), $match)) {
        return null;
    }
    return ((int) $match[1]) * 60 + (int) $match[2];
}

function hours_between(?string $start, ?string $end): string
{
    $startMinutes = minutes_from_time($start);
    $endMinutes = minutes_from_time($end);
    if ($startMinutes === null || $endMinutes === null) {
        return '';
    }
    if ($endMinutes < $startMinutes) {
        $endMinutes += 24 * 60;
    }
    return number_format(($endMinutes - $startMinutes) / 60, 2, ',', '');
}

function parse_pdf_records(string $text): array
{
    $records = [];
    $lines = preg_split('/\R+/', $text) ?: [];
    $current = null;

    foreach ($lines as $line) {
        $line = trim(preg_replace('/\s+/', ' ', $line) ?? '');
        if ($line === '') {
            continue;
        }

        if (preg_match('/^([\d.]+)-\s*(.*?)\s+Tarjeta/i', $line, $match)) {
            $current = [
                'legajo' => (string) ((int) str_replace('.', '', $match[1])),
                'empleado' => trim($match[2]),
            ];
            continue;
        }

        if ($current !== null && preg_match('/(\d{2}\/\d{2}\/\d{4}).*?((?:\d{1,2}:\d{2}\s*)+)/', $line, $match)) {
            preg_match_all('/\d{1,2}:\d{2}/', $match[2], $timeMatches);
            $times = array_values(array_unique($timeMatches[0]));
            $entrada = $times[0] ?? '';
            $salida = $times[1] ?? '';
            $records[] = [
                'fecha' => $match[1],
                'legajo' => $current['legajo'],
                'empleado' => $current['empleado'],
                'entradaPdf' => $entrada,
                'salidaPdf' => $salida,
                'horasPdf' => hours_between($entrada, $salida),
                'marcas' => $times,
            ];
            $current = null;
        }
    }

    if (count($records) > 0) {
        return $records;
    }

    $flat = trim(preg_replace('/\s+/', ' ', $text) ?? '');
    preg_match_all(
        '/([\d.]+)-\s*(.*?)\s+Tarjeta.*?(\d{2}\/\d{2}\/\d{4}).*?((?:\d{1,2}:\d{2}\s*)+)/i',
        $flat,
        $matches,
        PREG_SET_ORDER
    );
    foreach ($matches as $match) {
        preg_match_all('/\d{1,2}:\d{2}/', $match[4], $timeMatches);
        $times = array_values(array_unique($timeMatches[0]));
        $entrada = $times[0] ?? '';
        $salida = $times[1] ?? '';
        $records[] = [
            'fecha' => $match[3],
            'legajo' => (string) ((int) str_replace('.', '', $match[1])),
            'empleado' => trim($match[2]),
            'entradaPdf' => $entrada,
            'salidaPdf' => $salida,
            'horasPdf' => hours_between($entrada, $salida),
            'marcas' => $times,
        ];
    }

    return $records;
}

function col_to_number(string $letters): int
{
    $number = 0;
    foreach (str_split($letters) as $char) {
        $number = ($number * 26) + (ord(strtoupper($char)) - 64);
    }
    return $number;
}

function excel_serial_to_date($value): string
{
    if (!is_numeric($value)) {
        return '';
    }
    $days = (int) $value;
    $timestamp = strtotime('1899-12-30 +' . $days . ' days');
    return $timestamp === false ? '' : date('d/m/Y', $timestamp);
}

function normalize_excel_time($value): string
{
    if ($value === null || $value === '') {
        return '';
    }
    if (is_numeric($value)) {
        $number = (float) $value;
        if ($number > 0 && $number < 1) {
            $minutes = (int) round($number * 24 * 60);
            return sprintf('%02d:%02d', intdiv($minutes, 60), $minutes % 60);
        }
        if ($number >= 0 && $number <= 24) {
            $hour = (int) floor($number);
            $minutes = (int) round(($number - $hour) * 60);
            return sprintf('%02d:%02d', $hour, $minutes);
        }
    }
    $text = trim((string) $value);
    if (preg_match('/^\d{1,2}$/', $text)) {
        return sprintf('%02d:00', (int) $text);
    }
    if (preg_match('/^\d{1,2}:\d{2}$/', $text)) {
        return $text;
    }
    return $text;
}

function read_xlsx_rows(string $path, string $sheetName = 'Aceria'): array
{
    if (!class_exists('ZipArchive')) {
        throw new RuntimeException('El PHP del servidor necesita la extension ZipArchive para leer Excel .xlsx');
    }

    $zip = new ZipArchive();
    if ($zip->open($path) !== true) {
        throw new RuntimeException('No se pudo abrir el Excel');
    }

    $sharedStrings = [];
    $sharedXml = $zip->getFromName('xl/sharedStrings.xml');
    if ($sharedXml !== false) {
        $shared = simplexml_load_string($sharedXml);
        if ($shared !== false) {
            foreach ($shared->si as $si) {
                $sharedStrings[] = trim((string) $si->t);
            }
        }
    }

    $workbook = simplexml_load_string((string) $zip->getFromName('xl/workbook.xml'));
    $rels = simplexml_load_string((string) $zip->getFromName('xl/_rels/workbook.xml.rels'));
    if ($workbook === false || $rels === false) {
        throw new RuntimeException('No se pudo leer la estructura del Excel');
    }

    $relMap = [];
    foreach ($rels->Relationship as $rel) {
        $attrs = $rel->attributes();
        $relMap[(string) $attrs['Id']] = (string) $attrs['Target'];
    }

    $sheetPath = null;
    foreach ($workbook->sheets->sheet as $sheet) {
        $attrs = $sheet->attributes();
        $relAttrs = $sheet->attributes('http://schemas.openxmlformats.org/officeDocument/2006/relationships');
        $rid = (string) ($relAttrs['id'] ?? '');
        if ((string) $attrs['name'] === $sheetName || $sheetPath === null) {
            $target = $relMap[$rid] ?? '';
            $target = ltrim($target, '/');
            $sheetPath = str_starts_with($target, 'xl/') ? $target : 'xl/' . $target;
            if ((string) $attrs['name'] === $sheetName) {
                break;
            }
        }
    }

    if ($sheetPath === null) {
        throw new RuntimeException('No se encontro una hoja para leer');
    }

    $sheetXml = $zip->getFromName($sheetPath);
    if ($sheetXml === false) {
        throw new RuntimeException('No se pudo leer la hoja del Excel');
    }

    $sheet = simplexml_load_string($sheetXml);
    if ($sheet === false) {
        throw new RuntimeException('No se pudo interpretar la hoja del Excel');
    }

    $rows = [];
    foreach ($sheet->sheetData->row as $row) {
        $rowNumber = (int) $row['r'];
        $values = [];
        foreach ($row->c as $cell) {
            $attrs = $cell->attributes();
            $ref = (string) $attrs['r'];
            preg_match('/([A-Z]+)/', $ref, $colMatch);
            $col = col_to_number($colMatch[1] ?? 'A');
            $type = (string) $attrs['t'];
            $value = isset($cell->v) ? (string) $cell->v : '';
            if ($type === 's') {
                $value = $sharedStrings[(int) $value] ?? '';
            } elseif ($type === 'inlineStr' && isset($cell->is->t)) {
                $value = (string) $cell->is->t;
            }
            $values[$col] = trim($value);
        }

        $legajo = $values[2] ?? '';
        $empleado = $values[3] ?? '';
        $tipoHora = $values[8] ?? '';
        if ($rowNumber < 10 || $legajo === '' || $empleado === '' || strtolower((string) $tipoHora) !== 'normal') {
            continue;
        }

        $fecha = $values[1] ?? '';
        if ($fecha === '' && isset($values[5])) {
            $fecha = $values[5];
        }
        $rows[] = [
            'fecha' => is_numeric($fecha) ? excel_serial_to_date($fecha) : (string) $fecha,
            'legajo' => (string) ((int) $legajo),
            'empleado' => strtoupper((string) $empleado),
            'condicion' => (string) ($values[4] ?? ''),
            'entradaObra' => normalize_excel_time($values[5] ?? ''),
            'salidaObra' => normalize_excel_time($values[6] ?? ''),
            'horasObra' => is_numeric($values[7] ?? '') ? number_format((float) $values[7], 2, ',', '') : (string) ($values[7] ?? ''),
            'tipoHora' => (string) $tipoHora,
        ];
    }

    $zip->close();
    return $rows;
}

function row_by_legajo(array $rows): array
{
    $indexed = [];
    foreach ($rows as $row) {
        $indexed[(string) $row['legajo']] = $row;
    }
    return $indexed;
}

function compare_rows(array $pdfRows, array $excelRows): array
{
    $excelByLegajo = row_by_legajo($excelRows);
    $usedExcel = [];
    $rows = [];

    foreach ($pdfRows as $pdf) {
        $excel = $excelByLegajo[$pdf['legajo']] ?? null;
        if ($excel !== null) {
            $usedExcel[$pdf['legajo']] = true;
        }

        $estado = 'Coincide';
        $estadoTipo = 'ok';
        $observaciones = '';

        if (($pdf['entradaPdf'] ?? '') === '' || ($pdf['salidaPdf'] ?? '') === '') {
            $estado = 'Fichada incompleta';
            $estadoTipo = 'danger';
            $observaciones = 'Falta entrada o salida en reloj.';
        } elseif ($excel === null) {
            $estado = 'No esta en Excel';
            $estadoTipo = 'warn';
            $observaciones = 'Existe en PDF pero no en el parte de obra.';
        } else {
            $diffs = [];
            if (($pdf['entradaPdf'] ?? '') !== ($excel['entradaObra'] ?? '')) {
                $diffs[] = 'entrada';
            }
            if (($pdf['salidaPdf'] ?? '') !== ($excel['salidaObra'] ?? '')) {
                $diffs[] = 'salida';
            }
            if (count($diffs) > 0) {
                $estado = 'Revisar ' . implode(' y ', $diffs);
                $estadoTipo = 'warn';
                $observaciones = 'Diferencia entre reloj y parte de obra.';
            }
        }

        $rows[] = [
            'fecha' => $pdf['fecha'] ?? ($excel['fecha'] ?? ''),
            'legajo' => $pdf['legajo'],
            'empleado' => strtoupper((string) ($excel['empleado'] ?? $pdf['empleado'])),
            'condicion' => (string) ($excel['condicion'] ?? ''),
            'entradaPdf' => $pdf['entradaPdf'] ?? '',
            'salidaPdf' => $pdf['salidaPdf'] ?? '',
            'horasPdf' => $pdf['horasPdf'] ?? '',
            'entradaObra' => $excel['entradaObra'] ?? '',
            'salidaObra' => $excel['salidaObra'] ?? '',
            'horasObra' => $excel['horasObra'] ?? '',
            'tipoHora' => $excel['tipoHora'] ?? 'Normal',
            'observaciones' => $observaciones,
            'estado' => $estado,
            'estadoTipo' => $estadoTipo,
            'visado' => false,
        ];
    }

    foreach ($excelRows as $excel) {
        if (isset($usedExcel[$excel['legajo']])) {
            continue;
        }
        $rows[] = [
            'fecha' => $excel['fecha'] ?? '',
            'legajo' => $excel['legajo'],
            'empleado' => strtoupper((string) $excel['empleado']),
            'condicion' => (string) ($excel['condicion'] ?? ''),
            'entradaPdf' => '',
            'salidaPdf' => '',
            'horasPdf' => '',
            'entradaObra' => $excel['entradaObra'] ?? '',
            'salidaObra' => $excel['salidaObra'] ?? '',
            'horasObra' => $excel['horasObra'] ?? '',
            'tipoHora' => $excel['tipoHora'] ?? 'Normal',
            'observaciones' => 'Existe en Excel de obra pero no en PDF.',
            'estado' => 'No esta en PDF',
            'estadoTipo' => 'warn',
            'visado' => false,
        ];
    }

    return $rows;
}

function build_summary(array $pdfRows, array $rows): array
{
    $review = 0;
    $diff = 0;
    foreach ($rows as $row) {
        if (($row['estadoTipo'] ?? '') !== 'ok') {
            $review++;
        }
        if (($row['estado'] ?? '') !== 'Coincide') {
            $diff++;
        }
    }

    return [
        'pdfCount' => count($pdfRows),
        'okCount' => max(0, count($rows) - $review),
        'reviewCount' => $review,
        'diffCount' => $diff,
    ];
}

try {
    $pdf = save_upload('pdf', $uploadDir);
    $excel = save_upload('excel', $uploadDir);

    if ($pdf === null) {
        throw new RuntimeException('Tenes que subir el PDF de Comuwin');
    }

    $pdfText = extract_pdf_text($pdf['path']);
    $pdfRows = parse_pdf_records($pdfText);
    if (count($pdfRows) === 0) {
        throw new RuntimeException('No se pudieron detectar fichadas en el PDF');
    }

    $excelRows = $excel !== null ? read_xlsx_rows($excel['path'], 'Aceria') : [];
    $rows = compare_rows($pdfRows, $excelRows);

    echo json_encode([
        'ok' => true,
        'uploads' => [
            'pdf' => ['name' => $pdf['name'], 'size' => $pdf['size']],
            'excel' => $excel ? ['name' => $excel['name'], 'size' => $excel['size']] : null,
        ],
        'summary' => build_summary($pdfRows, $rows),
        'rows' => $rows,
    ], JSON_UNESCAPED_UNICODE);
} catch (Throwable $exception) {
    http_response_code(500);
    echo json_encode([
        'ok' => false,
        'error' => $exception->getMessage(),
    ], JSON_UNESCAPED_UNICODE);
}
