import { BookingOrdersService } from './booking-orders.service';

/**
 * Phase-0 spine: a drawdown order spawns a PRICED, UNPAID child booking that
 * waits for Marketing review (or the customs clearance gate first) — it does
 * NOT auto-enter the train batch pool, and the contract is not charged.
 */
describe('BookingOrdersService — child spawn on order create', () => {
  function makeService(opts: { includesCustoms: boolean; roadKm?: number | null }) {
    const contract = {
      id: 'c-1',
      bookingType: 'GENERAL_CONTRACT',
      status: 'CONTRACT_ACTIVE',
      expiresAt: new Date('2030-01-01T00:00:00.000Z'),
      freightType: 'BULK',
      originYardId: 'o-1',
      destinationYardId: 'd-1',
      companyId: null,
      paymentCurrency: 'ETB',
      serviceType: { includesCustoms: opts.includesCustoms, code: 'RAIL_BULK' },
      bookingContainers: [],
    };

    // Capture what status the child is created with.
    const created: Record<string, unknown>[] = [];
    const managerUpdates: Record<string, unknown>[] = [];
    const fakeManager = {
      create: (_entity: unknown, data: Record<string, unknown>) => {
        created.push(data);
        return { id: 'child-1', ...data };
      },
      save: async (row: Record<string, unknown>) => ({ id: 'child-1', ...row }),
      getRepository: () => ({
        findOne: async () => ({ id: 'child-1', paymentCurrency: 'ETB', bookingContainers: [] }),
        update: async (_id: string, data: Record<string, unknown>) => {
          managerUpdates.push(data);
        },
      }),
    };

    const dataSource = {
      transaction: async (cb: (m: unknown) => Promise<unknown>) => cb(fakeManager),
      getRepository: () => ({ update: jest.fn() }),
    };
    const ordersRepository = {
      countByYear: jest.fn().mockResolvedValue(0),
      findById: jest.fn().mockResolvedValue({ id: 'order-1', lines: [] }),
    };
    const bookingsRepository = {
      findById: jest.fn().mockResolvedValue(contract),
      countByYear: jest.fn().mockResolvedValue(0),
    };
    const generalContractService = {
      isGeneralContract: () => true,
      getRouteLines: jest.fn().mockResolvedValue([]),
      getQuantityLines: jest
        .fn()
        .mockResolvedValue([
          { containerTypeId: null, remainingQuantity: 100, containerTypeName: null },
        ]),
      isExhausted: jest.fn().mockResolvedValue(false),
    };
    const pricingService = {
      computePriceForBooking: jest.fn().mockResolvedValue({
        totalAmount: 500,
        priorityScore: 10,
        lineItems: [],
        currency: 'ETB',
      }),
    };
    const ratesService = { findLiveRates: jest.fn().mockResolvedValue([]) };
    const trainSchedulingService = {
      existsOpenScheduleOnRouteDay: jest.fn().mockResolvedValue(true),
    };
    const companiesService = {};

    const service = new BookingOrdersService(
      dataSource as never,
      ordersRepository as never,
      bookingsRepository as never,
      companiesService as never,
      generalContractService as never,
      pricingService as never,
      ratesService as never,
      trainSchedulingService as never,
    );
    return { service, created, managerUpdates, pricingService };
  }

  const dto = {
    contractBookingId: 'c-1',
    scheduledDate: '2026-07-01T00:00:00.000Z',
    lines: [{ quantity: 10, hazardousQuantity: 4, reeferQuantity: 0 }],
  };

  it('spawns the child at OPERATION_REQUEST_PENDING (no customs), priced + unpaid', async () => {
    const { service, created, managerUpdates, pricingService } = makeService({
      includesCustoms: false,
    });
    await service.create(dto as never);

    const child = created.find((c) => c.bookingType === 'ONE_TIME')!;
    expect(child.status).toBe('OPERATION_REQUEST_PENDING');
    expect(child.paymentStatus).toBe('PENDING');
    expect(child.isHazardous).toBe(true); // line has hazardousQuantity > 0
    expect(pricingService.computePriceForBooking).toHaveBeenCalled();
    // The computed price is persisted onto the child.
    expect(managerUpdates.some((u) => u.totalAmount === 500)).toBe(true);
  });

  it('spawns the child at AWAITING_DOCUMENTS when the service includes customs', async () => {
    const { service, created } = makeService({ includesCustoms: true });
    await service.create(dto as never);
    const child = created.find((c) => c.bookingType === 'ONE_TIME')!;
    expect(child.status).toBe('AWAITING_DOCUMENTS');
  });

  it('rejects when hazardous quantity exceeds the line quantity', async () => {
    const { service } = makeService({ includesCustoms: false });
    await expect(
      service.create({
        ...dto,
        lines: [{ quantity: 5, hazardousQuantity: 9, reeferQuantity: 0 }],
      } as never),
    ).rejects.toThrow(/exceed the line quantity/);
  });
});
