File size: 3,497 Bytes
c5fde49
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
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<BulkImportResult> {
        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;
    }
}