import { createServiceFactory, SpectatorService } from '@ngneat/spectator/jest';
import { OrderCreationFacade } from './order-creation.facade';
import { OrderCreationService } from '../services';
import { LogisticianDTO } from '@generated/swagger/oms-api';
import { Order } from '../models';

describe('OrderCreationFacade', () => {
  let spectator: SpectatorService<OrderCreationFacade>;
  const createService = createServiceFactory({
    service: OrderCreationFacade,
    mocks: [OrderCreationService],
  });

  beforeEach(() => {
    spectator = createService();
  });

  describe('createOrdersFromCheckout', () => {
    it('should delegate to OrderCreationService.createOrdersFromCheckout', async () => {
      // Arrange
      const checkoutId = 123;
      const mockOrders: Order[] = [
        { id: 1 } as Order,
        { id: 2 } as Order,
      ];
      const orderCreationService = spectator.inject(OrderCreationService);
      (orderCreationService.createOrdersFromCheckout as jest.Mock).mockResolvedValue(mockOrders);

      // Act
      const result = await spectator.service.createOrdersFromCheckout(checkoutId);

      // Assert
      expect(result).toEqual(mockOrders);
      expect(orderCreationService.createOrdersFromCheckout).toHaveBeenCalledWith(
        checkoutId,
      );
      expect(orderCreationService.createOrdersFromCheckout).toHaveBeenCalledTimes(1);
    });

    it('should propagate errors from OrderCreationService', async () => {
      // Arrange
      const checkoutId = 0;
      const errorMessage = 'Invalid checkoutId: 0';
      const orderCreationService = spectator.inject(OrderCreationService);
      (orderCreationService.createOrdersFromCheckout as jest.Mock).mockRejectedValue(
        new Error(errorMessage)
      );

      // Act & Assert
      await expect(
        spectator.service.createOrdersFromCheckout(checkoutId),
      ).rejects.toThrow(errorMessage);
      expect(orderCreationService.createOrdersFromCheckout).toHaveBeenCalledWith(
        checkoutId,
      );
    });
  });

  describe('getLogistician', () => {
    it('should delegate to OrderCreationService.getLogistician with default logisticianNumber', async () => {
      // Arrange
      const mockLogistician: LogisticianDTO = {
        logisticianNumber: '2470',
        name: 'Default Logistician',
      } as LogisticianDTO;
      const orderCreationService = spectator.inject(OrderCreationService);
      (orderCreationService.getLogistician as jest.Mock).mockResolvedValue(mockLogistician);

      // Act
      const result = await spectator.service.getLogistician();

      // Assert
      expect(result).toEqual(mockLogistician);
      expect(orderCreationService.getLogistician).toHaveBeenCalledWith(
        '2470',
        undefined,
      );
      expect(orderCreationService.getLogistician).toHaveBeenCalledTimes(1);
    });

    it('should delegate to OrderCreationService.getLogistician with custom logisticianNumber', async () => {
      // Arrange
      const logisticianNumber = '1000';
      const mockLogistician: LogisticianDTO = {
        logisticianNumber,
        name: 'Custom Logistician',
      } as LogisticianDTO;
      const orderCreationService = spectator.inject(OrderCreationService);
      (orderCreationService.getLogistician as jest.Mock).mockResolvedValue(mockLogistician);

      // Act
      const result = await spectator.service.getLogistician(logisticianNumber);

      // Assert
      expect(result).toEqual(mockLogistician);
      expect(orderCreationService.getLogistician).toHaveBeenCalledWith(
        logisticianNumber,
        undefined,
      );
    });

    it('should delegate to OrderCreationService.getLogistician with abortSignal', async () => {
      // Arrange
      const logisticianNumber = '2470';
      const abortSignal = new AbortController().signal;
      const mockLogistician: LogisticianDTO = {
        logisticianNumber,
        name: 'Default Logistician',
      } as LogisticianDTO;
      const orderCreationService = spectator.inject(OrderCreationService);
      (orderCreationService.getLogistician as jest.Mock).mockResolvedValue(mockLogistician);

      // Act
      const result = await spectator.service.getLogistician(
        logisticianNumber,
        abortSignal,
      );

      // Assert
      expect(result).toEqual(mockLogistician);
      expect(orderCreationService.getLogistician).toHaveBeenCalledWith(
        logisticianNumber,
        abortSignal,
      );
    });

    it('should propagate errors from OrderCreationService', async () => {
      // Arrange
      const errorMessage = 'Logistician 2470 not found';
      const orderCreationService = spectator.inject(OrderCreationService);
      (orderCreationService.getLogistician as jest.Mock).mockRejectedValue(
        new Error(errorMessage)
      );

      // Act & Assert
      await expect(spectator.service.getLogistician()).rejects.toThrow(
        errorMessage,
      );
      expect(orderCreationService.getLogistician).toHaveBeenCalledWith(
        '2470',
        undefined,
      );
    });
  });
});
