import { NextRequest, NextResponse } from "next/server";
import { db, pantryItems, eq, asc, inArray } from "@epicure/db";
import { requireSessionOrApiKey } from "@/lib/api-auth";
import { loadIngredientAliasIndex, resolveIngredientKey } from "@/lib/ingredient-match";

function normalizeUnit(unit: string | null): string {
  return (unit ?? "").trim().toLowerCase();
}

/**
 * One-shot cleanup for pantry items that turn out to be the same ingredient
 * under different names ("sel", "sel fin", "sel de table") — a case the
 * alias index (lib/ingredient-match.ts) only prevents going forward, not
 * for rows added before it existed. Groups by resolved ingredient key alone
 * (unit is NOT part of the grouping key — two rows of the same ingredient
 * still merge even if one has no quantity/unit at all, or the two use
 * different units); for any group with more than one row, merges into the
 * oldest row and deletes the rest.
 *
 * Quantities are only summed when every row in the group has a parseable
 * quantity AND they all share the same unit — mixing units without
 * conversion, or mixing a known and an unknown amount, would silently
 * invent a number, so the first known (quantity, unit) pair is kept
 * instead. Notes are concatenated (nothing is dropped); expiresAt keeps the
 * soonest date (the conservative choice — better to under-promise
 * freshness than over).
 */
export async function POST(req: NextRequest) {
  const { session, response } = await requireSessionOrApiKey(req);
  if (response) return response;
  const userId = session!.user.id;

  const [items, aliasIndex] = await Promise.all([
    db.query.pantryItems.findMany({ where: eq(pantryItems.userId, userId), orderBy: asc(pantryItems.createdAt) }),
    loadIngredientAliasIndex(),
  ]);

  const groups = new Map<string, typeof items>();
  for (const item of items) {
    const key = resolveIngredientKey(item.rawName, aliasIndex);
    const group = groups.get(key) ?? [];
    group.push(item);
    groups.set(key, group);
  }

  let mergedGroups = 0;
  let removed = 0;
  const idsToDelete: string[] = [];

  for (const group of groups.values()) {
    if (group.length < 2) continue;
    mergedGroups++;

    const [survivor, ...rest] = group;
    const quantities = group.map((i) => (i.quantity ? parseFloat(i.quantity) : null));
    const units = group.map((i) => normalizeUnit(i.unit));
    const sameUnit = units.every((u) => u === units[0]);
    const allParseable = sameUnit && quantities.every((q) => q !== null && !isNaN(q));
    const firstKnownIndex = quantities.findIndex((q) => q !== null && !isNaN(q));
    const mergedQuantity = allParseable
      ? String(quantities.reduce((sum, q) => sum! + q!, 0))
      : firstKnownIndex !== -1 ? quantities[firstKnownIndex]!.toString() : survivor!.quantity;
    const mergedUnit = allParseable
      ? survivor!.unit
      : firstKnownIndex !== -1 ? group[firstKnownIndex]!.unit : survivor!.unit;

    const mergedNotes = [...new Set(group.map((i) => i.notes?.trim()).filter((n): n is string => !!n))].join("; ") || null;
    const mergedAisle = group.find((i) => i.aisle)?.aisle ?? null;
    const mergedIngredientId = group.find((i) => i.ingredientId)?.ingredientId ?? null;
    const expiryDates = group.map((i) => i.expiresAt).filter((d): d is Date => d !== null);
    const mergedExpiresAt = expiryDates.length > 0 ? new Date(Math.min(...expiryDates.map((d) => d.getTime()))) : null;

    await db.update(pantryItems).set({
      quantity: mergedQuantity,
      unit: mergedUnit,
      notes: mergedNotes,
      aisle: mergedAisle,
      ingredientId: mergedIngredientId,
      expiresAt: mergedExpiresAt,
    }).where(eq(pantryItems.id, survivor!.id));

    idsToDelete.push(...rest.map((i) => i.id));
    removed += rest.length;
  }

  if (idsToDelete.length > 0) {
    await db.delete(pantryItems).where(inArray(pantryItems.id, idsToDelete));
  }

  return NextResponse.json({ mergedGroups, removed });
}
