<?php
header('Content-Type: application/json; charset=UTF-8');

$text = $_POST['text'] ?? '';
if (!trim($text)) { echo json_encode(['error' => 'No text provided']); exit; }

// Debug log
$firstLine = strtok($text, "\n");
file_put_contents(__DIR__ . '/../debug.txt',
    "=== " . date('Y-m-d H:i:s') . " ===\n" .
    "LEN:" . strlen($text) . " TABS:" . substr_count($firstLine, "\t") . "\n" .
    "H: " . substr($firstLine, 0, 500) . "\n===\n\n",
    FILE_APPEND
);

echo json_encode(parseInvoice($text));

// ─────────────────────────────────────────────────────────────────────────────

function parseInvoice(string $raw): array
{
    $firstLine = strtok($raw, "\n");
    $tabCount  = substr_count($firstLine, "\t");
    if ($tabCount >= 5) return parseTSV($raw);
    return parseFreeText($raw);
}

// ─── Proper RFC-4180 TSV parser (handles quoted cells with newlines/commas) ──

function parseTSVRows(string $tsv): array
{
    $rows   = [];
    $row    = [];
    $field  = '';
    $inQ    = false;
    $len    = strlen($tsv);

    for ($i = 0; $i < $len; $i++) {
        $c    = $tsv[$i];
        $next = $tsv[$i + 1] ?? '';

        if ($inQ) {
            if ($c === '"' && $next === '"') { $field .= '"'; $i++; }
            elseif ($c === '"')              { $inQ = false; }
            else                             { $field .= $c; }
        } else {
            if ($c === '"')                        { $inQ = true; }
            elseif ($c === "\t")                   { $row[] = trim($field); $field = ''; }
            elseif ($c === "\n" || $c === "\r") {
                if ($c === "\r" && $next === "\n") $i++;
                $row[] = trim($field); $field = '';
                if (!empty(array_filter($row, fn($v) => $v !== ''))) $rows[] = $row;
                $row = [];
            } else { $field .= $c; }
        }
    }
    if ($field !== '' || !empty($row)) { $row[] = trim($field); $rows[] = $row; }

    return $rows;
}

// ─── TSV Invoice Parser ───────────────────────────────────────────────────────

function parseTSV(string $raw): array
{
    $d    = emptyDoc();
    $rows = parseTSVRows($raw);

    if (empty($rows)) return $d;

    // Detect header row (first row has text labels, not dates/numbers)
    $isHeader = !preg_match('/^\d{2}\/\d{2}/', trim($rows[0][0] ?? ''))
             && !preg_match('/^\d{4,}$/', trim($rows[0][0] ?? ''));

    if ($isHeader && isset($rows[1])) {
        $headers = array_map(fn($h) => strtolower(trim($h)), $rows[0]);
        $values  = $rows[1];
    } else {
        $headers = knownHeaders();
        $values  = $rows[0];
    }

    while (count($values) < count($headers)) $values[] = '';

    // Header → value map
    $hv = [];
    foreach ($headers as $i => $h) {
        if ($h !== '') $hv[$h] = $values[$i] ?? '';
    }

    $get = function(array $keys) use ($hv): string {
        foreach ($keys as $k) {
            $v = $hv[strtolower(trim($k))] ?? '';
            if (trim($v) !== '') return trim($v);
        }
        return '';
    };

    // ── Standard fields ───────────────────────────────────────────────────────
    $d['bill_to_name']     = $get(['customer','customer name','bill to','client']);
    $d['booking_number']   = $get(['booking','booking #','booking number','bkg']);
    $d['reference_number'] = $get(['customer reference','reference','ref #','ref']);
    $d['container']        = $get(['container','cntr']);
    $d['container_number'] = $d['container'];
    $d['size']             = $get(['container size','size','sz']);
    $d['type']             = $get(['container type','type','tp']);
    $d['bol']              = $get(['bol','b/l','bill of lading']);
    $d['pickup_date']      = $get(['pick up date','pick up','pickup date','pickup']);
    $d['delivery_date']    = $get(['delivery date','delivery','del date']);
    $d['return_date']      = $get(['return date','return','ret date']);
    $d['invoice_number']   = $get(['invoice number','invoice #','inv #','invoice no']);
    $d['invoice_date']     = $get(['invoice date','date']);
    $d['due_date']         = $get(['invoice expiration date','due date','expiration date','exp date']);
    $d['notes']            = $get(['notes','comments','remarks']);

    // Origin
    $portName = $get(['port','origin','origin name','terminal']);
    $portAddr = $get(['port address','origin address','port addr']);
    $d['origin_name'] = $portName;
    if ($portAddr) splitAddress($portAddr, $d, 'origin');

    // Destination
    $destName = $get(['shipper/consignee','shipper consignee','consignee','destination','dest','deliver to']);
    $destAddr = $get(['shipper/consignee address','consignee address','nee address','destination address','dest address']);
    $d['dest_name'] = $destName;
    if ($destAddr) splitAddress($destAddr, $d, 'dest');

    // Customer info block (address + phone)
    $custInfo = $get(['customer information','customer info','bill to information']);
    if ($custInfo) extractCustomerInfo($custInfo, $d);

    // ── Services (QUANTITY + SERVICE_NAME pairs) ──────────────────────────────
    // Maps: header name (lowercase) → display label
    $svcMap = [
        'freight + fuel + 2 day chassis' => 'Freight + Fuel + 2 Day Chassis',
        'freight+fuel+2 day chassis'     => 'Freight + Fuel + 2 Day Chassis',
        'freight + fuel + 2day chassis'  => 'Freight + Fuel + 2 Day Chassis',
        'freight + fuel'                 => 'Freight + Fuel',
        'chassis'                        => 'Chassis',
        'chasis'                         => 'Chassis',        // typo variant
        'storage'                        => 'Storage',
        'hazmat'                         => 'Hazmat',
        'local trip'                     => 'Local Trip',
        'local'                          => 'Local Trip',
        'pre pull'                       => 'Pre Pull',
        'prepull'                        => 'Pre Pull',
        'port fees'                      => 'Port Fees',
        'port fee'                       => 'Port Fees',
        'admin fee'                      => 'Admin Fee',
        'admin'                          => 'Admin Fee',
        'lumper'                         => 'Lumper',
        'missed trip'                    => 'Missed Trip',
        'weekend fee'                    => 'Weekend Fee',
        'weekend'                        => 'Weekend Fee',
        'waiting time'                   => 'Waiting Time',
        'waiting'                        => 'Waiting Time',
        'detention'                      => 'Detention',
        'other'                          => 'Other',
        'others'                         => 'Other',
    ];

    $services = [];
    for ($i = 0; $i < count($headers); $i++) {
        $h     = $headers[$i];
        $label = $svcMap[$h] ?? null;
        if (!$label) continue;

        $rawVal = trim($values[$i] ?? '');
        $amt    = (float) preg_replace('/[^0-9.]/', '', str_replace(',', '.', $rawVal));
        if ($amt <= 0) continue;

        // Find QUANTITY: check column before (most common) then after
        $qty = 1;
        if ($i > 0 && strtolower(trim($headers[$i - 1])) === 'quantity') {
            $q = (int) preg_replace('/[^0-9]/', '', $values[$i - 1] ?? '1');
            if ($q > 0) $qty = $q;
        } elseif (isset($headers[$i + 1]) && strtolower(trim($headers[$i + 1])) === 'quantity') {
            $q = (int) preg_replace('/[^0-9]/', '', $values[$i + 1] ?? '1');
            if ($q > 0) $qty = $q;
        }

        $services[] = ['description' => $label, 'qty' => (string)$qty, 'amount' => $amt];
    }

    if (!empty($services)) $d['services'] = $services;

    return $d;
}

// ─── Free-text Parser ─────────────────────────────────────────────────────────

function parseFreeText(string $raw): array
{
    $lines = preg_split('/\r?\n/', $raw);
    $d     = emptyDoc();

    $kv = [];
    foreach ($lines as $line) {
        if (preg_match('/^([^:]{1,50}):\s*(.+)$/', trim($line), $m)) {
            $kv[strtolower(trim($m[1]))] = trim($m[2]);
        }
    }

    $pick = function(array $keys) use ($kv): string {
        foreach ($keys as $k) {
            if (!empty($kv[strtolower($k)])) return $kv[strtolower($k)];
        }
        return '';
    };

    $d['invoice_number']   = $pick(['invoice #','invoice number','invoice no','inv #']);
    $d['invoice_date']     = $pick(['invoice date','date']);
    $d['due_date']         = $pick(['due date','invoice expiration date','expiration date']);
    $d['container_number'] = $pick(['container #','container number','container','cntr #']);
    $d['booking_number']   = $pick(['booking #','booking number','booking','bkg']);
    $d['reference_number'] = $pick(['reference #','reference number','customer reference','ref #']);
    $d['bill_to_name']     = $pick(['bill to','bill to name','customer','client']);
    $d['bill_to_addr1']    = $pick(['bill to address 1','address 1','addr1']);
    $d['bill_to_addr2']    = $pick(['bill to address 2','address 2','addr2']);
    $d['bill_to_phone']    = $pick(['phone','ph','tel']);
    $d['container']        = $pick(['container','cntr']) ?: $d['container_number'];
    $d['size']             = $pick(['size','container size']);
    $d['type']             = $pick(['type','container type']);
    $d['bol']              = $pick(['bol','b/l','bill of lading']);
    $d['pickup_date']      = $pick(['pickup','pickup date','pick up','pick up date']);
    $d['delivery_date']    = $pick(['delivery','delivery date']);
    $d['return_date']      = $pick(['return','return date']);
    $d['origin_name']      = $pick(['origin','port','terminal','origin name']);
    $d['origin_addr1']     = $pick(['origin address 1','origin address','port address']);
    $d['origin_addr2']     = $pick(['origin address 2','origin city']);
    $d['dest_name']        = $pick(['destination','dest','shipper/consignee','consignee','destination name']);
    $d['dest_addr1']       = $pick(['destination address 1','destination address','dest address','consignee address']);
    $d['dest_addr2']       = $pick(['destination address 2','dest city']);
    $d['notes']            = $pick(['notes','comments','remarks']);

    // Container ISO auto-detect
    if (!$d['container_number'] && preg_match('/\b([A-Z]{4}\d{7})\b/', $raw, $m)) {
        $d['container_number'] = $d['container'] = $m[1];
    }

    // Phone
    if (!$d['bill_to_phone'] && preg_match('/(?:\+1\s*)?\(?\d{3}\)?[\s.\-]\d{3}[\s.\-]\d{4}/', $raw, $m))
        $d['bill_to_phone'] = trim($m[0]);

    // Dates near keywords
    $dateNear = function(string $kw) use ($raw): string {
        if (preg_match('/' . preg_quote($kw, '/') . '\W{0,20}(\d{1,2}[\/\-]\d{1,2}[\/\-]\d{2,4})/i', $raw, $m))
            return $m[1];
        return '';
    };
    if (!$d['pickup_date'])   $d['pickup_date']   = $dateNear('pickup');
    if (!$d['delivery_date']) $d['delivery_date'] = $dateNear('delivery');
    if (!$d['return_date'])   $d['return_date']   = $dateNear('return');

    // Services (lines ending in amount)
    $services = [];
    foreach ($lines as $line) {
        $line = trim($line);
        if (preg_match('/^(.+?)\s+(\d+)\s+\$?([\d,]+\.?\d{0,2})\s*$/', $line, $m)) {
            $amt = (float)str_replace(',', '', $m[3]);
            if ($amt > 0) { $services[] = ['description'=>trim($m[1]),'qty'=>$m[2],'amount'=>$amt]; continue; }
        }
        if (preg_match('/^(.+?)\s+\$?([\d,]+\.\d{2})\s*$/', $line, $m)) {
            $amt = (float)str_replace(',', '', $m[2]);
            if ($amt > 0 && !preg_match('/^(total|subtotal|due|amount|balance|invoice)/i', $m[1]))
                $services[] = ['description'=>trim($m[1]),'qty'=>'1','amount'=>$amt];
        }
    }
    if (!empty($services)) $d['services'] = $services;

    return $d;
}

// ─── Helpers ──────────────────────────────────────────────────────────────────

function emptyDoc(): array {
    return [
        'invoice_number'=>'','invoice_date'=>'','due_date'=>'',
        'container_number'=>'','booking_number'=>'','reference_number'=>'',
        'bill_to_name'=>'','bill_to_addr1'=>'','bill_to_addr2'=>'','bill_to_phone'=>'',
        'container'=>'','size'=>'','type'=>'',
        'bol'=>'','pickup_date'=>'','delivery_date'=>'','return_date'=>'',
        'origin_name'=>'','origin_addr1'=>'','origin_addr2'=>'',
        'dest_name'=>'','dest_addr1'=>'','dest_addr2'=>'',
        'notes'=>'','services'=>[],
    ];
}

// Collapse all whitespace/newlines to a single space — safe for PDF Cell()
function flattenText(string $s): string
{
    return trim(preg_replace('/[\r\n\t]+/', ' ', $s));
}

function splitAddress(string $addr, array &$d, string $prefix): void
{
    $addr = flattenText($addr);
    if (!$addr) return;
    if (preg_match('/^(.*?),\s*([A-Za-z][A-Za-z\s]+,\s*[A-Z]{2}[\s,]*\d{5}.*)$/s', $addr, $m)) {
        $d[$prefix.'_addr1'] = trim($m[1]);
        $d[$prefix.'_addr2'] = trim($m[2]);
    } else {
        $parts = array_map('trim', explode(',', $addr));
        if (count($parts) >= 2) {
            $d[$prefix.'_addr2'] = array_pop($parts);
            $d[$prefix.'_addr1'] = implode(', ', $parts);
        } else {
            $d[$prefix.'_addr1'] = $addr;
        }
    }
}

function extractCustomerInfo(string $info, array &$d): void
{
    $info = flattenText($info);
    if (!$d['bill_to_phone'] && preg_match('/(?:phone|ph|tel)[\s:]*(\+?[\d\s().+\-]{7,20})/i', $info, $m))
        $d['bill_to_phone'] = trim($m[1]);
    $clean = preg_replace('/,?\s*(?:phone|ph|tel|federal tax id|tax id|fax)[^,]*/i', '', $info);
    $clean = trim($clean, " ,");
    if (!$d['bill_to_addr1']) splitAddress($clean, $d, 'bill_to');
}

function knownHeaders(): array {
    return [
        'customer','customer information','pick up date','delivery date','return date',
        'port','port address','shipper/consignee','shipper/consignee address',
        'container','booking','customer reference','container size','container type','bol',
        'quantity','freight + fuel + 2 day chassis',
        'quantity','chasis',
        'quantity','storage',
        'quantity','hazmat',
        'quantity','local trip',
        'quantity','pre pull',
        'quantity','port fees',
        'quantity','admin fee',
        'quantity','lumper',
        'quantity','missed trip',
        'quantity','weekend fee',
        'quantity','waiting time',
        'quantity','other',
        'discount','discount reason',
        'invoice amount','invoice number','invoice date','invoice expiration date','notes',
    ];
}
