// universalSlugMapping.ts  (ES5-safe)

/** Accent folding (lowercase only; we lowercase before folding) */
function foldAccents(s: string): string {
  // groups cover common Latin-1 + a few extras
  s = s.replace(/[àáâãäåāăąª]/g, 'a');
  s = s.replace(/[æ]/g, 'ae');
  s = s.replace(/[çćĉċč]/g, 'c');
  s = s.replace(/[ďđ]/g, 'd');
  s = s.replace(/[èéêëēĕėęě]/g, 'e');
  s = s.replace(/[ƒ]/g, 'f');
  s = s.replace(/[ĝğġģ]/g, 'g');
  s = s.replace(/[ĥħ]/g, 'h');
  s = s.replace(/[ìíîïĩīĭįı]/g, 'i');
  s = s.replace(/[ĵ]/g, 'j');
  s = s.replace(/[ķ]/g, 'k');
  s = s.replace(/[ĺļľł]/g, 'l');
  s = s.replace(/[ñńņňŉŋ]/g, 'n');
  s = s.replace(/[òóôõöøōŏőº]/g, 'o');
  s = s.replace(/[œ]/g, 'oe');
  s = s.replace(/[ŕŗř]/g, 'r');
  s = s.replace(/[śŝşš]/g, 's');
  s = s.replace(/[ß]/g, 'ss');
  s = s.replace(/[ţťŧ]/g, 't');
  s = s.replace(/[ùúûüũūŭůűų]/g, 'u');
  s = s.replace(/[ŵ]/g, 'w');
  s = s.replace(/[ýÿŷ]/g, 'y');
  s = s.replace(/[źżž]/g, 'z');
  return s;
}

/** Word-level expansions & legal-type aliases */
const TOKEN_EQUIVS: { [k: string]: string } = {
  // symbols & html
  '&': 'and',
  '&amp;': 'and',
  '@': 'at',
  '+': 'plus',
  '#': 'number',
  '/': 'and',
  '\\': 'and',

  // company structures / legal
  pvt: 'private',
  'pvt.': 'private',
  ltd: 'limited',
  'ltd.': 'limited',
  llp: 'limited liability partnership',
  'llp.': 'limited liability partnership',
  plc: 'public limited company',
  'plc.': 'public limited company',
  llc: 'limited liability company',
  'llc.': 'limited liability company',
  lp: 'limited partnership',
  'lp.': 'limited partnership',

  // common combos
  pvtltd: 'private limited',
  'pvt. ltd.': 'private limited',
  'pvt. ltd': 'private limited',
  'pvt ltd': 'private limited',
  'pvt limited': 'private limited',
  'private ltd': 'private limited',
  'co., ltd': 'company limited',
  'co.,ltd': 'company limited',
  'co. ltd': 'company limited',
  'co ltd': 'company limited',

  // company/corporation
  co: 'company',
  'co.': 'company',
  corp: 'corporation',
  'corp.': 'corporation',
  inc: 'incorporated',
  'inc.': 'incorporated',
  incorporation: 'incorporated',

  // govt / nonprofit
  govt: 'government',
  'govt.': 'government',
  gov: 'government',
  ngo: 'non government organization',
  npo: 'non profit organization',

  // general words
  intl: 'international',
  'intl.': 'international',
  tech: 'technology',
  'tech.': 'technology',
  engg: 'engineering',
  'engg.': 'engineering',
  edu: 'education',
  'edu.': 'education',
  univ: 'university',
  'univ.': 'university',
  inst: 'institute',
  'inst.': 'institute',
  dept: 'department',
  'dept.': 'department',

  // countries (common)
  ind: 'india',
  'ind.': 'india',
  usa: 'united states',
  uk: 'united kingdom',
  uae: 'united arab emirates',
  sa: 'south africa',
  hk: 'hong kong',
};

/** ASCII cleaner */
function cleanToken(t: string): string {
  return t.replace(/[^a-z0-9]/gi, '');
}

/** Main: normalize any input to a comparable key */
export function toMatchKey(raw?: string | null): string {
  if (!raw) return '';
  let s = String(raw).toLowerCase();

  // isolate symbol-tokens so they become separate words
  s = s.replace(/&amp;/g, ' &amp; ');
  s = s.replace(/&/g, ' & ');
  s = s.replace(/@/g, ' @ ');
  s = s.replace(/\+/g, ' + ');
  s = s.replace(/#/g, ' # ');
  s = s.replace(/[\/\\]/g, ' / ');

  // punctuation / brackets / hyphens to spaces
  s = s.replace(/[-_]/g, ' ');
  s = s.replace(/[\(\)\[\]\{\}]/g, ' ');
  s = s.replace(/[.,]/g, ' ');

  // squeeze spaces
  s = s.replace(/\s+/g, ' ').trim();

  // fold accents after lowercasing
  s = foldAccents(s);

  // tokenize
  const tokens = s.split(' ').filter(Boolean);

  // expand tokens using mapping (no flatMap)
  const expanded = [] as string[];
  for (let i = 0; i < tokens.length; i++) {
    const t = tokens[i];
    const mapped = TOKEN_EQUIVS[t] || t;
    const parts = mapped.split(/\s+/);
    for (let j = 0; j < parts.length; j++) expanded.push(parts[j]);
  }

  // clean and keep
  const normalized = [];
  for (let k = 0; k < expanded.length; k++) {
    const ct = cleanToken(expanded[k]);
    if (ct) normalized.push(ct);
  }

  return normalized.join(' ');
}

export function isSlugEquivalent(
  a?: string | null,
  b?: string | null,
): boolean {
  return toMatchKey(a) === toMatchKey(b);
}

export function leadTokens(slugOrName: string, n: number): string[] {
  const toks = toMatchKey(slugOrName).split(' ').filter(Boolean);
  return toks.slice(0, n);
}
