import { GoogleGenAI } from '@google/genai'; import { calculateRetailPrice } from './pricing-engine'; const apiKey = process.env.GEMINI_API_KEY || ''; /** * Parser inteligente de mensajes de grupos mayoristas de WhatsApp */ export async function parseSupplierMessage(text: string, imageUrl?: string): Promise<{ name: string; brand: string; sizes: string[]; supplierPrice: number; suggestedRetailPrice: number; category: string; confidence: number; }> { // Regex fallback si no hay API key configurada o falla la red const fallbackExtract = () => { // Buscar precio (ej: 90000, 90.000, 90mil, 95k) let price = 90000; const priceMatch = text.match(/(?:\$|precio|mayorista|mayor|c\/u|\b)(\d{2,3})[.,]?(\d{3})?|\b(\d{2,3})\s*(?:mil|k)\b/i); if (priceMatch) { if (priceMatch[3]) { price = parseInt(priceMatch[3], 10) * 1000; } else if (priceMatch[1] && priceMatch[2]) { price = parseInt(priceMatch[1] + priceMatch[2], 10); } else if (priceMatch[1] && parseInt(priceMatch[1], 10) >= 40 && parseInt(priceMatch[1], 10) <= 250) { price = parseInt(priceMatch[1], 10) * 1000; } } // Detectar tallas (ej: 37 a 43, 38-42, 36,37,38) const sizes: string[] = []; const sizeRangeMatch = text.match(/(\d{2})\s*(?:a|al|-)\s*(\d{2})/i); if (sizeRangeMatch) { const start = parseInt(sizeRangeMatch[1], 10); const end = parseInt(sizeRangeMatch[2], 10); if (start >= 34 && end <= 46 && start <= end) { for (let s = start; s <= end; s++) sizes.push(s.toString()); } } else { const individualSizes = text.match(/\b(3[4-9]|4[0-6])\b/g); if (individualSizes) { sizes.push(...Array.from(new Set(individualSizes))); } } if (sizes.length === 0) { sizes.push('37', '38', '39', '40', '41', '42', '43'); } // Detectar Marca let brand = 'Nike'; if (/jordan/i.test(text)) brand = 'Jordan'; else if (/adidas|bad bunny|campus|forum/i.test(text)) brand = 'Adidas'; else if (/new balance|nb/i.test(text)) brand = 'New Balance'; else if (/puma/i.test(text)) brand = 'Puma'; else if (/asics/i.test(text)) brand = 'Asics'; else if (/vans/i.test(text)) brand = 'Vans'; const cleanLines = text.split('\n').filter((l) => l.trim().length > 0 && !/🔥|🔴|llegando|promo/i.test(l)); const name = cleanLines[0] || `${brand} Sneakers Urban Edition`; return { name: name.replace(/tallas?:.*|precio:.*|\$/gi, '').trim() || `${brand} Zapatillas`, brand, sizes, supplierPrice: price, suggestedRetailPrice: calculateRetailPrice(price, 35, 30000), category: 'Calzado / Sneakers', confidence: 0.85, }; }; if (!apiKey) { return fallbackExtract(); } try { const ai = new GoogleGenAI({ apiKey }); const prompt = `Eres un asistente experto en e-commerce y calzado deportivo en Colombia. Analiza el siguiente mensaje extraído de un grupo mayorista de WhatsApp ("DIFUSION JOVIC STORE" o "EXCLUSIVE CC MIO 3B"). Extrae los siguientes campos en formato JSON estricto: - "name": Nombre comercial limpio del modelo (ej: "Nike Dunk Low Retro Panda", "Air Jordan 4 Military Black"). - "brand": Marca ("Nike", "Jordan", "Adidas", "New Balance", "Puma", "Asics", etc.). - "sizes": Array de tallas en horma colombiana/europea (ej: ["38", "39", "40", "41", "42", "43"]). Si dice "38 al 43", expande todas las tallas intermedias. - "supplierPrice": Precio de compra mayorista como número entero en COP (ej: 90000, 95000). Si dice "90mil" o "90", es 90000. - "category": Categoría ("Sneakers / Colección", "Urbano / Casual", "Running / Deportivo", etc.). Mensaje del grupo: """ ${text} """ Responde ÚNICAMENTE con el objeto JSON sin bloques de código markdown ni texto adicional.`; const response = await ai.models.generateContent({ model: 'gemini-2.5-flash', contents: prompt, }); const outputText = response.text || ''; const cleanJson = outputText.replace(/```json|```/gi, '').trim(); const parsed = JSON.parse(cleanJson); const supplierPrice = typeof parsed.supplierPrice === 'number' ? parsed.supplierPrice : 90000; const sizes = Array.isArray(parsed.sizes) && parsed.sizes.length > 0 ? parsed.sizes.map(String) : ['37', '38', '39', '40', '41', '42', '43']; return { name: parsed.name || 'Zapatilla Urbana', brand: parsed.brand || 'Nike', sizes, supplierPrice, suggestedRetailPrice: calculateRetailPrice(supplierPrice, 35, 30000), category: parsed.category || 'Sneakers', confidence: 0.98, }; } catch (err) { console.warn('Fallo llamada Gemini, utilizando extractor regex fallback:', err); return fallbackExtract(); } }