import Fuse, { type IFuseOptions } from "fuse.js"
import type { FullContact } from "./types"

function stripAccents(str: string): string {
  return str.normalize("NFD").replace(/[\u0300-\u036f]/g, "")
}

const fuseOptions: IFuseOptions<FullContact> = {
  keys: [
    "firstName",
    "lastName",
    "middleName",
    "nicknames",
    "emails.value",
    "phones.value",
    "company",
    "department",
    "jobTitle",
    "addresses.street",
    "addresses.city",
    "addresses.country",
  ],
  isCaseSensitive: false,
  threshold: 0.4,
  ignoreLocation: true,
  getFn: (obj: FullContact, path: string | string[]) => {
    const raw = Fuse.config.getFn(obj, path)
    if (Array.isArray(raw)) {
      return raw.map((v) => (typeof v === "string" ? stripAccents(v) : v)) as unknown as string
    }
    if (typeof raw === "string") {
      return stripAccents(raw)
    }
    return raw as unknown as string
  },
}

let cachedFuse: { key: FullContact[]; fuse: Fuse<FullContact> } | null = null

function getFuse(contacts: FullContact[]): Fuse<FullContact> {
  if (cachedFuse && cachedFuse.key === contacts) return cachedFuse.fuse
  const fuse = new Fuse(contacts, fuseOptions)
  cachedFuse = { key: contacts, fuse }
  return fuse
}

export function searchContacts(
  contacts: FullContact[],
  query: string
): FullContact[] {
  if (!query.trim()) return contacts

  const fuse = getFuse(contacts)
  const results = fuse.search(stripAccents(query))
  const seen = new Set<string>()
  const deduped: FullContact[] = []
  for (const r of results) {
    if (!seen.has(r.item.id)) {
      seen.add(r.item.id)
      deduped.push(r.item)
    }
  }
  return deduped
}
