const axios = require('axios'); const fs = require('fs'); const readline = require('readline'); const XLSX = require('xlsx'); const { apiUrlBase } = require('../config/apiConfig'); const { normalizeHeader, isExcelFile, detectDelimiter, hasCepHeader, hasAddressOrNumberHeader, hasGeoHeaders } = require('./fileFormat'); function findHeaderRow(rows) { return rows.find(row => { const hasCepNumero = hasCepHeader(row) && hasAddressOrNumberHeader(row); const hasGeo = hasGeoHeaders(row); return hasCepNumero || hasGeo; }) || []; } function readExcelHeaders(filePath) { const workbook = XLSX.readFile(filePath, { cellDates: false, raw: false }); const firstSheetName = workbook.SheetNames[0]; if (!firstSheetName) return []; const rows = XLSX.utils.sheet_to_json(workbook.Sheets[firstSheetName], { header: 1, blankrows: true, defval: '' }); return findHeaderRow(rows).map(normalizeHeader); } async function readDelimitedHeaders(filePath) { const instream = fs.createReadStream(filePath, { encoding: 'utf8' }); const rl = readline.createInterface({ input: instream, crlfDelay: Infinity }); for await (const rawLine of rl) { const line = rawLine.replace(/^/, '').replace(/\r$/, ''); if (!line.trim()) continue; const headers = line.split(detectDelimiter(line)).map(normalizeHeader); if ((hasCepHeader(headers) && hasAddressOrNumberHeader(headers)) || hasGeoHeaders(headers)) { rl.close(); return headers; } } rl.close(); return []; } // Verifica se os dados vindos são de CEP ou de geolocalização (aceita caminho de arquivo ou objeto). async function discoverDataType(input) { if (typeof input === 'string') { const headers = isExcelFile(input) ? readExcelHeaders(input) : await readDelimitedHeaders(input); if (hasCepHeader(headers) && hasAddressOrNumberHeader(headers)) return 'cep'; if (hasGeoHeaders(headers)) return 'geolocalizacao'; return 'unknown'; } if (input && typeof input === 'object') { if (input.cep && input.numero) return 'cep'; if (input.latitude !== undefined && input.longitude !== undefined) return 'geolocalizacao'; return 'unknown'; } return 'unknown'; } // Consulta individual (manual / uma linha). Escolhe o endpoint conforme o tipo dos dados. async function consultarViabilidade(data) { const dataType = await discoverDataType(data); const endpoint = apiUrlBase + (dataType === 'geolocalizacao' ? 'viabilidade/lat-long' : 'viabilidade'); const response = await axios.post(endpoint, data, { // A API agora faz geocode + GeoGrid + trajeto (várias chamadas externas); // o cep-promise também pode ser lento. 30s evita timeout prematuro. timeout: 30000, headers: { 'Content-Type': 'application/json' } }); return response.data; } // Consulta em LOTE: envia um array de itens ({cep,numero} ou {latitude,longitude}) de uma só vez. // Retorna o array `resultados` da API, alinhado por índice ao array enviado. async function consultarViabilidadeLote(itens, modo) { const endpoint = apiUrlBase + 'viabilidade/lote'; const body = { itens }; if (modo) body.modo = modo; const response = await axios.post(endpoint, body, { timeout: 180000, headers: { 'Content-Type': 'application/json' } }); const data = response.data || {}; return Array.isArray(data.resultados) ? data.resultados : []; } module.exports = { consultarViabilidade, consultarViabilidadeLote, discoverDataType };