import { BookingLifecycleNotifierService } from './booking-lifecycle-notifier.service';
import type { Booking } from './entities/booking.entity';

/**
 * Who hears "Operations wants changes" depends on who owns the booking. A
 * customs (Path B) booking is created BY GL Ethiopia on the customer's behalf —
 * the customer can neither edit nor resubmit it, so the note has to reach the GL
 * who made it, not the portal.
 */
describe('BookingLifecycleNotifierService — operation changes requested', () => {
  const booking = (over: Partial<Booking> = {}): Booking =>
    ({
      id: 'b-1',
      reference: 'BKG-0001',
      companyId: 'co-1',
      contractId: 'ctr-1',
      createdByRole: 'CUSTOMER',
      company: { email: 'customer@example.com' },
      ...over,
    }) as Booking;

  let notifications: { directSend: jest.Mock };
  let inbox: { notify: jest.Mock };
  let service: BookingLifecycleNotifierService;

  const flush = () => new Promise((resolve) => setImmediate(resolve));

  beforeEach(() => {
    notifications = { directSend: jest.fn().mockResolvedValue(undefined) };
    inbox = { notify: jest.fn().mockResolvedValue(undefined) };
    service = new BookingLifecycleNotifierService(
      notifications as never,
      inbox as never,
      { query: jest.fn().mockResolvedValue([{ phone: '+251900000000' }]) } as never,
    );
  });

  it('sends a GL-created booking back to the GL who created it, not the customer', async () => {
    service.operationChangesRequested(
      booking({ createdByRole: 'GL_ET', createdByUserId: 'gl-user-1' }),
      'Cargo weight does not match the declaration',
    );
    await flush();

    expect(inbox.notify).toHaveBeenCalledTimes(1);
    const sent = inbox.notify.mock.calls[0][0];
    expect(sent.recipients).toEqual({ userIds: ['gl-user-1'] });
    expect(sent.audience).toBe('BACKOFFICE');
    expect(sent.body).toContain('Cargo weight does not match the declaration');
    // Deep-links the clearance page GL works from, not the portal booking.
    expect(sent.link).toBe('/dashboard/contracts/clearance/ctr-1');
    // The customer is not told to fix something they cannot touch.
    expect(notifications.directSend).not.toHaveBeenCalled();
  });

  it('still tells the customer when the booking is their own', async () => {
    service.operationChangesRequested(booking(), 'Please attach the packing list');
    await flush();

    const sent = inbox.notify.mock.calls[0][0];
    expect(sent.recipients).toEqual({ companyId: 'co-1' });
    expect(sent.audience).toBe('PORTAL');
    expect(sent.link).toBe('/bookings/b-1');
    expect(notifications.directSend).toHaveBeenCalled();
  });

  it('falls back to the customer when the GL creator is unknown (legacy rows)', async () => {
    service.operationChangesRequested(
      booking({ createdByRole: 'GL_ET', createdByUserId: null }),
      'Fix the declaration',
    );
    await flush();

    expect(inbox.notify.mock.calls[0][0].recipients).toEqual({ companyId: 'co-1' });
  });
});
