import { TestBed } from '@angular/core/testing';
import { HandleCommandFacade } from './handle-command.facade';
import { HandleCommandService } from '../services';
import {
  HandleCommand,
  FetchReceiptsByOrderItemSubsetIdsInput,
} from '../schemas';
import { ReceiptType } from '../models/receipt-type';

// Mock the HandleCommandService module
jest.mock('../services', () => ({
  HandleCommandService: jest.fn().mockImplementation(() => ({
    handle: jest.fn(),
    fetchReceiptsByOrderItemSubsetIds: jest.fn(),
  })),
}));

describe('HandleCommandFacade', () => {
  let facade: HandleCommandFacade;
  let mockHandleCommandService: {
    handle: jest.Mock;
    fetchReceiptsByOrderItemSubsetIds: jest.Mock;
  };

  beforeEach(() => {
    mockHandleCommandService = {
      handle: jest.fn(),
      fetchReceiptsByOrderItemSubsetIds: jest.fn(),
    };

    TestBed.configureTestingModule({
      providers: [
        HandleCommandFacade,
        {
          provide: HandleCommandService,
          useValue: mockHandleCommandService,
        },
      ],
    });

    facade = TestBed.inject(HandleCommandFacade);
  });

  it('should create', () => {
    // Assert
    expect(facade).toBeTruthy();
  });

  it('should delegate handle call to service', async () => {
    // Arrange
    const mockParams: HandleCommand = {
      action: { key: 'test-action', value: 'Test Action' },
      items: [],
    };
    const mockResult: HandleCommand = {
      action: { key: 'test-action', value: 'Test Action' },
      items: [],
    };

    mockHandleCommandService.handle.mockResolvedValue(mockResult);

    // Act
    const result = await facade.handle(mockParams);

    // Assert
    expect(mockHandleCommandService.handle).toHaveBeenCalledWith(mockParams);
    expect(mockHandleCommandService.handle).toHaveBeenCalledTimes(1);
    expect(result).toEqual(mockResult);
  });

  it('should pass through all parameters to service', async () => {
    // Arrange
    const mockParams: HandleCommand = {
      action: {
        key: 'complex-action',
        value: 'Complex Action',
        command: 'DO_SOMETHING',
      },
      items: [
        {
          orderItemId: 123,
          orderItemSubsetId: 456,
        },
      ] as any,
      compartmentCode: 'ABC123',
      compartmentInfo: 'Extra Info',
      itemQuantity: { 456: 2 },
      order: {
        id: 100,
        orderNumber: 'ORD-001',
        orderType: 1,
      } as any,
    };

    mockHandleCommandService.handle.mockResolvedValue(mockParams);

    // Act
    await facade.handle(mockParams);

    // Assert
    expect(mockHandleCommandService.handle).toHaveBeenCalledWith(mockParams);
  });

  it('should propagate errors from service', async () => {
    // Arrange
    const mockParams: HandleCommand = {
      action: { key: 'error-action', value: 'Error Action' },
      items: [],
    };
    const mockError = new Error('Service error');

    mockHandleCommandService.handle.mockRejectedValue(mockError);

    // Act & Assert
    await expect(facade.handle(mockParams)).rejects.toThrow('Service error');
  });

  describe('fetchReceiptsByOrderItemSubsetIds', () => {
    it('should delegate call to service', async () => {
      // Arrange
      const mockParams: FetchReceiptsByOrderItemSubsetIdsInput = {
        ids: [123, 456, 789],
      };
      const mockResult = {
        receipts: [{ id: 1 }, { id: 2 }],
      };

      mockHandleCommandService.fetchReceiptsByOrderItemSubsetIds.mockResolvedValue(
        mockResult,
      );

      // Act
      const result = await facade.fetchReceiptsByOrderItemSubsetIds(mockParams);

      // Assert
      expect(
        mockHandleCommandService.fetchReceiptsByOrderItemSubsetIds,
      ).toHaveBeenCalledWith(mockParams, undefined);
      expect(
        mockHandleCommandService.fetchReceiptsByOrderItemSubsetIds,
      ).toHaveBeenCalledTimes(1);
      expect(result).toEqual(mockResult);
    });

    it('should pass all parameters with defaults to service', async () => {
      // Arrange
      const mockParams: FetchReceiptsByOrderItemSubsetIdsInput = {
        eagerLoading: 1,
        receiptType: (1 + 64 + 128) as ReceiptType,
        ids: [100, 200],
      };

      mockHandleCommandService.fetchReceiptsByOrderItemSubsetIds.mockResolvedValue(
        {},
      );

      // Act
      await facade.fetchReceiptsByOrderItemSubsetIds(mockParams);

      // Assert
      expect(
        mockHandleCommandService.fetchReceiptsByOrderItemSubsetIds,
      ).toHaveBeenCalledWith(mockParams, undefined);
    });

    it('should propagate errors from service', async () => {
      // Arrange
      const mockParams: FetchReceiptsByOrderItemSubsetIdsInput = {
        ids: [111],
      };
      const mockError = new Error('Fetch receipts error');

      mockHandleCommandService.fetchReceiptsByOrderItemSubsetIds.mockRejectedValue(
        mockError,
      );

      // Act & Assert
      await expect(
        facade.fetchReceiptsByOrderItemSubsetIds(mockParams),
      ).rejects.toThrow('Fetch receipts error');
    });

    it('should pass abort signal to service when provided', async () => {
      // Arrange
      const mockParams: FetchReceiptsByOrderItemSubsetIdsInput = {
        ids: [123],
      };
      const abortController = new AbortController();
      const mockResult = {
        receipts: [{ id: 1 }],
      };

      mockHandleCommandService.fetchReceiptsByOrderItemSubsetIds.mockResolvedValue(
        mockResult,
      );

      // Act
      const result = await facade.fetchReceiptsByOrderItemSubsetIds(
        mockParams,
        abortController.signal,
      );

      // Assert
      expect(
        mockHandleCommandService.fetchReceiptsByOrderItemSubsetIds,
      ).toHaveBeenCalledWith(mockParams, abortController.signal);
      expect(result).toEqual(mockResult);
    });
  });
});
