import { prisma } from './prisma'; import { logger } from '../logger'; export interface ContactImportRow { [key: string]: any; } export interface BulkImportResult { created: number; updated: number; errors: number; } export class ContactService { /** * Identifies the phone number column using fuzzy matching */ static findPhoneKey(row: ContactImportRow): string | undefined { return Object.keys(row).find(k => k.toLowerCase().includes('phone') || k.toLowerCase().includes('téléphone') || k.toLowerCase().includes('tel') || k.toLowerCase().includes('num') || k.toLowerCase().includes('whatsapp') ); } /** * Identifies the name column using fuzzy matching */ static findNameKey(row: ContactImportRow): string | undefined { return Object.keys(row).find(k => k.toLowerCase().includes('name') || k.toLowerCase().includes('nom') || k.toLowerCase().includes('contact') || k.toLowerCase().includes('client') ); } /** * Normalizes a phone number (removes spaces, adds country code if needed) */ static normalizePhone(rawPhone: any): string | null { if (!rawPhone) return null; let phoneNumber = String(rawPhone) .replace(/\s+/g, '') .replace(/^\+/, '') .replace(/\D/g, ''); if (phoneNumber.length < 7) return null; // Senegal logic (9 digits -> add 221) if (phoneNumber.length === 9) { phoneNumber = `221${phoneNumber}`; } return phoneNumber; } /** * Performs a bulk upsert of contacts and links them to a broadcast list */ static async bulkImport(organizationId: string, contacts: ContactImportRow[], listId: string): Promise { const results = { created: 0, updated: 0, errors: 0 }; for (const row of contacts) { try { const phoneKey = this.findPhoneKey(row); const nameKey = this.findNameKey(row); const phoneNumber = this.normalizePhone(phoneKey ? row[phoneKey] : null); const name = nameKey ? String(row[nameKey]).trim() : null; if (!phoneNumber) { results.errors++; continue; } const attributes: any = { ...row }; if (phoneKey) delete attributes[phoneKey]; if (nameKey) delete attributes[nameKey]; await prisma.contact.upsert({ where: { phoneNumber_organizationId: { phoneNumber, organizationId } }, update: { name, attributes, broadcastLists: { connect: { id: listId } } }, create: { phoneNumber, name, attributes, organizationId, broadcastLists: { connect: { id: listId } } } }); results.created++; } catch (err) { logger.error({ err, row }, '[ContactService] Failed to import row'); results.errors++; } } return results; } }