import type { ApiContact } from './types' import type { FullContact, ContactAddress } from '@/lib/contacts/types' interface VCardFields { fn?: string emails: { value: string; type: string }[] phones: { value: string; type: string }[] org?: string title?: string bday?: string note?: string nickname?: string addresses: { street?: string; city?: string; region?: string; postalCode?: string; country?: string; type: string }[] } function parseVCard(raw: string): VCardFields { const fields: VCardFields = { emails: [], phones: [], addresses: [] } const lines: string[] = [] for (const line of raw.split(/\r?\n/)) { if (/^\s/.test(line) && lines.length > 0) { lines[lines.length - 1] += line.trimStart() } else { lines.push(line) } } for (const line of lines) { const colonIdx = line.indexOf(':') if (colonIdx === -1) continue const rawKey = line.slice(0, colonIdx) const value = line.slice(colonIdx + 1).trim() if (!value) continue const keyParts = rawKey.split(';') const propName = keyParts[0].toUpperCase() const params = keyParts.slice(1).join(';').toUpperCase() const typeMatch = params.match(/TYPE=([^;,]+)/i) const type = typeMatch?.[1]?.toLowerCase() ?? 'other' switch (propName) { case 'FN': fields.fn = value break case 'EMAIL': fields.emails.push({ value, type }) break case 'TEL': fields.phones.push({ value, type }) break case 'ORG': fields.org = value.split(';')[0] break case 'TITLE': fields.title = value break case 'BDAY': { fields.bday = value break } case 'NOTE': fields.note = value break case 'NICKNAME': fields.nickname = value break case 'ADR': { const parts = value.split(';') fields.addresses.push({ street: parts[2] || undefined, city: parts[3] || undefined, region: parts[4] || undefined, postalCode: parts[5] || undefined, country: parts[6] || undefined, type, }) break } } } return fields } function parseBday(raw: string): { day?: number; month?: number; year?: number } | undefined { const m = raw.match(/^(\d{4})-?(\d{2})-?(\d{2})$/) if (m) { return { year: Number(m[1]), month: Number(m[2]), day: Number(m[3]) } } const partial = raw.match(/^--(\d{2})-?(\d{2})$/) if (partial) { return { month: Number(partial[1]), day: Number(partial[2]) } } return undefined } function splitName(fullName: string): { firstName: string; lastName: string } { const parts = fullName.trim().split(/\s+/) if (parts.length <= 1) return { firstName: parts[0] ?? '', lastName: '' } return { firstName: parts[0], lastName: parts.slice(1).join(' ') } } export function apiContactToFullContact(api: ApiContact): FullContact { const vcard = api.raw_vcard ? parseVCard(api.raw_vcard) : null const { firstName, lastName } = splitName(vcard?.fn ?? api.full_name ?? '') const emails: { value: string; label: string }[] = vcard?.emails.length ? vcard.emails.map((e) => ({ value: e.value, label: e.type })) : api.email ? [{ value: api.email, label: 'personal' }] : [] const phones: { value: string; label: string }[] = vcard?.phones.length ? vcard.phones.map((p) => ({ value: p.value, label: p.type })) : api.phone ? [{ value: api.phone, label: 'mobile' }] : [] const addresses: ContactAddress[] | undefined = vcard?.addresses.length ? vcard.addresses.map((a) => ({ street: a.street, city: a.city, region: a.region, postalCode: a.postalCode, country: a.country, label: a.type, })) : undefined const birthday = vcard?.bday ? parseBday(vcard.bday) : undefined return { id: api.uid, firstName, lastName, emails, phones, addresses, company: vcard?.org ?? api.org, jobTitle: vcard?.title, birthday, notes: vcard?.note, nicknames: vcard?.nickname ? [vcard.nickname] : undefined, createdAt: Date.now(), updatedAt: Date.now(), } } export function fullContactToApiContact(contact: FullContact): Partial { return { uid: contact.id, full_name: `${contact.firstName} ${contact.lastName}`.trim(), email: contact.emails[0]?.value, phone: contact.phones[0]?.value, org: contact.company, } }