// Tolerant matching of a scanned barcode against stored product codes.
//
// Printed barcodes differ from the stored value in two ways:
//   1. separators     — "PZN-15821889" / "PZN - 15821889"  vs stored "PZN15821889"
//   2. missing prefix — "-15821889" / "15821889"            vs stored "PZN15821889"
//      (a PZN Code39 barcode encodes a leading "-" + the digits, dropping "PZN")
//
// We match in TIERS, exact first and loosest last, so a precise match always
// wins before a looser one is even tried:
//   'exact'  — trim + uppercase equality
//   'strip'  — also remove spaces + hyphens                 (handles case 1)
//   'suffix' — strip, then allow a missing leading           (handles case 2)
//              ALPHABETIC prefix in front of a numeric code
//
// Looser tiers can only ADD matches, never remove the exact ones.

export const SCAN_TIERS = ['exact', 'strip', 'suffix'] as const

// Trim + uppercase; with { strip:true } also remove spaces + hyphens, so a
// printed barcode "PZN-15821889" / "PZN - 15821889" matches stored "PZN15821889".
export const normalizeScanCode = (s: any, { strip = false }: { strip?: boolean } = {}): string => {
  let v = String(s ?? '').trim().toUpperCase()
  if (strip) v = v.replace(/[\s-]+/g, '')
  return v
}

// Compare one scanned code to one stored code at a given tolerance tier.
const codeEqAtTier = (scanned: any, stored: any, tier: string): boolean => {
  const strip = tier !== 'exact'
  const a = normalizeScanCode(scanned, { strip })
  const b = normalizeScanCode(stored, { strip })
  if (!a || !b) return false
  if (a === b) return true
  if (tier !== 'suffix') return false
  // suffix tier: whichever side is a pure number (>= 6 digits), the other must be
  // that same number behind an ALPHABETIC-only prefix, e.g. "PZN" + "15821889".
  const num = /^\d{6,}$/.test(a) ? a : (/^\d{6,}$/.test(b) ? b : null)
  if (!num) return false
  const full = num === a ? b : a
  return full.endsWith(num) && /^[A-Z]+$/.test(full.slice(0, full.length - num.length))
}

// Does the scanned code match ANY of the given stored code strings at this tier?
export const scanMatchesCodes = (scanned: any, codes: any[], { tier = 'strip' }: { tier?: string } = {}): boolean => {
  if (!String(scanned ?? '').trim()) return false
  return (codes || []).some(c => c && codeEqAtTier(scanned, c, tier))
}

// Convenience for a nested iDempiere M_Product_ID object.
export const scanMatchesProduct = (scanned: any, product: any, opts: { tier?: string } = {}): boolean => {
  if (!product) return false
  return scanMatchesCodes(
    scanned,
    [product.SKU, product.UPC, product.Value, product.mpn, product.MPN, product.EAN, product.ASIN, product.asin, product.fnsku, product.FNSKU],
    opts
  )
}

// True if the scanned code matches the given codes at ANY tier — for a single
// candidate check, where there is no other candidate to collide with.
export const scanMatchesAnyTier = (scanned: any, codes: any[]): boolean =>
  SCAN_TIERS.some(tier => scanMatchesCodes(scanned, codes, { tier }))

// Find matching items in a list — tries each tier in order and returns the
// matches from the FIRST tier that yields any (exact > strip > suffix).
export const findScanMatches = <T>(items: T[], scanned: any, getCodes: (i: T) => any[]): T[] => {
  const list = items || []
  for (const tier of SCAN_TIERS) {
    const hits = list.filter(i => scanMatchesCodes(scanned, getCodes(i), { tier }))
    if (hits.length) return hits
  }
  return []
}
