import { describe, it, expect } from 'vitest';
import { autoFillPlacements, unitKey, validateLocalPlacements } from './containerPlacement.util';
import type { ContainerUnitRow } from '@/types/trainScheduling';

function makeUnits(containerType: string, sizeFt: number, quantity: number): ContainerUnitRow[] {
  const units: ContainerUnitRow[] = [];
  const containersPerWagon = sizeFt >= 40 ? 1 : 2;
  const wagonsPerUnit = sizeFt >= 40 ? 1 : 0.5;

  for (let i = 0; i < quantity; i++) {
    units.push({
      bookingId: 'booking-1',
      bookingReference: 'BKG-001',
      bookingContainerId: 'bc-1',
      unitIndex: i,
      containerTypeId: 'ct-1',
      containerTypeCode: containerType,
      label: `${containerType} ${i + 1}/${quantity}`,
      grossWeightTons: 25,
      sizeFt,
      wagonsPerUnit,
      containersPerWagon,
      teuSlots: sizeFt >= 40 ? 2 : 1,
    });
  }
  return units;
}

describe('containerPlacement.util', () => {
  describe('unitKey', () => {
    it('creates unique keys for units', () => {
      expect(unitKey('bc-1', 0)).toBe('bc-1:0');
      expect(unitKey('bc-1', 1)).toBe('bc-1:1');
      expect(unitKey('bc-2', 0)).toBe('bc-2:0');
    });
  });

  describe('autoFillPlacements', () => {
    it('returns empty array when no units or slots', () => {
      expect(autoFillPlacements([], [1, 2, 3])).toEqual([]);
      expect(autoFillPlacements(makeUnits('20GP', 20, 1), [])).toEqual([]);
    });

    it('places 2×20ft containers in 1 wagon slot', () => {
      const units = makeUnits('20GP', 20, 2);
      const slots = [1, 2, 3];
      const placements = autoFillPlacements(units, slots);

      expect(placements).toHaveLength(2);
      // Both 20ft containers should be in slot 1
      expect(placements[0]?.sequenceNo).toBe(1);
      expect(placements[1]?.sequenceNo).toBe(1);
    });

    it('places 6×20ft containers in 3 wagon slots (2 per wagon)', () => {
      const units = makeUnits('20GP', 20, 6);
      const slots = [1, 2, 3, 4, 5];
      const placements = autoFillPlacements(units, slots);

      expect(placements).toHaveLength(6);
      // Units 0,1 -> Slot 1
      expect(placements[0]?.sequenceNo).toBe(1);
      expect(placements[1]?.sequenceNo).toBe(1);
      // Units 2,3 -> Slot 2
      expect(placements[2]?.sequenceNo).toBe(2);
      expect(placements[3]?.sequenceNo).toBe(2);
      // Units 4,5 -> Slot 3
      expect(placements[4]?.sequenceNo).toBe(3);
      expect(placements[5]?.sequenceNo).toBe(3);
    });

    it('places 1×40ft container in 1 wagon slot', () => {
      const units = makeUnits('40GP', 40, 1);
      const slots = [1, 2, 3];
      const placements = autoFillPlacements(units, slots);

      expect(placements).toHaveLength(1);
      expect(placements[0]?.sequenceNo).toBe(1);
    });

    it('places 3×40ft containers in 3 wagon slots (1 per wagon)', () => {
      const units = makeUnits('40GP', 40, 3);
      const slots = [1, 2, 3, 4, 5];
      const placements = autoFillPlacements(units, slots);

      expect(placements).toHaveLength(3);
      // Each 40ft container gets its own slot
      expect(placements[0]?.sequenceNo).toBe(1);
      expect(placements[1]?.sequenceNo).toBe(2);
      expect(placements[2]?.sequenceNo).toBe(3);
    });

    it('handles mixed 20ft and 40ft containers correctly', () => {
      const units20 = makeUnits('20GP', 20, 2);
      const units40 = makeUnits('40GP', 40, 1);
      const units = [...units20, ...units40];
      const slots = [1, 2, 3, 4, 5];
      const placements = autoFillPlacements(units, slots);

      expect(placements).toHaveLength(3);
      // First two 20ft containers share slot 1
      expect(placements[0]?.sequenceNo).toBe(1);
      expect(placements[1]?.sequenceNo).toBe(1);
      // 40ft container gets slot 2
      expect(placements[2]?.sequenceNo).toBe(2);
    });

    it('never shares a wagon between a 40ft and a 20ft when the 40ft comes first', () => {
      // Regression: a 40ft (2 TEU) must occupy its own wagon and never pair with a 20ft.
      const units40 = makeUnits('40GP', 40, 1);
      const units20 = makeUnits('20GP', 20, 2);
      const units = [...units40, ...units20];
      const slots = [1, 2, 3, 4];
      const placements = autoFillPlacements(units, slots);

      expect(placements).toHaveLength(3);
      // 40ft alone on slot 1
      expect(placements[0]?.sequenceNo).toBe(1);
      // both 20ft together on slot 2 — NOT on slot 1 with the 40ft
      expect(placements[1]?.sequenceNo).toBe(2);
      expect(placements[2]?.sequenceNo).toBe(2);
    });

    it('falls back to last slot when running out of slots', () => {
      const units = makeUnits('20GP', 20, 6);
      const slots = [1, 2]; // Only 2 slots available
      const placements = autoFillPlacements(units, slots);

      expect(placements).toHaveLength(6);
      // First 4 units fit in slots 1 and 2
      expect(placements[0]?.sequenceNo).toBe(1);
      expect(placements[1]?.sequenceNo).toBe(1);
      expect(placements[2]?.sequenceNo).toBe(2);
      expect(placements[3]?.sequenceNo).toBe(2);
      // Remaining units fall back to last available slot (slot 2)
      expect(placements[4]?.sequenceNo).toBe(2);
      expect(placements[5]?.sequenceNo).toBe(2);
    });

    it('defaults to 2 containers per wagon when sizeFt is not provided', () => {
      const units: ContainerUnitRow[] = [
        {
          bookingId: 'booking-1',
          bookingReference: 'BKG-001',
          bookingContainerId: 'bc-1',
          unitIndex: 0,
          containerTypeId: 'ct-1',
          containerTypeCode: '20GP',
          label: 'Container 1',
          grossWeightTons: 25,
          // sizeFt not provided, should default to 2 per wagon
        },
        {
          bookingId: 'booking-1',
          bookingReference: 'BKG-001',
          bookingContainerId: 'bc-1',
          unitIndex: 1,
          containerTypeId: 'ct-1',
          containerTypeCode: '20GP',
          label: 'Container 2',
          grossWeightTons: 25,
        },
      ];
      const slots = [1, 2, 3];
      const placements = autoFillPlacements(units, slots);

      expect(placements[0]?.sequenceNo).toBe(1);
      expect(placements[1]?.sequenceNo).toBe(1);
    });

    it('uses 1 container per wagon for 40ft when sizeFt is 40', () => {
      const units: ContainerUnitRow[] = [
        {
          bookingId: 'booking-1',
          bookingReference: 'BKG-001',
          bookingContainerId: 'bc-1',
          unitIndex: 0,
          containerTypeId: 'ct-1',
          containerTypeCode: '40GP',
          label: 'Container 1',
          grossWeightTons: 25,
          sizeFt: 40,
        },
        {
          bookingId: 'booking-1',
          bookingReference: 'BKG-001',
          bookingContainerId: 'bc-1',
          unitIndex: 1,
          containerTypeId: 'ct-1',
          containerTypeCode: '40GP',
          label: 'Container 2',
          grossWeightTons: 25,
          sizeFt: 40,
        },
      ];
      const slots = [1, 2, 3];
      const placements = autoFillPlacements(units, slots);

      // Each 40ft container should get its own slot
      expect(placements[0]?.sequenceNo).toBe(1);
      expect(placements[1]?.sequenceNo).toBe(2);
    });
  });

  describe('validateLocalPlacements', () => {
    it('returns empty array for valid placements', () => {
      const units = makeUnits('20GP', 20, 1);
      const placements = [
        {
          bookingContainerId: 'bc-1',
          unitIndex: 0,
          sequenceNo: 1,
          containerNumber: 'CNTR123',
        },
      ];
      expect(validateLocalPlacements(units, placements)).toEqual([]);
    });

    it('returns error for missing slot', () => {
      const units = makeUnits('20GP', 20, 1);
      const placements: ReturnType<typeof autoFillPlacements> = [];
      const issues = validateLocalPlacements(units, placements);
      expect(issues.some((i) => i.includes('Slot missing'))).toBe(true);
    });

    it('returns error for missing container number', () => {
      const units = makeUnits('20GP', 20, 1);
      const placements = [
        {
          bookingContainerId: 'bc-1',
          unitIndex: 0,
          sequenceNo: 1,
          // No containerNumber or containerId
        },
      ];
      const issues = validateLocalPlacements(units, placements);
      expect(issues.some((i) => i.includes('Enter a container number'))).toBe(true);
    });

    it('returns error for duplicate container numbers', () => {
      const units = makeUnits('20GP', 20, 2);
      const placements = [
        {
          bookingContainerId: 'bc-1',
          unitIndex: 0,
          sequenceNo: 1,
          containerNumber: 'CNTR123',
        },
        {
          bookingContainerId: 'bc-1',
          unitIndex: 1,
          sequenceNo: 1,
          containerNumber: 'CNTR123', // Duplicate!
        },
      ];
      const issues = validateLocalPlacements(units, placements);
      expect(issues.some((i) => i.includes('Duplicate container number'))).toBe(true);
    });
  });
});
