import type { ContainerPlacement, ContainerUnitRow } from "@/types/trainScheduling";

export function unitKey(bookingContainerId: string, unitIndex: number) {
  return `${bookingContainerId}:${unitIndex}`;
}

type ScheduleWagonForPlacements = {
  sequenceNo: number;
  allocations?: Array<{
    containerItems?: Array<{
      bookingContainerId?: string | null;
      positionOnWagon?: number | null;
      containerId?: string | null;
      containerNumber?: string | null;
    }>;
  }>;
};

export function placementsFromScheduleWagons(
  wagons: ScheduleWagonForPlacements[],
): ContainerPlacement[] {
  const placements: ContainerPlacement[] = [];

  for (const wagon of wagons) {
    for (const allocation of wagon.allocations ?? []) {
      for (const containerItem of allocation.containerItems ?? []) {
        if (containerItem.bookingContainerId && containerItem.positionOnWagon != null) {
          placements.push({
            bookingContainerId: containerItem.bookingContainerId,
            unitIndex: containerItem.positionOnWagon - 1,
            sequenceNo: wagon.sequenceNo,
            containerNumber: containerItem.containerNumber ?? undefined,
          });
        }
      }
    }
  }

  return placements;
}

export function mergePlacementsWithSaved(
  autoFilled: ContainerPlacement[],
  saved: ContainerPlacement[],
): ContainerPlacement[] {
  const savedMap = new Map(
    saved.map((placement) => [unitKey(placement.bookingContainerId, placement.unitIndex), placement]),
  );

  return autoFilled.map((placement) => {
    const existing = savedMap.get(unitKey(placement.bookingContainerId, placement.unitIndex));
    if (existing?.containerNumber?.trim()) {
      return {
        ...placement,
        containerNumber: existing.containerNumber,
        containerId: undefined,
        sealNumber: existing.sealNumber,
      };
    }
    return placement;
  });
}

export function autoFillPlacements(
  units: ContainerUnitRow[],
  containerSlots: number[],
): ContainerPlacement[] {
  if (!units.length || !containerSlots.length) return [];

  const placements: ContainerPlacement[] = [];
  // Pack by TEU: a wagon holds 2 TEU (one 40ft, or two 20ft). This MUST mirror the
  // backend allocation (allocateContainersToSlots) so a placement's sequenceNo lands on
  // the same wagon the booking is allocated to — and a 40ft never shares with a 20ft.
  const MAX_TEU_PER_WAGON = 2;
  let currentSlotIndex = 0;
  let teuInCurrentSlot = 0;

  for (const unit of units) {
    const teu = unit.teuSlots ?? (unit.sizeFt && unit.sizeFt >= 40 ? 2 : 1);

    if (teuInCurrentSlot > 0 && teuInCurrentSlot + teu > MAX_TEU_PER_WAGON) {
      currentSlotIndex += 1;
      teuInCurrentSlot = 0;
    }

    const sequenceNo =
      containerSlots[Math.min(currentSlotIndex, containerSlots.length - 1)] ??
      containerSlots[containerSlots.length - 1] ??
      containerSlots[0];

    placements.push({
      bookingContainerId: unit.bookingContainerId,
      unitIndex: unit.unitIndex,
      sequenceNo,
      containerNumber: unit.containerNumber ?? undefined,
    });

    teuInCurrentSlot += teu;
  }

  return placements;
}

export function validateLocalPlacements(
  units: ContainerUnitRow[],
  placements: ContainerPlacement[],
): string[] {
  const issues: string[] = [];
  const numbers = new Set<string>();

  for (const unit of units) {
    const placement = placements.find(
      (p) =>
        p.bookingContainerId === unit.bookingContainerId && p.unitIndex === unit.unitIndex,
    );
    if (!placement?.sequenceNo) {
      issues.push(`Slot missing for ${unit.label}`);
      continue;
    }
    if (!placement.containerNumber?.trim()) {
      issues.push(`Enter a container number for ${unit.label}`);
    }
    if (placement.containerNumber?.trim()) {
      const normalized = placement.containerNumber.trim().toUpperCase();
      if (numbers.has(normalized)) {
        issues.push(`Duplicate container number ${normalized}`);
      }
      numbers.add(normalized);
    }
  }

  return issues;
}
