import { parseDisplayNameToNameParts } from "./find-contact"
import type { FullContact } from "./types"

export type ContactImportInput = Omit<FullContact, "id" | "createdAt" | "updatedAt">

function stripQuotes(value: string): string {
  const t = value.trim()
  if ((t.startsWith('"') && t.endsWith('"')) || (t.startsWith("'") && t.endsWith("'"))) {
    return t.slice(1, -1).trim()
  }
  return t
}

function parseCsvLine(line: string): string[] {
  const fields: string[] = []
  let current = ""
  let inQuotes = false

  for (let i = 0; i < line.length; i++) {
    const ch = line[i]
    if (ch === '"') {
      if (inQuotes && line[i + 1] === '"') {
        current += '"'
        i++
      } else {
        inQuotes = !inQuotes
      }
    } else if (ch === "," && !inQuotes) {
      fields.push(stripQuotes(current))
      current = ""
    } else {
      current += ch
    }
  }
  fields.push(stripQuotes(current))
  return fields
}

function unfoldVcardLines(text: string): string[] {
  const raw = text.split(/\r?\n/)
  const lines: string[] = []
  for (const line of raw) {
    if ((line.startsWith(" ") || line.startsWith("\t")) && lines.length > 0) {
      lines[lines.length - 1] += line.slice(1)
    } else {
      lines.push(line)
    }
  }
  return lines
}

function parseVcardProperty(line: string): { key: string; value: string } | null {
  const idx = line.indexOf(":")
  if (idx === -1) return null
  const left = line.slice(0, idx)
  const value = line.slice(idx + 1).trim()
  const key = left.split(";")[0].toUpperCase()
  return { key, value }
}

function parseVcardBlock(lines: string[]): ContactImportInput | null {
  let firstName = ""
  let lastName = ""
  let company: string | undefined
  let jobTitle: string | undefined
  const emails: { value: string; label: string }[] = []
  const phones: { value: string; label: string }[] = []
  let notes: string | undefined

  for (const line of lines) {
    const prop = parseVcardProperty(line)
    if (!prop || !prop.value) continue
    const { key, value } = prop

    switch (key) {
      case "FN": {
        const parts = parseDisplayNameToNameParts(value)
        if (!firstName && !lastName) {
          firstName = parts.firstName
          lastName = parts.lastName
        }
        break
      }
      case "N": {
        const segments = value.split(";")
        lastName = segments[0]?.trim() ?? ""
        firstName = segments[1]?.trim() ?? ""
        break
      }
      case "EMAIL":
        emails.push({ value, label: "personal" })
        break
      case "TEL":
        phones.push({ value, label: "mobile" })
        break
      case "ORG": {
        const [co, title] = value.split(";")
        company = co?.trim() || undefined
        jobTitle = title?.trim() || undefined
        break
      }
      case "NOTE":
        notes = value
        break
      default:
        break
    }
  }

  if (!firstName && !lastName && emails.length === 0 && phones.length === 0) {
    return null
  }

  return {
    firstName,
    lastName,
    company,
    jobTitle,
    emails,
    phones,
    notes,
  }
}

export function parseVCardText(text: string): ContactImportInput[] {
  const unfolded = unfoldVcardLines(text)
  const contacts: ContactImportInput[] = []
  let block: string[] = []
  let inCard = false

  for (const line of unfolded) {
    const upper = line.trim().toUpperCase()
    if (upper === "BEGIN:VCARD") {
      inCard = true
      block = []
      continue
    }
    if (upper === "END:VCARD") {
      if (inCard) {
        const parsed = parseVcardBlock(block)
        if (parsed) contacts.push(parsed)
      }
      inCard = false
      block = []
      continue
    }
    if (inCard) block.push(line)
  }

  return contacts
}

function headerIndex(headers: string[], candidates: string[]): number {
  const lower = headers.map((h) => h.toLowerCase().trim())
  for (const c of candidates) {
    const i = lower.indexOf(c)
    if (i >= 0) return i
  }
  for (let i = 0; i < lower.length; i++) {
    if (candidates.some((c) => lower[i].includes(c))) return i
  }
  return -1
}

export function parseCsvText(text: string): ContactImportInput[] {
  const lines = text.split(/\r?\n/).filter((l) => l.trim())
  if (lines.length === 0) return []

  const firstFields = parseCsvLine(lines[0])
  const nameIdx = headerIndex(firstFields, ["name", "nom", "full name", "display name"])
  const emailIdx = headerIndex(firstFields, ["email", "e-mail", "mail"])
  const phoneIdx = headerIndex(firstFields, ["phone", "telephone", "tel", "mobile"])
  const firstIdx = headerIndex(firstFields, ["first name", "prénom", "prenom", "firstname"])
  const lastIdx = headerIndex(firstFields, ["last name", "nom", "lastname"])
  const companyIdx = headerIndex(firstFields, ["company", "organisation", "organization", "entreprise"])

  const hasHeader = nameIdx >= 0 || emailIdx >= 0 || phoneIdx >= 0 || firstIdx >= 0
  const dataLines = hasHeader ? lines.slice(1) : lines

  const contacts: ContactImportInput[] = []

  for (const line of dataLines) {
    const fields = parseCsvLine(line)
    if (fields.every((f) => !f.trim())) continue

    let firstName = ""
    let lastName = ""
    let company: string | undefined

    if (hasHeader) {
      if (firstIdx >= 0) firstName = fields[firstIdx]?.trim() ?? ""
      if (lastIdx >= 0) lastName = fields[lastIdx]?.trim() ?? ""
      if (nameIdx >= 0 && !firstName && !lastName) {
        const parts = parseDisplayNameToNameParts(fields[nameIdx] ?? "")
        firstName = parts.firstName
        lastName = parts.lastName
      }
      if (companyIdx >= 0) company = fields[companyIdx]?.trim() || undefined
    } else if (fields.length === 1) {
      const entry = fields[0]
      if (entry.includes("@")) {
        contacts.push({
          firstName: "",
          lastName: "",
          emails: [{ value: entry.trim(), label: "personal" }],
          phones: [],
        })
        continue
      }
      const parts = parseDisplayNameToNameParts(entry)
      firstName = parts.firstName
      lastName = parts.lastName
    } else {
      const parts = parseDisplayNameToNameParts(fields[0] ?? "")
      firstName = parts.firstName
      lastName = parts.lastName
    }

    const emails =
      hasHeader && emailIdx >= 0 && fields[emailIdx]?.trim()
        ? [{ value: fields[emailIdx].trim(), label: "personal" }]
        : !hasHeader && fields[1]?.includes("@")
          ? [{ value: fields[1].trim(), label: "personal" }]
          : []

    const phones =
      hasHeader && phoneIdx >= 0 && fields[phoneIdx]?.trim()
        ? [{ value: fields[phoneIdx].trim(), label: "mobile" }]
        : !hasHeader && fields[2]?.trim()
          ? [{ value: fields[2].trim(), label: "mobile" }]
          : !hasHeader && fields[1]?.trim() && !fields[1].includes("@")
            ? [{ value: fields[1].trim(), label: "mobile" }]
            : []

    if (!firstName && !lastName && emails.length === 0 && phones.length === 0) continue

    contacts.push({ firstName, lastName, company, emails, phones })
  }

  return contacts
}

/** Parse one bulk line: "Name", "email", or "Name <email>". */
export function parseBulkContactLine(entry: string): ContactImportInput | null {
  const trimmed = entry.trim()
  if (!trimmed) return null

  const emailMatch = trimmed.match(/^(.+?)\s*<([^>]+)>$/)
  const email = emailMatch
    ? emailMatch[2].trim()
    : trimmed.includes("@")
      ? trimmed
      : ""
  const namePart = emailMatch
    ? emailMatch[1].trim()
    : email && !trimmed.includes("@")
      ? ""
      : trimmed

  const { firstName, lastName } = parseDisplayNameToNameParts(namePart)

  if (!firstName && !lastName && !email) return null

  return {
    firstName,
    lastName,
    emails: email ? [{ value: email, label: "personal" }] : [],
    phones: [],
  }
}

/** Split bulk text by commas or newlines (respecting quoted segments). */
export function parseBulkContactText(text: string): ContactImportInput[] {
  const entries: string[] = []
  let current = ""
  let inQuotes = false

  for (let i = 0; i < text.length; i++) {
    const ch = text[i]
    if (ch === '"') {
      inQuotes = !inQuotes
      current += ch
    } else if ((ch === "," || ch === "\n" || ch === "\r") && !inQuotes) {
      if (current.trim()) entries.push(current.trim())
      current = ""
      if (ch === "\r" && text[i + 1] === "\n") i++
    } else {
      current += ch
    }
  }
  if (current.trim()) entries.push(current.trim())

  const contacts: ContactImportInput[] = []
  for (const entry of entries) {
    const parsed = parseBulkContactLine(entry)
    if (parsed) contacts.push(parsed)
  }
  return contacts
}

export async function parseContactFile(file: File): Promise<ContactImportInput[]> {
  const text = await file.text()
  const lower = file.name.toLowerCase()
  if (lower.endsWith(".vcf") || lower.endsWith(".vcard")) {
    return parseVCardText(text)
  }
  if (lower.endsWith(".csv")) {
    return parseCsvText(text)
  }
  if (text.includes("BEGIN:VCARD")) {
    return parseVCardText(text)
  }
  return parseCsvText(text)
}
