/**
 * Adivinhar qual insumo do catalogo um item do fornecedor abastece.
 *
 * Isto existe para uma coisa so: transformar a ligacao dos 72 materiais de
 * fornecedor que ja estao no banco numa tela de confirmar, em vez de um
 * trabalho de digitar tudo de novo. A sugestao nunca liga sozinha; ela so
 * aponta, e quem decide e a pessoa.
 *
 * A comparacao e por nome porque e a unica coisa que os dois cadastros tem em
 * comum hoje. Por isso a confianca vem junto: "alta" e nome identico, e o
 * resto pede olhada.
 */

export type InsumoParaCasar = {
  id: string;
  name: string;
  unit?: string | null;
  cost_per_unit?: number | string | null;
  current_stock?: number | string | null;
  track_stock?: boolean | null;
  min_stock_alert?: number | string | null;
  purchase_format?: string | null;
  is_active?: boolean | null;
};

export type Confianca = "alta" | "media" | "baixa";

export type Sugestao = { insumo: InsumoParaCasar; confianca: Confianca };

/** Sem acento, sem pontuacao, minusculo, um espaco entre palavras. */
export function normalizarNome(valor: unknown) {
  return String(valor ?? "")
    .normalize("NFD")
    .replace(/[̀-ͯ]/g, "")
    .toLowerCase()
    .replace(/[^a-z0-9]+/g, " ")
    .trim();
}

/* Palavras que aparecem em quase todo nome de insumo grafico e nao ajudam a
   distinguir um do outro. Contar elas faria "Papel A" casar com "Papel B". */
const PALAVRAS_VAZIAS = new Set(["de", "do", "da", "com", "para", "em", "e", "o", "a", "un", "und", "unid"]);

function palavrasUteis(nome: string) {
  return normalizarNome(nome)
    .split(" ")
    .filter((p) => p.length > 1 && !PALAVRAS_VAZIAS.has(p));
}

/**
 * Quanto dois nomes se parecem, de 0 a 1: quantas palavras uteis eles dividem
 * sobre o total de palavras distintas dos dois.
 */
export function semelhanca(a: string, b: string) {
  const pa = new Set(palavrasUteis(a));
  const pb = new Set(palavrasUteis(b));
  if (pa.size === 0 || pb.size === 0) return 0;

  let comuns = 0;
  for (const palavra of pa) if (pb.has(palavra)) comuns += 1;

  const distintas = new Set([...pa, ...pb]).size;
  return comuns / distintas;
}

export function sugerirInsumo(nomeDoFornecedor: string, catalogo: InsumoParaCasar[]): Sugestao | null {
  const alvo = normalizarNome(nomeDoFornecedor);
  if (!alvo) return null;

  const ativos = catalogo.filter((item) => item.is_active !== false);

  /* 1. Nome identico depois de tirar acento e pontuacao. */
  const identico = ativos.find((item) => normalizarNome(item.name) === alvo);
  if (identico) return { insumo: identico, confianca: "alta" };

  /* 2. Um nome inteiro dentro do outro ("Papel Couche 250g" x "Couche 250g"). */
  const contido = ativos.find((item) => {
    const nome = normalizarNome(item.name);
    if (!nome) return false;
    return nome.includes(alvo) || alvo.includes(nome);
  });
  if (contido) return { insumo: contido, confianca: "media" };

  /* 3. Palavras em comum. Metade ja e suficiente para valer a olhada, mas
        abaixo disso a sugestao atrapalha mais do que ajuda. */
  let melhor: Sugestao | null = null;
  let melhorNota = 0;
  for (const item of ativos) {
    const nota = semelhanca(nomeDoFornecedor, item.name);
    if (nota > melhorNota) {
      melhorNota = nota;
      melhor = { insumo: item, confianca: nota >= 0.75 ? "media" : "baixa" };
    }
  }

  return melhorNota >= 0.5 ? melhor : null;
}
