import type { FullContact } from "./types"
import { fullContactDisplayName } from "./types"

function contactMergeScore(c: FullContact): number {
  let score = 0
  if (fullContactDisplayName(c)) score += 3
  if (c.emails.some((e) => e.value.trim())) score += 4
  if (c.phones.some((p) => p.value.trim())) score += 2
  if (c.company?.trim()) score += 1
  if (c.path?.trim()) score += 1
  return score
}

export function pickContactMergePrimary(contacts: FullContact[]): FullContact | undefined {
  if (contacts.length === 0) return undefined
  return [...contacts].sort((a, b) => contactMergeScore(b) - contactMergeScore(a))[0]
}

export function mergeManyContacts(
  contacts: FullContact[],
  primaryId?: string,
): { primary: FullContact; secondaries: FullContact[]; merged: FullContact } | null {
  if (contacts.length < 2) return null

  const primary =
    (primaryId ? contacts.find((c) => c.id === primaryId) : undefined) ??
    pickContactMergePrimary(contacts)
  if (!primary) return null

  const secondaries = contacts.filter((c) => c.id !== primary.id)
  let merged = primary
  for (const secondary of secondaries) {
    merged = mergeFullContactFields(merged, secondary)
  }

  return {
    primary,
    secondaries,
    merged: {
      ...merged,
      id: primary.id,
      path: primary.path,
      etag: primary.etag,
      createdAt: primary.createdAt,
    },
  }
}

function dedupeLabeledValues<T extends { value: string; label: string }>(items: T[]): T[] {
  const seen = new Set<string>()
  const out: T[] = []
  for (const item of items) {
    const key = item.value.trim().toLowerCase()
    if (!key || seen.has(key)) continue
    seen.add(key)
    out.push(item)
  }
  return out
}

function dedupeAddresses(items: NonNullable<FullContact["addresses"]>): NonNullable<FullContact["addresses"]> {
  const seen = new Set<string>()
  const out: NonNullable<FullContact["addresses"]> = []
  for (const item of items) {
    const key = [
      item.street,
      item.city,
      item.region,
      item.postalCode,
      item.country,
      item.label,
    ]
      .map((part) => part?.trim().toLowerCase() ?? "")
      .join("|")
    if (!key.replace(/\|/g, "") || seen.has(key)) continue
    seen.add(key)
    out.push(item)
  }
  return out
}

function dedupeStrings(items: string[]): string[] {
  const seen = new Set<string>()
  const out: string[] = []
  for (const item of items) {
    const key = item.trim().toLowerCase()
    if (!key || seen.has(key)) continue
    seen.add(key)
    out.push(item)
  }
  return out
}

function mergeNotes(a?: string, b?: string): string | undefined {
  const parts = [a, b]
    .map((n) => n?.trim())
    .filter(Boolean) as string[]
  if (parts.length === 0) return undefined
  return dedupeStrings(parts).join("\n\n")
}

export function mergeFullContactFields(primary: FullContact, secondary: FullContact): FullContact {
  return {
    ...primary,
    namePrefix: primary.namePrefix || secondary.namePrefix,
    firstName: primary.firstName || secondary.firstName,
    middleName: primary.middleName || secondary.middleName,
    lastName: primary.lastName || secondary.lastName,
    nameSuffix: primary.nameSuffix || secondary.nameSuffix,
    phoneticFirstName: primary.phoneticFirstName || secondary.phoneticFirstName,
    phoneticLastName: primary.phoneticLastName || secondary.phoneticLastName,
    company: primary.company || secondary.company,
    department: primary.department || secondary.department,
    jobTitle: primary.jobTitle || secondary.jobTitle,
    website: primary.website || secondary.website,
    notes: mergeNotes(primary.notes, secondary.notes),
    emails: dedupeLabeledValues([...primary.emails, ...secondary.emails]),
    phones: dedupeLabeledValues([...primary.phones, ...secondary.phones]),
    addresses: dedupeAddresses([
      ...(primary.addresses ?? []),
      ...(secondary.addresses ?? []),
    ]),
    nicknames: dedupeStrings([...(primary.nicknames ?? []), ...(secondary.nicknames ?? [])]),
    labels: dedupeStrings([...(primary.labels ?? []), ...(secondary.labels ?? [])]),
    avatarUrl: primary.avatarUrl || secondary.avatarUrl,
    birthday: primary.birthday ?? secondary.birthday,
    updatedAt: Date.now(),
  }
}

export function mergeTwoContacts(
  a: FullContact,
  b: FullContact,
): { primary: FullContact; secondary: FullContact; merged: FullContact } {
  const [primary, secondary] =
    contactMergeScore(a) >= contactMergeScore(b) ? [a, b] : [b, a]
  return { primary, secondary, merged: mergeFullContactFields(primary, secondary) }
}
