/**
 * Booking-time 20ft weight-pairing rule.
 *
 * A container wagon holds two 20ft containers (2 TEU). When two 20ft ride the
 * same wagon their gross-weight difference must not exceed `maxPairDiffTons`
 * (global rule `max20ftPairWeightDiffTons`, default 10t) so the wagon load stays
 * balanced. 40ft containers occupy a whole wagon alone and never pair.
 *
 * At booking time the customer enters every 20ft container's weight but not its
 * wagon slot, so we auto-pair: sort the 20ft weights ascending and pair adjacent
 * (0-1, 2-3, …). Adjacent pairing minimises the diff of every pair, so if ANY
 * valid pairing exists this one finds it — a violation here means no balanced
 * pairing is possible and the booking must be blocked. An odd leftover 20ft is
 * fine: it has no partner in this booking and flows to consolidation.
 */

export interface Container20ftUnit {
  /** Human label for messages, e.g. the container number. */
  label: string;
  grossWeightTons: number;
}

export interface PairingViolation {
  message: string;
  /** The two container labels whose pairing exceeds the diff cap. */
  labels: [string, string];
  diffTons: number;
}

const round2 = (n: number): number => Math.round(n * 100) / 100;

/**
 * Validate that the given 20ft units can all be paired onto wagons within the
 * weight-difference cap. Returns one violation per over-cap adjacent pair (empty
 * when every wagon pair is balanced or there is nothing to pair). A single
 * leftover unit (odd count) is not a violation.
 */
export function validate20ftWeightPairing(
  units: Container20ftUnit[],
  maxPairDiffTons: number,
): PairingViolation[] {
  if (units.length < 2 || maxPairDiffTons == null) return [];

  // Ascending by weight: adjacent pairs have the smallest possible diffs.
  const sorted = [...units].sort((a, b) => a.grossWeightTons - b.grossWeightTons);
  const violations: PairingViolation[] = [];

  for (let i = 0; i + 1 < sorted.length; i += 2) {
    const a = sorted[i];
    const b = sorted[i + 1];
    const diff = Math.abs(a.grossWeightTons - b.grossWeightTons);
    if (diff > maxPairDiffTons) {
      violations.push({
        message:
          `20ft containers ${a.label} (${round2(a.grossWeightTons)}T) and ` +
          `${b.label} (${round2(b.grossWeightTons)}T) cannot share a wagon: ` +
          `weight difference ${round2(diff)}T exceeds the ${maxPairDiffTons}T limit.`,
        labels: [a.label, b.label],
        diffTons: round2(diff),
      });
    }
  }
  return violations;
}
