/**
 * Draw order for a schedule's consist.
 *
 * A slot's stored `sequenceNo` is its place in the wagon PLAN, not its place in
 * the train. The train's real coupling order lives on the physical wagons
 * (`wagons.sequence_number`), which the caller passes in already ordered — ASC
 * normally, DESC for a `reverseWagonOrder` schedule.
 *
 * Ordering by the physical wagon is what keeps the drawing honest:
 *  - moving a load between wagons repaints WHICH wagon is loaded and never
 *    shuffles the train, because each slot is drawn wherever its wagon sits;
 *  - a train-builder reorder lands on the next read, allocations included,
 *    since the order is derived on every read instead of copied at pin time.
 *
 * Slots with no physical wagon (not pinned yet, or a schedule that isn't tied
 * to a built train) have no place in the consist — they keep slot order, last.
 */
export interface ConsistOrderable {
  sequenceNo: number;
  physicalWagonId?: string | null;
}

export interface ConsistOrderOptions {
  /**
   * Every wagon coupled to the built train, in real coupling order (already
   * reversed by the caller for a `reverseWagonOrder` schedule). Empty for a
   * frozen schedule or one with no built train — the consist then keeps slot
   * order.
   */
  physicalWagonIdsInOrder: string[];
  reverseWagonOrder?: boolean;
}

export const orderConsistWagons = <T extends ConsistOrderable>(
  wagons: T[],
  { physicalWagonIdsInOrder, reverseWagonOrder }: ConsistOrderOptions,
): (T & { position: number })[] => {
  const physicalOrder = new Map(physicalWagonIdsInOrder.map((id, index) => [id, index]));
  const bySlotSequence = (a: T, b: T) =>
    reverseWagonOrder ? b.sequenceNo - a.sequenceNo : a.sequenceNo - b.sequenceNo;

  const ordered = physicalOrder.size
    ? [...wagons].sort((a, b) => {
        const ai = a.physicalWagonId ? physicalOrder.get(a.physicalWagonId) : undefined;
        const bi = b.physicalWagonId ? physicalOrder.get(b.physicalWagonId) : undefined;
        if (ai == null && bi == null) return bySlotSequence(a, b);
        if (ai == null) return 1;
        if (bi == null) return -1;
        return ai - bi;
      })
    : [...wagons].sort(bySlotSequence);

  return ordered.map((wagon, index) => ({ ...wagon, position: index + 1 }));
};
