import { Booking } from '../bookings/entities/booking.entity';
import { WagonType } from '../wagon-types/entities/wagon-type.entity';
import {
  computeFleetAvailability,
  selectBookingsWithinFleetCap,
  sortBookingsForScheduling,
  summarizeFleetWarnings,
  wagonsRequiredForBooking,
} from './fleet-plan.util';
import { buildContainerWagonPlan, type WagonPlanSlot } from './wagon-plan.util';

const nw5: WagonType = {
  id: 'wt-nw5',
  code: 'NW5',
  name: 'Flat Wagon',
  capacityTons: 70,
  lengthMeters: 14,
  maxWagonsPerTrain: 53,
  supportedLoadTypes: ['CONTAINER'],
  isActive: true,
  supportsContainer: true,
} as WagonType;

const makeBooking = (
  id: string,
  extra: Partial<Booking> = {},
): Booking =>
  ({
    id,
    reference: id,
    freightType: 'CONTAINER',
    isGovernment: false,
    priorityScore: 0,
    scheduledDate: new Date('2026-06-20T08:00:00.000Z'),
    cargoTotalWeightVgm: 50,
    bookingContainers: [{ id: `${id}-line`, quantity: 2, wagonsRequired: 1, vgmPerUnitTons: 25 }],
    ...extra,
  }) as Booking;

describe('fleet-plan.util', () => {
  it('sorts bookings government first, then priority, then date', () => {
    const bookings = [
      makeBooking('late', { scheduledDate: new Date('2026-06-22T08:00:00.000Z') }),
      makeBooking('gov', { isGovernment: true, priorityScore: 0 }),
      makeBooking('prio', { priorityScore: 10 }),
    ];

    const sorted = sortBookingsForScheduling(bookings);
    expect(sorted.map((b) => b.id)).toEqual(['gov', 'prio', 'late']);
  });

  it('computes fleet availability with shortfall', () => {
    const plan: WagonPlanSlot[] = buildContainerWagonPlan(
      [
        makeBooking('b1', {
          bookingContainers: [
            { id: 'b1-line', quantity: 4, wagonsRequired: 2, vgmPerUnitTons: 25 } as never,
          ],
        }),
      ],
      nw5,
    );
    const fleetByTypeId = new Map([[nw5.id, 1]]);

    const rows = computeFleetAvailability(plan, fleetByTypeId, new Map([[nw5.id, 'NW5']]));
    const nw5Row = rows.find((r) => r.wagonTypeCode === 'NW5');

    expect(nw5Row?.needed).toBe(2);
    expect(nw5Row?.available).toBe(1);
    expect(nw5Row?.shortfall).toBe(1);
  });

  it('defers lower-priority bookings when fleet is insufficient', () => {
    const high = makeBooking('high', {
      priorityScore: 100,
      bookingContainers: [
        { id: 'high-line', quantity: 2, wagonsRequired: 2, vgmPerUnitTons: 25 } as never,
      ],
    });
    const low = makeBooking('low', {
      priorityScore: 1,
      bookingContainers: [
        { id: 'low-line', quantity: 2, wagonsRequired: 2, vgmPerUnitTons: 25 } as never,
      ],
    });
    const fleet = new Map([[nw5.id, 2]]);

    const { fitting, deferred } = selectBookingsWithinFleetCap(
      [low, high],
      fleet,
      () => nw5.id,
    );

    expect(fitting.map((b) => b.id)).toEqual(['high']);
    expect(deferred).toHaveLength(1);
    expect(deferred[0]?.id).toBe('low');
    expect(deferred[0]?.reason).toContain('2');
  });

  it('summarizes fleet shortage warnings', () => {
    const warnings = summarizeFleetWarnings(
      [
        {
          wagonTypeId: nw5.id,
          wagonTypeCode: 'NW5',
          needed: 5,
          available: 2,
          shortfall: 3,
        },
      ],
      [{ id: 'b1', reference: 'BKG-1', reason: 'No wagons' }],
    );

    expect(warnings.some((w) => w.includes('Fleet shortage'))).toBe(true);
    expect(warnings.some((w) => w.includes('deferred'))).toBe(true);
  });

  it('counts wagons required per booking from container lines', () => {
    const booking = makeBooking('b1', {
      bookingContainers: [
        { id: 'b1-line-0', quantity: 2, wagonsRequired: 1, vgmPerUnitTons: 25 } as never,
        { id: 'b1-line-1', quantity: 1, wagonsRequired: 1, vgmPerUnitTons: 25 } as never,
      ],
    });
    expect(wagonsRequiredForBooking(booking)).toBe(2);
  });
});
