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

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

export interface Allocation {
  itemId: string;
  quantity: number;
  unit: Unit;
}

export interface FefoResult {
  allocations: Allocation[];
  /** Hur mycket som saknas i efterfrågad enhet (0 om täckt). */
  shortfall: number;
  covered: boolean;
}

/**
 * FEFO – First Expire, First Out (spec §17–18: prioritera utgångsdatum).
 * Plockar från de poster som går ut först; vid lika datum äldst inköp först.
 */
export function allocateFefo(
  required: number,
  unit: Unit,
  items: StockItemLike[],
  info: IngredientUnitInfo = {},
  today: Date = new Date(),
): FefoResult {
  const sorted = [...items].sort((a, b) => {
    const rank = urgencyRank(classifyExpiry(a, today)) - urgencyRank(classifyExpiry(b, today));
    if (rank !== 0) return rank;
    const pa = a.purchasedAt ?? "9999-12-31";
    const pb = b.purchasedAt ?? "9999-12-31";
    return pa.localeCompare(pb);
  });

  let remaining = required;
  const allocations: Allocation[] = [];
  for (const item of sorted) {
    if (remaining <= 1e-9) break;
    const available = convert(item.quantity, item.unit, unit, info);
    if (available == null || available <= 0) continue;
    const take = Math.min(available, remaining);
    const takeInItemUnit = convert(take, unit, item.unit, info);
    if (takeInItemUnit == null) continue;
    allocations.push({ itemId: item.id, quantity: round3(takeInItemUnit), unit: item.unit });
    remaining -= take;
  }

  return {
    allocations,
    shortfall: round3(Math.max(0, remaining)),
    covered: remaining <= 1e-9,
  };
}

function round3(v: number): number {
  return Math.round(v * 1000) / 1000;
}
