import { BadRequestException } from '@nestjs/common';
import { BookingTransitionService } from './booking-transition.service';

/**
 * Focused tests for the contract validity window set at the accept step.
 * The backoffice must supply a number of days; the window runs from the accept
 * moment through accept + N days.
 */
describe('BookingTransitionService — acceptIntake validity window', () => {
  const booking = {
    id: 'b-1',
    status: 'SUBMITTED',
    freightType: 'CONTAINER',
    cargoTypeId: null,
  };

  function makeService() {
    const bookingsRepository = {
      update: jest.fn().mockResolvedValue({ id: 'b-1' }),
    };
    const bookingsService = {
      findById: jest.fn().mockResolvedValue(booking),
    };
    const ruleEngineService = {
      assertNoHardBlocks: jest.fn(),
    };
    const contractService = {
      generateContract: jest.fn().mockResolvedValue({ id: 'b-1' }),
    };

    const service = new BookingTransitionService(
      bookingsRepository as never,
      ruleEngineService as never,
      {} as never, // pricingService
      contractService as never,
      {} as never, // filesService
      {} as never, // fileUploadSettingsService
      {} as never, // bookingBatchService
      bookingsService as never,
      { isPhasedGeneralCustomsBooking: () => false } as never,
      {} as never, // workflowService
      {} as never, // invoiceService
      { validate20ftPairing: jest.fn().mockResolvedValue([]) } as never,
      {
        accepted: jest.fn(),
        approved: jest.fn(),
        rejected: jest.fn(),
        changesRequested: jest.fn(),
        documentQueried: jest.fn(),
        clearanceReady: jest.fn(),
        operationChangesRequested: jest.fn(),
        operationAccepted: jest.fn(),
        inTransit: jest.fn(),
        completed: jest.fn(),
        cancelled: jest.fn(),
        submittedToStaff: jest.fn(),
        customerSignedToStaff: jest.fn(),
        operationRequestedToStaff: jest.fn(),
        clearanceDocsUploadedToStaff: jest.fn(),
        dutySlipUploadedToStaff: jest.fn(),
      } as never, // notifier
    );
    return { service, bookingsRepository, ruleEngineService, contractService };
  }

  it('rejects accept when validity days is missing or non-positive', async () => {
    const { service } = makeService();
    await expect(
      service.acceptIntake('b-1', 'staff-1', 0),
    ).rejects.toBeInstanceOf(BadRequestException);
    await expect(
      service.acceptIntake('b-1', 'staff-1', -5),
    ).rejects.toBeInstanceOf(BadRequestException);
    await expect(
      service.acceptIntake('b-1', 'staff-1', 1.5),
    ).rejects.toBeInstanceOf(BadRequestException);
  });

  it('sets a validity window of validFrom..validFrom + N days', async () => {
    const { service, bookingsRepository } = makeService();
    await service.acceptIntake('b-1', 'staff-1', 10);

    expect(bookingsRepository.update).toHaveBeenCalledTimes(1);
    const [id, updates] = bookingsRepository.update.mock.calls[0];
    expect(id).toBe('b-1');
    expect(updates).toMatchObject({
      status: 'APPROVED',
      approvedByStaffId: 'staff-1',
      contractValidityDays: 10,
    });

    const from = updates.contractValidFrom as Date;
    const until = updates.contractValidUntil as Date;
    const diffDays = Math.round(
      (until.getTime() - from.getTime()) / (1000 * 60 * 60 * 24),
    );
    expect(diffDays).toBe(10);
    // The accept timestamp and the validity start are the same moment.
    expect((updates.approvedByStaffAt as Date).getTime()).toBe(from.getTime());
  });

  it('approves outright and generates the contract (no approval chain)', async () => {
    const { service, contractService } = makeService();
    await service.acceptIntake('b-1', 'staff-1', 30);
    expect(contractService.generateContract).toHaveBeenCalledWith('b-1');
  });
});
