import { BookingSplitService } from './booking-split.service';
import { Booking } from '../bookings/entities/booking.entity';
import { BookingContainer } from '../bookings/entities/booking-container.entity';
import { BookingContainerUnit } from '../bookings/entities/booking-container-unit.entity';
import { Contract } from '../contracts/entities/contract.entity';
import { BookingBatchOffer } from './entities/booking-batch-offer.entity';

/**
 * applySplit split-marking behaviour: the reduced booking is flagged is_split
 * and keeps a pre_split_quantities snapshot (the remainder ledger for ONE_TIME
 * contracts). The contract kind is NEVER changed — a ONE_TIME contract stays
 * ONE_TIME through the split chain.
 */
describe('BookingSplitService — applySplit split marking', () => {
  const bookingId = 'bk-1';
  const contractId = 'ct-1';
  const offerId = 'of-1';

  const buildService = (bookingContractKind: 'ONE_TIME' | 'GENERAL') => {
    const offer = {
      id: offerId,
      bookingId,
      status: 'OFFERED',
      offeredWagons: 3,
      totalWagons: 5,
      offeredWeightTons: 30,
      offeredAmount: 300,
      offeredPricingBreakdown: {},
      offeredLines: null,
    } as unknown as BookingBatchOffer;

    const bookingRepo = {
      update: jest.fn().mockResolvedValue(undefined),
      findOne: jest.fn().mockResolvedValue({
        id: bookingId,
        contractId,
        contractKind: bookingContractKind,
        cargoTotalWeightVgm: 50,
      }),
      find: jest.fn().mockResolvedValue([]),
      softDelete: jest.fn().mockResolvedValue(undefined),
    };
    const contractRepo = { update: jest.fn().mockResolvedValue(undefined) };
    const offerRepo = {
      findOne: jest.fn().mockResolvedValue(offer),
      update: jest.fn().mockResolvedValue(undefined),
    };
    const containerRepo = {
      find: jest.fn().mockResolvedValue([]),
      update: jest.fn(),
      softDelete: jest.fn(),
    };
    const unitRepo = { find: jest.fn().mockResolvedValue([]), softDelete: jest.fn() };

    const repoFor = (entity: unknown) => {
      if (entity === Booking) return bookingRepo;
      if (entity === Contract) return contractRepo;
      if (entity === BookingBatchOffer) return offerRepo;
      if (entity === BookingContainer) return containerRepo;
      if (entity === BookingContainerUnit) return unitRepo;
      return { find: jest.fn().mockResolvedValue([]), update: jest.fn() };
    };

    const dataSource = {
      getRepository: jest.fn(repoFor),
      transaction: jest.fn(async (fn: (m: unknown) => Promise<void>) => {
        await fn({ getRepository: repoFor });
      }),
    };

    const service = new BookingSplitService(
      dataSource as never,
      {} as never,
      {} as never,
      { expirePayable: jest.fn() } as never,
      { payNowPartial: jest.fn() } as never,
    );
    return { service, bookingRepo, contractRepo };
  };

  it('flags the reduced booking is_split with a pre-split bulk snapshot', async () => {
    const { service, bookingRepo } = buildService('ONE_TIME');

    await service.applySplit(bookingId);

    expect(bookingRepo.update).toHaveBeenCalledWith(
      bookingId,
      expect.objectContaining({
        isSplit: true,
        preSplitQuantities: { bulkTons: 50 },
        cargoTotalWeightVgm: 30,
        wagonsRequired: 3,
      }),
    );
  });

  it('never changes the contract kind — ONE_TIME stays ONE_TIME', async () => {
    const { service, bookingRepo, contractRepo } = buildService('ONE_TIME');

    await service.applySplit(bookingId);

    expect(contractRepo.update).not.toHaveBeenCalled();
    expect(bookingRepo.update).not.toHaveBeenCalledWith(
      bookingId,
      expect.objectContaining({ contractKind: expect.anything() }),
    );
  });

  it('leaves a GENERAL contract untouched too', async () => {
    const { service, contractRepo } = buildService('GENERAL');

    await service.applySplit(bookingId);

    expect(contractRepo.update).not.toHaveBeenCalled();
  });
});
