import { apiContactToFullContact } from '@/lib/api/adapters' import type { ApiContact } from '@/lib/api/types' import type { FullContact } from '@/lib/contacts/types' type BookListCache = { apiContacts: ApiContact[] fullContacts: FullContact[] } const parseCache = new Map() const parseSigCache = new Map() const listCacheByBook = new Map() function contactCacheKey(api: ApiContact): string { return api.uid || api.path || `${api.full_name}:${api.email ?? ''}` } function contactSignature(api: ApiContact): string { return [ api.uid, api.path, api.etag, api.full_name, api.email, api.raw_vcard?.length ?? 0, ].join('|') } /** Parse vCard une seule fois par contact tant que la signature API est stable. */ export function apiContactToFullContactCached(api: ApiContact): FullContact { const key = contactCacheKey(api) const sig = contactSignature(api) if (parseSigCache.get(key) === sig) { const hit = parseCache.get(key) if (hit) return hit } const full = apiContactToFullContact(api) parseCache.set(key, full) parseSigCache.set(key, sig) return full } function rebuildFullList(bookId: string, apiContacts: ApiContact[]): FullContact[] { const fullContacts = apiContacts.map(apiContactToFullContactCached) listCacheByBook.set(bookId, { apiContacts, fullContacts }) return fullContacts } /** * Reconstruit la liste complète seulement si nécessaire. * Sur ajout en fin de liste (cas courant), réutilise les contacts déjà parsés. */ export function mapApiContactsToFullContacts( bookId: string, apiContacts: ApiContact[] | undefined, ): FullContact[] { if (!apiContacts) return [] const prev = listCacheByBook.get(bookId) if (prev && prev.apiContacts === apiContacts) { return prev.fullContacts } if (prev && apiContacts.length === prev.apiContacts.length + 1) { let prefixMatch = true for (let i = 0; i < prev.apiContacts.length; i++) { if (apiContacts[i] !== prev.apiContacts[i]) { prefixMatch = false break } } if (prefixMatch) { const appended = apiContactToFullContactCached(apiContacts[apiContacts.length - 1]!) const fullContacts = [...prev.fullContacts, appended] listCacheByBook.set(bookId, { apiContacts, fullContacts }) return fullContacts } } if ( prev && apiContacts.length === prev.apiContacts.length && apiContacts.every((c, i) => c === prev.apiContacts[i]) ) { return prev.fullContacts } return rebuildFullList(bookId, apiContacts) } export function invalidateContactListCache(bookId?: string) { if (bookId) { listCacheByBook.delete(bookId) return } listCacheByBook.clear() parseCache.clear() parseSigCache.clear() }