export type ArchivedMmaMatchup = {
  home_team: string;
  away_team: string;
};

export type UfcFightRow = {
  fighter1Name: string | null;
  fighter2Name: string | null;
  eventName?: string | null;
};

function normName(value: string | null | undefined): string {
  return String(value || '')
    .normalize('NFD')
    .replace(/[\u0300-\u036f]/g, '')
    .toLowerCase()
    .replace(/&/g, 'and')
    .replace(/['.\-,;:!?()]/g, '')
    .replace(/\s+/g, ' ')
    .trim();
}

function boundedEditDistance(a: string, b: string, maxDistance: number): number {
  if (Math.abs(a.length - b.length) > maxDistance) return maxDistance + 1;
  const prev = Array.from({ length: b.length + 1 }, (_, i) => i);
  const curr = new Array<number>(b.length + 1);

  for (let i = 1; i <= a.length; i++) {
    curr[0] = i;
    let rowMin = curr[0];
    for (let j = 1; j <= b.length; j++) {
      const cost = a[i - 1] === b[j - 1] ? 0 : 1;
      curr[j] = Math.min(prev[j] + 1, curr[j - 1] + 1, prev[j - 1] + cost);
      rowMin = Math.min(rowMin, curr[j]);
    }
    if (rowMin > maxDistance) return maxDistance + 1;
    for (let j = 0; j <= b.length; j++) prev[j] = curr[j];
  }

  return prev[b.length];
}

function nearNameMatch(a: string | null | undefined, b: string | null | undefined): boolean {
  const na = normName(a);
  const nb = normName(b);
  if (!na || !nb) return false;
  if (na === nb) return true;
  if (na.includes(nb) || nb.includes(na)) return true;

  const wordsA = na.split(' ');
  const wordsB = nb.split(' ');
  if (wordsA.length !== wordsB.length || wordsA.length > 3) return false;

  for (let i = 0; i < wordsA.length; i++) {
    const maxDistance = wordsA[i].length >= 6 ? 2 : 1;
    if (boundedEditDistance(wordsA[i], wordsB[i], maxDistance) > maxDistance) return false;
  }

  return true;
}

function fullPairMatches(archive: ArchivedMmaMatchup, fight: UfcFightRow): boolean {
  const direct = nearNameMatch(archive.home_team, fight.fighter1Name) && nearNameMatch(archive.away_team, fight.fighter2Name);
  const swapped = nearNameMatch(archive.home_team, fight.fighter2Name) && nearNameMatch(archive.away_team, fight.fighter1Name);
  return direct || swapped;
}

function fighterAppears(name: string, fights: UfcFightRow[]): boolean {
  return fights.some((fight) =>
    nearNameMatch(name, fight.fighter1Name) || nearNameMatch(name, fight.fighter2Name)
  );
}

export function shouldVoidArchivedMmaMatchup(
  archive: ArchivedMmaMatchup,
  fights: UfcFightRow[],
): boolean {
  if (fights.length === 0) return false;
  if (fights.some((fight) => fullPairMatches(archive, fight))) return false;
  return !fighterAppears(archive.home_team, fights) && !fighterAppears(archive.away_team, fights);
}
