import type { ApiContact } from './types'
import type { FullContact, ContactAddress } from '@/lib/contacts/types'
import type { ContactBulkEditField } from '@/lib/contacts/bulk-edit-fields'
import {
  avatarUrlToVCardPhotoLine,
  parseVCardPhoto,
} from '@/lib/contact-avatar'

interface VCardFields {
  fn?: string
  emails: { value: string; type: string }[]
  phones: { value: string; type: string }[]
  org?: string
  title?: string
  bday?: string
  note?: string
  nickname?: string
  url?: string
  socialProfiles: { value: string; type: string }[]
  ultiLabels?: string[]
  photo?: string
  addresses: { street?: string; city?: string; region?: string; postalCode?: string; country?: string; type: string }[]
}

const VCARD_TYPE_MAP: Record<string, string> = {
  travail: 'WORK',
  work: 'WORK',
  domicile: 'HOME',
  home: 'HOME',
  mobile: 'CELL',
  cell: 'CELL',
  autre: 'OTHER',
  other: 'OTHER',
  internet: 'INTERNET',
  personal: 'HOME',
}

function escapeVCardValue(value: string): string {
  return value
    .replace(/\\/g, '\\\\')
    .replace(/;/g, '\\;')
    .replace(/,/g, '\\,')
    .replace(/\r?\n/g, '\\n')
}

function vcardTypeParam(label: string): string {
  const key = label.trim().toLowerCase()
  return VCARD_TYPE_MAP[key] ?? 'OTHER'
}

function foldVCardLine(line: string): string {
  const max = 75
  if (line.length <= max) return line
  const parts: string[] = [line.slice(0, max)]
  let rest = line.slice(max)
  while (rest.length > 0) {
    parts.push(` ${rest.slice(0, max - 1)}`)
    rest = rest.slice(max - 1)
  }
  return parts.join('\r\n')
}

function vcardSocialTypeParam(label: string): string {
  const key = label.trim().toLowerCase()
  if (key === 'x') return 'twitter'
  return key || 'other'
}

function socialProfileLabelFromType(type: string): string {
  const key = type.trim().toLowerCase()
  if (key === 'x') return 'twitter'
  return key || 'other'
}

export function buildVCardFromFullContact(contact: FullContact): string {
  const lines: string[] = ['BEGIN:VCARD', 'VERSION:3.0']
  const uid = contact.id?.trim()
  if (uid) lines.push(`UID:${escapeVCardValue(uid)}`)

  const fullName = `${contact.firstName} ${contact.lastName}`.trim()
  lines.push(
    `N:${escapeVCardValue(contact.lastName)};${escapeVCardValue(contact.firstName)};;;`,
  )
  lines.push(`FN:${escapeVCardValue(fullName || contact.emails[0]?.value || 'Contact')}`)

  for (const email of contact.emails) {
    const value = email.value?.trim()
    if (!value) continue
    const type = vcardTypeParam(email.label)
    lines.push(`EMAIL;TYPE=${type}:${escapeVCardValue(value)}`)
  }

  for (const phone of contact.phones) {
    const value = phone.value?.trim()
    if (!value) continue
    const type = vcardTypeParam(phone.label)
    lines.push(`TEL;TYPE=${type}:${escapeVCardValue(value)}`)
  }

  if (contact.company?.trim() || contact.department?.trim()) {
    const orgParts = [contact.company?.trim() ?? '', contact.department?.trim() ?? '']
    lines.push(`ORG:${orgParts.map(escapeVCardValue).join(';')}`)
  }

  if (contact.jobTitle?.trim()) {
    lines.push(`TITLE:${escapeVCardValue(contact.jobTitle.trim())}`)
  }

  if (contact.website?.trim()) {
    lines.push(`URL:${escapeVCardValue(contact.website.trim())}`)
  }

  for (const profile of contact.socialProfiles ?? []) {
    const value = profile.value?.trim()
    if (!value) continue
    const type = vcardSocialTypeParam(profile.label)
    lines.push(`X-SOCIALPROFILE;TYPE=${type}:${escapeVCardValue(value)}`)
  }

  for (const addr of contact.addresses ?? []) {
    const hasValue =
      addr.street?.trim() ||
      addr.city?.trim() ||
      addr.region?.trim() ||
      addr.postalCode?.trim() ||
      addr.country?.trim()
    if (!hasValue) continue
    const type = vcardTypeParam(addr.label)
    const adr = [
      '',
      '',
      addr.street?.trim() ?? '',
      addr.city?.trim() ?? '',
      addr.region?.trim() ?? '',
      addr.postalCode?.trim() ?? '',
      addr.country?.trim() ?? '',
    ]
      .map(escapeVCardValue)
      .join(';')
    lines.push(`ADR;TYPE=${type}:${adr}`)
  }

  if (contact.notes?.trim()) {
    lines.push(`NOTE:${escapeVCardValue(contact.notes.trim())}`)
  }

  if (contact.labels?.length) {
    lines.push(
      `X-ULTI-LABELS:${contact.labels.map(escapeVCardValue).join(',')}`,
    )
  }

  const photoLine = avatarUrlToVCardPhotoLine(contact.avatarUrl, escapeVCardValue)
  if (photoLine) lines.push(photoLine)

  lines.push('END:VCARD')
  return lines.map(foldVCardLine).join('\r\n')
}

function parseVCard(raw: string): VCardFields {
  const fields: VCardFields = { emails: [], phones: [], addresses: [], socialProfiles: [] }

  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 'URL':
        fields.url = value
        break
      case 'X-SOCIALPROFILE':
      case 'SOCIALPROFILE':
        fields.socialProfiles.push({ value, type: socialProfileLabelFromType(type) })
        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
      }
      case 'X-ULTI-LABELS':
        fields.ultiLabels = value
          .split(',')
          .map((s) => s.trim())
          .filter(Boolean)
        break
      case 'PHOTO':
        fields.photo = parseVCardPhoto(rawKey, value) ?? fields.photo
        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,
    path: api.path,
    etag: api.etag,
    firstName,
    lastName,
    emails,
    phones,
    addresses,
    company: vcard?.org ?? api.org,
    jobTitle: vcard?.title,
    website: vcard?.url,
    socialProfiles: vcard?.socialProfiles.length
      ? vcard.socialProfiles.map((p) => ({ value: p.value, label: p.type }))
      : undefined,
    birthday,
    notes: vcard?.note,
    nicknames: vcard?.nickname ? [vcard.nickname] : undefined,
    labels: vcard?.ultiLabels?.length ? vcard.ultiLabels : undefined,
    avatarUrl: vcard?.photo,
    createdAt: Date.now(),
    updatedAt: Date.now(),
  }
}

export function fullContactToApiContact(contact: FullContact): Partial<ApiContact> {
  const raw_vcard = buildVCardFromFullContact(contact)
  const fullName = `${contact.firstName} ${contact.lastName}`.trim()
  return {
    uid: contact.id,
    full_name: fullName || contact.emails[0]?.value || 'Contact',
    email: contact.emails[0]?.value,
    phone: contact.phones[0]?.value,
    org: contact.company,
    raw_vcard,
  }
}

function unfoldVCardLines(raw: string): string[] {
  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)
    }
  }
  return lines
}

function vcardPropName(line: string): string {
  return line.split(':')[0]?.split(';')[0]?.toUpperCase() ?? ''
}

function upsertVCardLine(
  lines: string[],
  propName: string,
  lineContent: string | null,
): string[] {
  const upper = propName.toUpperCase()
  const kept = lines.filter((line) => vcardPropName(line) !== upper)
  if (!lineContent) return kept
  const endIdx = kept.findIndex((l) => l.toUpperCase() === 'END:VCARD')
  const insertAt = endIdx >= 0 ? endIdx : kept.length
  kept.splice(insertAt, 0, lineContent)
  return kept
}

function readOrgParts(vcard: string): { company: string; department: string } {
  for (const line of unfoldVCardLines(vcard)) {
    if (!line.toUpperCase().startsWith('ORG:')) continue
    const value = line.slice(line.indexOf(':') + 1)
    const parts = value.split(';')
    return {
      company: (parts[0] ?? '').replace(/\\;/g, ';').replace(/\\\\/g, '\\'),
      department: (parts[1] ?? '').replace(/\\;/g, ';').replace(/\\\\/g, '\\'),
    }
  }
  return { company: '', department: '' }
}

/** Patch an existing vCard in-place to avoid losing unparsed fields on bulk edit. */
export function patchVCardBulkField(
  rawVCard: string,
  contact: FullContact,
  field: ContactBulkEditField,
  value: string,
): string {
  const trimmed = value.trim()
  let lines = unfoldVCardLines(rawVCard)

  switch (field) {
    case 'company': {
      const org = readOrgParts(rawVCard)
      const company = trimmed
      const department = contact.department?.trim() ?? org.department
      const orgLine =
        company || department
          ? `ORG:${escapeVCardValue(company)};${escapeVCardValue(department)}`
          : null
      lines = upsertVCardLine(lines, 'ORG', orgLine)
      break
    }
    case 'department': {
      const org = readOrgParts(rawVCard)
      const company = contact.company?.trim() ?? org.company
      const department = trimmed
      const orgLine =
        company || department
          ? `ORG:${escapeVCardValue(company)};${escapeVCardValue(department)}`
          : null
      lines = upsertVCardLine(lines, 'ORG', orgLine)
      break
    }
    case 'jobTitle':
      lines = upsertVCardLine(
        lines,
        'TITLE',
        trimmed ? `TITLE:${escapeVCardValue(trimmed)}` : null,
      )
      break
    case 'website':
      lines = upsertVCardLine(
        lines,
        'URL',
        trimmed ? `URL:${escapeVCardValue(trimmed)}` : null,
      )
      break
    case 'notes':
      lines = upsertVCardLine(
        lines,
        'NOTE',
        trimmed ? `NOTE:${escapeVCardValue(trimmed)}` : null,
      )
      break
  }

  return lines.map(foldVCardLine).join('\r\n')
}

export function patchVCardPhoto(rawVCard: string, avatarUrl: string | undefined): string {
  const lines = unfoldVCardLines(rawVCard)
  const lineContent = avatarUrlToVCardPhotoLine(avatarUrl, escapeVCardValue)
  const next = upsertVCardLine(lines, 'PHOTO', lineContent)
  return next.map(foldVCardLine).join('\r\n')
}

export function patchVCardLabels(rawVCard: string, labelIds: string[] | undefined): string {
  const lines = unfoldVCardLines(rawVCard)
  const lineContent =
    labelIds?.length
      ? `X-ULTI-LABELS:${labelIds.map(escapeVCardValue).join(',')}`
      : null
  const next = upsertVCardLine(lines, 'X-ULTI-LABELS', lineContent)
  return next.map(foldVCardLine).join('\r\n')
}

export function buildContactUpdatePayload(
  existing: ApiContact | undefined,
  contact: FullContact,
  opts?: {
    bulkField?: ContactBulkEditField
    bulkValue?: string
    patchLabels?: boolean
  },
): Partial<ApiContact> {
  const base = fullContactToApiContact(contact)
  const raw = existing?.raw_vcard?.trim()

  if (raw && opts?.bulkField !== undefined) {
    const patched = patchVCardBulkField(raw, contact, opts.bulkField, opts.bulkValue ?? '')
    return { ...base, raw_vcard: patched }
  }

  if (raw && opts?.patchLabels) {
    return { ...base, raw_vcard: patchVCardLabels(raw, contact.labels) }
  }

  if (raw) {
    const patched = patchVCardPhoto(raw, contact.avatarUrl)
    return { ...base, raw_vcard: patched }
  }

  return base
}
