import { db, ingredients } from "@epicure/db";

export type IngredientAliasIndex = Map<string, string>;

// Case- and accent-insensitive so "café"/"cafe" and "Épinard"/"epinard"
// compare equal — NFD splits an accented letter into base letter +
// combining mark, then the combining marks (U+0300-U+036F) are stripped.
function normalize(name: string): string {
  return name.trim().toLowerCase().normalize("NFD").replace(/[̀-ͯ]/g, "");
}

/**
 * Loads every canonical ingredient's name + aliases into a flat
 * normalized-string -> canonical-ingredient-id map, once per request. Used
 * to recognize that "sel", "sel fin", and "table salt" are all the same
 * ingredient, without requiring every recipe/pantry row to carry a stored
 * ingredientId (they don't — this resolves purely from the free-text name
 * at comparison time).
 */
export async function loadIngredientAliasIndex(): Promise<IngredientAliasIndex> {
  const rows = await db.select({ id: ingredients.id, name: ingredients.name, aliases: ingredients.aliases }).from(ingredients);
  const index: IngredientAliasIndex = new Map();
  for (const row of rows) {
    index.set(normalize(row.name), row.id);
    for (const alias of row.aliases) {
      index.set(normalize(alias), row.id);
    }
  }
  return index;
}

/** Canonical ingredient id if `rawName` matches a known name/alias exactly
 * (case/accent/whitespace-insensitive); otherwise the normalized rawName
 * itself, so unmatched items still compare equal to other unmatched items
 * with the same text (today's behavior, unchanged for anything not seeded). */
export function resolveIngredientKey(rawName: string, index: IngredientAliasIndex): string {
  const normalized = normalize(rawName);
  return index.get(normalized) ?? normalized;
}

/** Single-name lookup (pantry add/edit). Loads the same small table as
 * loadIngredientAliasIndex and compares in JS rather than in SQL — accent
 * stripping via NFD has no simple SQL equivalent without the `unaccent`
 * extension, which isn't guaranteed to be installed. The ingredients table
 * is small (tens to low hundreds of rows), so this is cheap. Returns null
 * when there's no canonical match, meaning the item stays a plain freeform
 * pantry entry. */
export async function findIngredientIdByName(rawName: string): Promise<string | null> {
  const normalized = normalize(rawName);
  if (!normalized) return null;
  const index = await loadIngredientAliasIndex();
  return index.get(normalized) ?? null;
}
