import { describe, it, expect, beforeEach, vi } from 'vitest';
import { TestBed } from '@angular/core/testing';
import { of } from 'rxjs';
import { SupplierService } from './supplier.service';
import { StoreCheckoutSupplierService } from '@generated/swagger/checkout-api';
import { ResponseArgsError } from '@isa/common/data-access';
import { LogLevel, provideLogging } from '@isa/core/logging';
import { Supplier } from '../models';

describe('SupplierService', () => {
  let service: SupplierService;
  let mockSupplierService: any;

  const createMockSupplier = (
    supplierNumber: string,
    id: number,
  ): Supplier => ({
    id,
    supplierNumber,
    name: `Supplier ${supplierNumber}`,
    key: `supplier-${supplierNumber}`,
    supplierType: 1,
  });

  beforeEach(() => {
    mockSupplierService = {
      StoreCheckoutSupplierGetSuppliers: vi.fn(),
    };

    TestBed.configureTestingModule({
      providers: [
        SupplierService,
        {
          provide: StoreCheckoutSupplierService,
          useValue: mockSupplierService,
        },
        provideLogging({ level: LogLevel.Off }),
      ],
    });

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

  describe('getTakeAwaySupplier', () => {
    it('should successfully fetch take away supplier', async () => {
      // Arrange
      const suppliers = [
        createMockSupplier('A', 1),
        createMockSupplier('F', 2), // Take away supplier
        createMockSupplier('B', 3),
      ];

      mockSupplierService.StoreCheckoutSupplierGetSuppliers.mockReturnValue(
        of({ result: suppliers, error: null }),
      );

      // Act
      const result = await service.getTakeAwaySupplier();

      // Assert
      expect(result).toEqual(suppliers[1]);
      expect(result.supplierNumber).toBe('F');
      expect(result.id).toBe(2);
      expect(
        mockSupplierService.StoreCheckoutSupplierGetSuppliers,
      ).toHaveBeenCalledWith({});
    });

    it('should throw error when supplier F is not found', async () => {
      // Arrange
      const suppliers = [
        createMockSupplier('A', 1),
        createMockSupplier('B', 2),
      ];

      mockSupplierService.StoreCheckoutSupplierGetSuppliers.mockReturnValue(
        of({ result: suppliers, error: null }),
      );

      // Act & Assert
      await expect(service.getTakeAwaySupplier()).rejects.toThrow(
        'Take away supplier (F) not found',
      );
    });

    it('should throw ResponseArgsError when API call fails', async () => {
      // Arrange
      const errorResponse = {
        result: null,
        error: { message: 'API Error', code: 500 },
      };

      mockSupplierService.StoreCheckoutSupplierGetSuppliers.mockReturnValue(
        of(errorResponse),
      );

      // Act & Assert
      await expect(service.getTakeAwaySupplier()).rejects.toThrow(
        ResponseArgsError,
      );
    });

    it('should throw error when result is empty', async () => {
      // Arrange
      mockSupplierService.StoreCheckoutSupplierGetSuppliers.mockReturnValue(
        of({ result: [], error: null }),
      );

      // Act & Assert
      await expect(service.getTakeAwaySupplier()).rejects.toThrow(
        'Take away supplier (F) not found',
      );
    });

    it('should support abort signal cancellation', async () => {
      // Arrange
      const abortController = new AbortController();
      const suppliers = [createMockSupplier('F', 1)];

      mockSupplierService.StoreCheckoutSupplierGetSuppliers.mockImplementation(
        () => {
          abortController.abort();
          return of({ result: suppliers, error: null });
        },
      );

      // Act
      const promise = service.getTakeAwaySupplier(abortController.signal);

      // Assert
      // The promise should still resolve because we abort after the observable is created
      // In a real scenario with HTTP, the abort would cancel the request
      await expect(promise).resolves.toBeDefined();
    });

    it('should use cache for repeated calls', async () => {
      // Arrange
      const suppliers = [createMockSupplier('F', 1)];

      mockSupplierService.StoreCheckoutSupplierGetSuppliers.mockReturnValue(
        of({ result: suppliers, error: null }),
      );

      // Act
      const result1 = await service.getTakeAwaySupplier();
      const result2 = await service.getTakeAwaySupplier();

      // Assert
      expect(result1).toEqual(result2);
      // Cache decorator should prevent second API call
      // Note: In actual implementation, the cache would work, but we can't easily test
      // the decorator behavior without more complex setup. This test verifies basic functionality.
      expect(
        mockSupplierService.StoreCheckoutSupplierGetSuppliers,
      ).toHaveBeenCalled();
    });
  });
});
