import { prisma } from "@/lib/db"; export interface VaultLead { domain?: string | null; companyName?: string | null; contactName?: string | null; contactTitle?: string | null; email?: string | null; linkedinUrl?: string | null; phone?: string | null; address?: string | null; emailConfidence?: number | null; serpTitle?: string | null; serpSnippet?: string | null; serpRank?: number | null; serpUrl?: string | null; country?: string | null; headcount?: string | null; industry?: string | null; } /** * Deduplication logic: * - Same domain + same email → skip (already exists) * - Same domain + no email yet + new version has email → update * - Otherwise → create new lead */ export async function sinkLeadToVault( lead: VaultLead, sourceTab: string, sourceTerm?: string, sourceJobId?: string, ): Promise { const domain = lead.domain || null; const email = lead.email || null; if (domain) { // Check for exact duplicate (same domain + same email) if (email) { const exact = await prisma.lead.findFirst({ where: { domain, email }, }); if (exact) return exact.id; // already exists, skip } // Check if same domain exists without email and we now have one const existing = await prisma.lead.findFirst({ where: { domain, email: null }, }); if (existing && email) { // Upgrade: fill in email + other missing fields await prisma.lead.update({ where: { id: existing.id }, data: { email, emailConfidence: lead.emailConfidence ?? existing.emailConfidence, contactName: lead.contactName || existing.contactName, contactTitle: lead.contactTitle || existing.contactTitle, linkedinUrl: lead.linkedinUrl || existing.linkedinUrl, phone: lead.phone || existing.phone, updatedAt: new Date(), }, }); return existing.id; } } const created = await prisma.lead.create({ data: { domain, companyName: lead.companyName || null, contactName: lead.contactName || null, contactTitle: lead.contactTitle || null, email, linkedinUrl: lead.linkedinUrl || null, phone: lead.phone || null, address: lead.address || null, emailConfidence: lead.emailConfidence ?? null, serpTitle: lead.serpTitle || null, serpSnippet: lead.serpSnippet || null, serpRank: lead.serpRank ?? null, serpUrl: lead.serpUrl || null, country: lead.country || null, headcount: lead.headcount || null, industry: lead.industry || null, sourceTab, sourceTerm: sourceTerm || null, sourceJobId: sourceJobId || null, status: "new", priority: "normal", }, }); return created.id; } export async function sinkLeadsToVault( leads: VaultLead[], sourceTab: string, sourceTerm?: string, sourceJobId?: string, ): Promise<{ added: number; updated: number; skipped: number }> { let added = 0, updated = 0, skipped = 0; for (const lead of leads) { const domain = lead.domain || null; const email = lead.email || null; if (domain) { if (email) { const exact = await prisma.lead.findFirst({ where: { domain, email } }); if (exact) { skipped++; continue; } } const existing = await prisma.lead.findFirst({ where: { domain, email: null } }); if (existing && email) { await prisma.lead.update({ where: { id: existing.id }, data: { email, emailConfidence: lead.emailConfidence ?? existing.emailConfidence, contactName: lead.contactName || existing.contactName, contactTitle: lead.contactTitle || existing.contactTitle, linkedinUrl: lead.linkedinUrl || existing.linkedinUrl, phone: lead.phone || existing.phone, updatedAt: new Date(), }, }); updated++; continue; } } await prisma.lead.create({ data: { domain, companyName: lead.companyName || null, contactName: lead.contactName || null, contactTitle: lead.contactTitle || null, email, linkedinUrl: lead.linkedinUrl || null, phone: lead.phone || null, address: lead.address || null, emailConfidence: lead.emailConfidence ?? null, serpTitle: lead.serpTitle || null, serpSnippet: lead.serpSnippet || null, serpRank: lead.serpRank ?? null, serpUrl: lead.serpUrl || null, country: lead.country || null, sourceTab, sourceTerm: sourceTerm || null, sourceJobId: sourceJobId || null, status: "new", priority: "normal", }, }); added++; } return { added, updated, skipped }; }