const fs = require('fs'); const path = require('path'); // Helpers de detecção de formato/cabeçalho compartilhados entre viabilidadeService e csvService. // Antes estavam duplicados nos dois arquivos. Todos normalizam internamente (normalizeHeader // é idempotente), então funcionam tanto com cabeçalhos crus quanto já normalizados. function normalizeHeader(value) { return String(value || '') .trim() .toLowerCase() .normalize('NFD') .replace(/[̀-ͯ]/g, '') .replace(/[_-]+/g, ' ') .replace(/\s+/g, ' '); } function isExcelFile(filePath) { const ext = path.extname(filePath).toLowerCase(); if (['.xls', '.xlsx'].includes(ext)) return true; const fileStart = fs.readFileSync(filePath).subarray(0, 512); const signature = fileStart.subarray(0, 8); const isZipBasedXlsx = signature[0] === 0x50 && signature[1] === 0x4b; const isOleBasedXls = signature[0] === 0xd0 && signature[1] === 0xcf && signature[2] === 0x11 && signature[3] === 0xe0 && signature[4] === 0xa1 && signature[5] === 0xb1 && signature[6] === 0x1a && signature[7] === 0xe1; const startText = fileStart.toString('latin1').trimStart().toLowerCase(); const isHtmlExcel = startText.startsWith(' ({ delimiter, count: line.split(delimiter).length })) .sort((a, b) => b.count - a.count)[0].delimiter; } function hasCepHeader(headers) { return headers.map(normalizeHeader).some(header => /\bcep\b/.test(header) || header === 'codigo postal'); } function hasAddressOrNumberHeader(headers) { return headers.map(normalizeHeader).some(header => ['numero', 'num', 'nº', 'n°'].includes(header) || header.includes('endereco') || header.includes('logradouro')); } function hasGeoHeaders(headers) { const norm = headers.map(normalizeHeader); return norm.some(h => /\blat(itude)?\b/.test(h)) && norm.some(h => /\blo?n[g]?(itude)?\b/.test(h)); } module.exports = { normalizeHeader, isExcelFile, detectDelimiter, hasCepHeader, hasAddressOrNumberHeader, hasGeoHeaders };