import { TestBed } from '@angular/core/testing';
import { of } from 'rxjs';
import { HandleCommandService } from './handle-command.service';
import { ReceiptService } from '@generated/swagger/oms-api';
import { provideLogging, LogLevel } from '@isa/core/logging';
import { FetchReceiptsByOrderItemSubsetIdsInput } from '../schemas';
import { ReceiptType } from '../models';

// Create simple mock object
const mockCommandService = {
  handleCommand: jest.fn(),
};

// Mock the module
jest.mock('@core/command', () => ({
  CommandService: jest.fn().mockImplementation(() => mockCommandService),
}));

// Import after mock
import { CommandService } from '@core/command';

describe('HandleCommandService', () => {
  let service: HandleCommandService;
  let mockReceiptService: any;

  beforeEach(() => {
    mockReceiptService = {
      ReceiptGetReceiptsByOrderItemSubset: jest.fn(),
    };

    // Reset mock before each test
    mockCommandService.handleCommand.mockReset();

    TestBed.configureTestingModule({
      providers: [
        HandleCommandService,
        {
          provide: CommandService,
          useValue: mockCommandService,
        },
        {
          provide: ReceiptService,
          useValue: mockReceiptService,
        },
        provideLogging({ level: LogLevel.Off }),
      ],
    });

    service = TestBed.inject(HandleCommandService);
  });

  describe('handle', () => {
    it('should handle PRINT_SHIPPINGNOTE command and fetch receipts automatically', async () => {
      // Arrange
      const mockParams = {
        action: { key: 'test', command: 'PRINT_SHIPPINGNOTE' },
        items: [{ orderItemSubsetId: 123 }, { orderItemSubsetId: 456 }],
      };

      mockReceiptService.ReceiptGetReceiptsByOrderItemSubset.mockReturnValue(
        of({
          result: [
            { item3: { data: { receiptNumber: 'R001' } } },
            { item3: { data: { receiptNumber: 'R002' } } },
          ],
          error: null,
        }),
      );

      mockCommandService.handleCommand.mockResolvedValue({});

      // Act
      await service.handle(mockParams as any);

      // Assert
      expect(
        mockReceiptService.ReceiptGetReceiptsByOrderItemSubset,
      ).toHaveBeenCalledWith({
        payload: expect.objectContaining({
          ids: [123, 456],
        }),
      });

      expect(mockCommandService.handleCommand).toHaveBeenCalledWith(
        'PRINT_SHIPPINGNOTE',
        expect.objectContaining({
          receipts: expect.arrayContaining([
            expect.objectContaining({ receiptNumber: 'R001' }),
            expect.objectContaining({ receiptNumber: 'R002' }),
          ]),
        }),
      );
    });

    it('should use provided receipts if already available', async () => {
      // Arrange
      const providedReceipts = [{ receiptNumber: 'R999' }];
      const mockParams = {
        action: { key: 'test', command: 'PRINT_SHIPPINGNOTE' },
        items: [{ orderItemSubsetId: 123 }],
        receipts: providedReceipts,
      };

      mockCommandService.handleCommand.mockResolvedValue({});

      // Act
      await service.handle(mockParams as any);

      // Assert
      // Should NOT call the receipt service
      expect(
        mockReceiptService.ReceiptGetReceiptsByOrderItemSubset,
      ).not.toHaveBeenCalled();

      expect(mockCommandService.handleCommand).toHaveBeenCalledWith(
        'PRINT_SHIPPINGNOTE',
        expect.objectContaining({
          receipts: providedReceipts,
        }),
      );
    });

    it('should handle PRINT_SMALLAMOUNTINVOICE command and fetch receipts', async () => {
      // Arrange
      const mockParams = {
        action: { key: 'test', command: 'PRINT_SMALLAMOUNTINVOICE' },
        items: [{ orderItemSubsetId: 789 }],
      };

      mockReceiptService.ReceiptGetReceiptsByOrderItemSubset.mockReturnValue(
        of({
          result: [{ item3: { data: { receiptNumber: 'R003' } } }],
          error: null,
        }),
      );

      mockCommandService.handleCommand.mockResolvedValue({});

      // Act
      await service.handle(mockParams as any);

      // Assert
      expect(
        mockReceiptService.ReceiptGetReceiptsByOrderItemSubset,
      ).toHaveBeenCalled();

      expect(mockCommandService.handleCommand).toHaveBeenCalledWith(
        'PRINT_SMALLAMOUNTINVOICE',
        expect.objectContaining({
          receipts: expect.any(Array),
        }),
      );
    });

    it('should omit receipts for other commands', async () => {
      // Arrange
      const mockParams = {
        action: { key: 'test', command: 'SOME_OTHER_COMMAND' },
        items: [{ orderItemSubsetId: 123 }],
        receipts: [{ receiptNumber: 'R001' }],
      };

      mockCommandService.handleCommand.mockResolvedValue({});

      // Act
      await service.handle(mockParams as any);

      // Assert
      expect(
        mockReceiptService.ReceiptGetReceiptsByOrderItemSubset,
      ).not.toHaveBeenCalled();

      expect(mockCommandService.handleCommand).toHaveBeenCalledWith(
        'SOME_OTHER_COMMAND',
        expect.not.objectContaining({
          receipts: expect.anything(),
        }),
      );
    });

    it('should remove PRINT_COMPARTMENTLABEL when compartmentCode is present', async () => {
      // Arrange
      const mockParams = {
        action: { key: 'test', command: 'SOME_COMMAND|PRINT_COMPARTMENTLABEL' },
        items: [{ orderItemSubsetId: 123 }],
        compartmentCode: 'ABC123',
      };

      mockCommandService.handleCommand.mockResolvedValue({});

      // Act
      await service.handle(mockParams as any);

      // Assert
      expect(mockCommandService.handleCommand).toHaveBeenCalledWith(
        'SOME_COMMAND',
        expect.anything(),
      );
    });

    it('should handle empty items array gracefully', async () => {
      // Arrange
      const mockParams = {
        action: { key: 'test', command: 'PRINT_SHIPPINGNOTE' },
        items: [],
      };

      mockCommandService.handleCommand.mockResolvedValue({});

      // Act
      await service.handle(mockParams as any);

      // Assert
      expect(
        mockReceiptService.ReceiptGetReceiptsByOrderItemSubset,
      ).not.toHaveBeenCalled();

      expect(mockCommandService.handleCommand).toHaveBeenCalledWith(
        'PRINT_SHIPPINGNOTE',
        expect.objectContaining({
          receipts: [],
        }),
      );
    });
  });

  describe('fetchReceiptsByOrderItemSubsetIds', () => {
    it('should fetch receipts successfully with minimal params', async () => {
      // Arrange
      const mockParams: FetchReceiptsByOrderItemSubsetIdsInput = {
        ids: [123, 456, 789],
      };

      const mockReceipts = [
        { id: 1, data: { receiptNumber: 'R001' } },
        { id: 2, data: { receiptNumber: 'R002' } },
      ];

      mockReceiptService.ReceiptGetReceiptsByOrderItemSubset.mockReturnValue(
        of({
          result: [
            { item3: { data: mockReceipts[0].data } },
            { item3: { data: mockReceipts[1].data } },
          ],
          error: null,
        }),
      );

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

      // Assert
      expect(
        mockReceiptService.ReceiptGetReceiptsByOrderItemSubset,
      ).toHaveBeenCalledWith({
        payload: expect.objectContaining({
          ids: [123, 456, 789],
        }),
      });
      expect(result).toHaveLength(2);
      expect(result[0]).toEqual(mockReceipts[0].data);
      expect(result[1]).toEqual(mockReceipts[1].data);
    });

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

      mockReceiptService.ReceiptGetReceiptsByOrderItemSubset.mockReturnValue(
        of({
          result: [{ item3: { data: { receiptNumber: 'R003' } } }],
          error: null,
        }),
      );

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

      // Assert
      expect(
        mockReceiptService.ReceiptGetReceiptsByOrderItemSubset,
      ).toHaveBeenCalledWith({
        payload: expect.objectContaining({
          eagerLoading: 1,
          receiptType: 193, // 1 + 64 + 128
          ids: [100, 200],
        }),
      });
      expect(result).toHaveLength(1);
    });

    it('should filter out null/undefined receipts from result', async () => {
      // Arrange
      const mockParams: FetchReceiptsByOrderItemSubsetIdsInput = {
        ids: [111, 222],
      };

      mockReceiptService.ReceiptGetReceiptsByOrderItemSubset.mockReturnValue(
        of({
          result: [
            { item3: { data: { receiptNumber: 'R004' } } },
            { item3: null },
            { item3: { data: null } },
            { item3: { data: { receiptNumber: 'R005' } } },
          ],
          error: null,
        }),
      );

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

      // Assert
      expect(result).toHaveLength(2);
      expect(result[0].receiptNumber).toBe('R004');
      expect(result[1].receiptNumber).toBe('R005');
    });

    it('should return empty array when no receipts found', async () => {
      // Arrange
      const mockParams: FetchReceiptsByOrderItemSubsetIdsInput = {
        ids: [999],
      };

      mockReceiptService.ReceiptGetReceiptsByOrderItemSubset.mockReturnValue(
        of({
          result: [],
          error: null,
        }),
      );

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

      // Assert
      expect(result).toEqual([]);
    });

    it('should handle abort signal when provided', async () => {
      // Arrange
      const mockParams: FetchReceiptsByOrderItemSubsetIdsInput = {
        ids: [123],
      };
      const abortController = new AbortController();

      mockReceiptService.ReceiptGetReceiptsByOrderItemSubset.mockReturnValue(
        of({
          result: [{ item3: { data: { receiptNumber: 'R006' } } }],
          error: null,
        }),
      );

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

      // Assert
      expect(result).toHaveLength(1);
      expect(
        mockReceiptService.ReceiptGetReceiptsByOrderItemSubset,
      ).toHaveBeenCalled();
    });
  });
});
