import type { Unit } from "@app/shared-types";
import { convert, type IngredientUnitInfo } from "@app/nutrition-engine";
import { classifyExpiry, type ExpiryInput } from "@app/inventory-engine";

export interface PantryItem extends ExpiryInput {
  id: string;
  canonicalIngredientId?: string | null;
  quantity: number;
  unit: Unit;
}

export interface RecipeIngredientRequirement {
  canonicalIngredientId: string;
  displayNameSv: string;
  quantity: number;
  unit: Unit;
  optional: boolean;
}

export interface IngredientMatch {
  canonicalIngredientId: string;
  displayNameSv: string;
  required: number;
  unit: Unit;
  availableInUnit: number;
  covered: boolean;
  optional: boolean;
  /** Mest brådskande status bland matchande lagerposter. */
  mostUrgentDaysLeft: number | null;
  usesExpiringItem: boolean;
}

export interface CoverageResult {
  /** Andel obligatoriska ingredienser som täcks helt (0–1). */
  coverage: number;
  matches: IngredientMatch[];
  missing: IngredientMatch[];
  /** Ingredienser som finns hemma och snart går ut – guld för rekommendationen. */
  expiringUsed: IngredientMatch[];
}

/**
 * Matcha receptets ingredienser mot hushållets lager (spec §17–18).
 * Enhetskonvertering via nutrition-engine; poster som inte kan konverteras
 * räknas som otäckta i stället för att gissas.
 */
export function computeCoverage(
  requirements: RecipeIngredientRequirement[],
  pantry: PantryItem[],
  unitInfo: Map<string, IngredientUnitInfo>,
  today: Date = new Date(),
): CoverageResult {
  const byIngredient = new Map<string, PantryItem[]>();
  for (const item of pantry) {
    if (!item.canonicalIngredientId || item.quantity <= 0) continue;
    const list = byIngredient.get(item.canonicalIngredientId) ?? [];
    list.push(item);
    byIngredient.set(item.canonicalIngredientId, list);
  }

  const matches: IngredientMatch[] = [];
  for (const req of requirements) {
    const stock = byIngredient.get(req.canonicalIngredientId) ?? [];
    const info = unitInfo.get(req.canonicalIngredientId) ?? {};
    let available = 0;
    let mostUrgent: number | null = null;
    let usesExpiring = false;

    for (const item of stock) {
      const inReqUnit = convert(item.quantity, item.unit, req.unit, info);
      if (inReqUnit == null) continue;
      available += inReqUnit;
      const expiry = classifyExpiry(item, today);
      if (expiry.daysLeft != null) {
        mostUrgent = mostUrgent == null ? expiry.daysLeft : Math.min(mostUrgent, expiry.daysLeft);
      }
      if (expiry.status === "expiring" || expiry.status === "use_soon") usesExpiring = true;
    }

    matches.push({
      canonicalIngredientId: req.canonicalIngredientId,
      displayNameSv: req.displayNameSv,
      required: req.quantity,
      unit: req.unit,
      availableInUnit: Math.round(available * 1000) / 1000,
      covered: available + 1e-9 >= req.quantity,
      optional: req.optional,
      mostUrgentDaysLeft: mostUrgent,
      usesExpiringItem: usesExpiring,
    });
  }

  const mandatory = matches.filter((m) => !m.optional);
  const coveredCount = mandatory.filter((m) => m.covered).length;
  const coverage = mandatory.length === 0 ? 1 : coveredCount / mandatory.length;

  return {
    coverage: Math.round(coverage * 100) / 100,
    matches,
    missing: matches.filter((m) => !m.covered),
    expiringUsed: matches.filter((m) => m.covered && m.usesExpiringItem),
  };
}
