import { TestBed } from '@angular/core/testing';
import { describe, it, expect, beforeEach, vi } from 'vitest';
import { CheckoutPrintService } from './checkout-print.service';
import { OMSPrintService, ResponseArgs } from '@generated/swagger/print-api';
import { of, throwError } from 'rxjs';
import { ResponseArgsError } from '@isa/common/data-access';

describe('CheckoutPrintService', () => {
  let service: CheckoutPrintService;
  let mockOMSPrintService: {
    OMSPrintAbholscheinById: ReturnType<typeof vi.fn>;
  };

  beforeEach(() => {
    // Arrange: Create mock
    mockOMSPrintService = {
      OMSPrintAbholscheinById: vi.fn(),
    };

    TestBed.configureTestingModule({
      providers: [
        CheckoutPrintService,
        { provide: OMSPrintService, useValue: mockOMSPrintService },
      ],
    });

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

  it('should be created', () => {
    expect(service).toBeTruthy();
  });

  it('should successfully print order confirmation', async () => {
    // Arrange
    const params = {
      printer: 'printer-1',
      data: [123, 456, 789],
    };
    const mockResponse: ResponseArgs = {
      error: false,
    };
    mockOMSPrintService.OMSPrintAbholscheinById.mockReturnValue(
      of(mockResponse),
    );

    // Act
    const result = await service.printOrderConfirmation(params);

    // Assert
    expect(mockOMSPrintService.OMSPrintAbholscheinById).toHaveBeenCalledWith(
      params,
    );
    expect(result).toEqual(mockResponse);
    expect(result.error).toBe(false);
  });

  it('should print with single order ID', async () => {
    // Arrange
    const params = {
      printer: 'label-printer',
      data: [100],
    };
    const mockResponse: ResponseArgs = {
      error: false,
    };
    mockOMSPrintService.OMSPrintAbholscheinById.mockReturnValue(
      of(mockResponse),
    );

    // Act
    const result = await service.printOrderConfirmation(params);

    // Assert
    expect(mockOMSPrintService.OMSPrintAbholscheinById).toHaveBeenCalledWith(
      params,
    );
    expect(result.error).toBe(false);
  });

  it('should print with empty order list', async () => {
    // Arrange
    const params = {
      printer: 'printer-2',
      data: [],
    };
    const mockResponse: ResponseArgs = {
      error: false,
    };
    mockOMSPrintService.OMSPrintAbholscheinById.mockReturnValue(
      of(mockResponse),
    );

    // Act
    const result = await service.printOrderConfirmation(params);

    // Assert
    expect(mockOMSPrintService.OMSPrintAbholscheinById).toHaveBeenCalledWith(
      params,
    );
    expect(result).toEqual(mockResponse);
  });

  it('should validate params using PrintOrderConfirmationSchema', async () => {
    // Arrange
    const params = {
      printer: 'test-printer',
      data: [1, 2, 3],
    };
    const mockResponse: ResponseArgs = {
      error: false,
    };
    mockOMSPrintService.OMSPrintAbholscheinById.mockReturnValue(
      of(mockResponse),
    );

    // Act
    await service.printOrderConfirmation(params);

    // Assert - Schema validation happens implicitly, if invalid would throw
    expect(mockOMSPrintService.OMSPrintAbholscheinById).toHaveBeenCalledWith(
      expect.objectContaining({
        printer: expect.any(String),
        data: expect.any(Array),
      }),
    );
  });

  it('should throw ResponseArgsError when response has error', async () => {
    // Arrange
    const params = {
      printer: 'printer-1',
      data: [123],
    };
    const errorResponse: ResponseArgs = {
      error: true,
      message: 'Printer not available',
    };
    mockOMSPrintService.OMSPrintAbholscheinById.mockReturnValue(
      of(errorResponse),
    );

    // Act & Assert
    await expect(service.printOrderConfirmation(params)).rejects.toThrow(
      ResponseArgsError,
    );
    expect(mockOMSPrintService.OMSPrintAbholscheinById).toHaveBeenCalledWith(
      params,
    );
  });

  it('should handle different printer keys', async () => {
    // Arrange
    const params1 = { printer: 'label-printer-1', data: [1] };
    const params2 = { printer: 'document-printer-2', data: [2] };
    const mockResponse: ResponseArgs = { error: false };
    mockOMSPrintService.OMSPrintAbholscheinById.mockReturnValue(
      of(mockResponse),
    );

    // Act
    await service.printOrderConfirmation(params1);
    await service.printOrderConfirmation(params2);

    // Assert
    expect(mockOMSPrintService.OMSPrintAbholscheinById).toHaveBeenCalledTimes(
      2,
    );
    expect(mockOMSPrintService.OMSPrintAbholscheinById).toHaveBeenNthCalledWith(
      1,
      params1,
    );
    expect(mockOMSPrintService.OMSPrintAbholscheinById).toHaveBeenNthCalledWith(
      2,
      params2,
    );
  });

  it('should handle multiple order IDs', async () => {
    // Arrange
    const params = {
      printer: 'main-printer',
      data: [100, 200, 300, 400, 500],
    };
    const mockResponse: ResponseArgs = {
      error: false,
    };
    mockOMSPrintService.OMSPrintAbholscheinById.mockReturnValue(
      of(mockResponse),
    );

    // Act
    const result = await service.printOrderConfirmation(params);

    // Assert
    expect(result.error).toBe(false);
    expect(mockOMSPrintService.OMSPrintAbholscheinById).toHaveBeenCalledWith(
      expect.objectContaining({
        data: expect.arrayContaining([100, 200, 300, 400, 500]),
      }),
    );
  });
});
