import { BadRequestException } from '@nestjs/common';
import type { DataSource } from 'typeorm';

import { LastMileService } from './last-mile.service';

/**
 * A booking reaches the last-mile queue only if its contract bought EDR
 * delivery, and never if the customer is hauling it themselves. Creation used
 * to check payment alone, so any paid booking could be accepted — which put a
 * self-haul booking and an EDR leg on the same shipment at once.
 */
type BookingRow = { tradeDirection: string; firstMile: string | null; lastMile: string | null };

function makeService(opts: { booking?: BookingRow; hasCustomerTruck?: boolean }) {
  const booking = opts.booking ?? {
    tradeDirection: 'IMPORT',
    firstMile: null,
    lastMile: 'Bole, Addis Ababa',
  };

  const query = jest.fn((sql: string) => {
    if (sql.includes('customer_truck_assignments')) {
      return Promise.resolve(opts.hasCustomerTruck ? [{ '?column?': 1 }] : []);
    }
    if (sql.includes('FROM freight.bookings')) {
      return Promise.resolve([booking]);
    }
    return Promise.resolve([]);
  });

  const lastMileRepository = {
    findAll: jest.fn().mockResolvedValue([]),
    create: jest.fn((row: unknown) => Promise.resolve({ id: 'lm-1', ...(row as object) })),
  };

  const service = new LastMileService(
    lastMileRepository as never,
    {} as never, // bookingsRepository
    { setAvailability: jest.fn() } as never, // vehiclesService
    {} as never, // driversService
    {} as never, // smsClient
    { query } as unknown as DataSource,
    { record: jest.fn() } as never, // history
    {} as never, // billing
    {} as never, // filesService
  );

  return { service, lastMileRepository, query };
}

describe('LastMileService.create — haulage guard', () => {
  it('accepts a booking whose contract chose EDR delivery', async () => {
    const { service, lastMileRepository } = makeService({});

    await service.create({ bookingId: 'b-1', advancedPayment: 0 } as never);

    expect(lastMileRepository.create).toHaveBeenCalled();
  });

  it('rejects a booking that chose no road legs on its contract', async () => {
    const { service, lastMileRepository } = makeService({
      booking: { tradeDirection: 'IMPORT', firstMile: null, lastMile: null },
    });

    await expect(
      service.create({ bookingId: 'b-1', advancedPayment: 0 } as never),
    ).rejects.toBeInstanceOf(BadRequestException);
    expect(lastMileRepository.create).not.toHaveBeenCalled();
  });

  it('rejects a booking already hauled by the customer’s own truck', async () => {
    const { service, lastMileRepository } = makeService({ hasCustomerTruck: true });

    await expect(
      service.create({ bookingId: 'b-1', advancedPayment: 0 } as never),
    ).rejects.toBeInstanceOf(BadRequestException);
    expect(lastMileRepository.create).not.toHaveBeenCalled();
  });

  it('rejects an import that only chose collection — that is the export leg', async () => {
    const { service } = makeService({
      booking: { tradeDirection: 'IMPORT', firstMile: 'Modjo', lastMile: null },
    });

    await expect(
      service.create({ bookingId: 'b-1', advancedPayment: 0 } as never),
    ).rejects.toBeInstanceOf(BadRequestException);
  });

  it('returns the existing leg without re-checking, so the queue stays idempotent', async () => {
    const { service, lastMileRepository } = makeService({ hasCustomerTruck: true });
    lastMileRepository.findAll.mockResolvedValue([{ id: 'lm-existing' }]);

    const result = await service.create({ bookingId: 'b-1', advancedPayment: 0 } as never);

    expect(result).toEqual({ id: 'lm-existing' });
    expect(lastMileRepository.create).not.toHaveBeenCalled();
  });
});
