import type { EmptyContainerSize } from "@/types/importOperations"; export interface EmptyLoadPick { id: string; containerSize: EmptyContainerSize; } export interface EmptyLoadAssignment extends EmptyLoadPick { wagonSequenceNo: number; } /** * Fill wagons with the picked empties: a wagon takes ONE 40ft or TWO 20ft, * never a mix. 40ft boxes are seated first so a half-filled 20ft wagon can * never block them, and the 20s pair up behind them. * * `freeWagons` is the caller's ordered list of wagon sequence numbers with no * cargo allocation. Returns the assignments that fit plus the picks that had * no wagon left — the caller surfaces the shortfall instead of silently * dropping boxes. */ export function packEmptiesOntoWagons( picks: EmptyLoadPick[], freeWagons: number[], ): { assignments: EmptyLoadAssignment[]; unplaced: EmptyLoadPick[] } { const forty = picks.filter((pick) => pick.containerSize === "40"); const twenty = picks.filter((pick) => pick.containerSize === "20"); const assignments: EmptyLoadAssignment[] = []; const unplaced: EmptyLoadPick[] = []; const wagons = [...freeWagons]; for (const pick of forty) { const wagon = wagons.shift(); if (wagon == null) unplaced.push(pick); else assignments.push({ ...pick, wagonSequenceNo: wagon }); } for (let index = 0; index < twenty.length; index += 2) { const pair = twenty.slice(index, index + 2); const wagon = wagons.shift(); if (wagon == null) unplaced.push(...pair); else assignments.push(...pair.map((pick) => ({ ...pick, wagonSequenceNo: wagon }))); } return { assignments, unplaced }; } /** Wagons the picks consume, whether or not enough are free. */ export function wagonsNeeded(picks: EmptyLoadPick[]): number { const forty = picks.filter((pick) => pick.containerSize === "40").length; const twenty = picks.length - forty; return forty + Math.ceil(twenty / 2); }