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

export type IngredientAliasIndex = Map<string, string>;

function normalize(name: string): string {
  return name.trim().toLowerCase();
}

/**
 * Loads every canonical ingredient's name + aliases into a flat
 * lowercased-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/whitespace-insensitive); otherwise the normalized rawName itself,
 * so unmatched items still compare equal to other unmatched items with the
 * exact 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) — a direct query rather than
 * loading the whole table, since this runs once per add/rename rather than
 * in a loop. 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 [match] = await db
    .select({ id: ingredients.id })
    .from(ingredients)
    .where(sql`lower(${ingredients.name}) = ${normalized} or exists (select 1 from unnest(${ingredients.aliases}) a where lower(a) = ${normalized})`)
    .limit(1);
  return match?.id ?? null;
}
