import { createServiceFactory, SpectatorService } from '@ngneat/spectator/jest';
import { LogisticianService } from './logistician.service';
import { LogisticianService as GeneratedLogisticianService } from '@generated/swagger/oms-api';
import { of } from 'rxjs';
import { Logistician } from '../models';

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

  const createMockLogistician = (
    logisticianNumber: string,
    id: number,
  ): Logistician => ({
    id,
    logisticianNumber,
    name: `Logistician ${logisticianNumber}`,
  });

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

  describe('getLogistician2470', () => {
    it('should successfully fetch logistician 2470', async () => {
      // Arrange
      const logisticians = [
        createMockLogistician('1000', 1),
        createMockLogistician('2470', 2), // Target logistician
        createMockLogistician('3000', 3),
      ];

      const generatedService = spectator.inject(GeneratedLogisticianService);
      generatedService.LogisticianGetLogisticians.mockReturnValue(
        of({ result: logisticians, error: false }),
      );

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

      // Assert
      expect(result).toEqual(logisticians[1]);
      expect(result.logisticianNumber).toBe('2470');
      expect(result.id).toBe(2);
      expect(
        generatedService.LogisticianGetLogisticians,
      ).toHaveBeenCalledWith({});
    });

    it('should throw error when logistician 2470 is not found', async () => {
      // Arrange
      const logisticians = [
        createMockLogistician('1000', 1),
        createMockLogistician('3000', 2),
      ];

      const generatedService = spectator.inject(GeneratedLogisticianService);
      generatedService.LogisticianGetLogisticians.mockReturnValue(
        of({ result: logisticians, error: false }),
      );

      // Act & Assert
      await expect(spectator.service.getLogistician2470()).rejects.toThrow(
        'Logistician 2470 not found',
      );
    });

    it('should throw ResponseArgsError when API call fails', async () => {
      // Arrange
      const errorResponse = {
        result: undefined,
        error: true,
      };

      const generatedService = spectator.inject(GeneratedLogisticianService);
      generatedService.LogisticianGetLogisticians.mockReturnValue(
        of(errorResponse),
      );

      // Act & Assert
      await expect(spectator.service.getLogistician2470()).rejects.toThrow();
    });

    it('should throw error when result is null', async () => {
      // Arrange
      const generatedService = spectator.inject(GeneratedLogisticianService);
      generatedService.LogisticianGetLogisticians.mockReturnValue(
        of({ result: undefined, error: false }),
      );

      // Act & Assert
      await expect(spectator.service.getLogistician2470()).rejects.toThrow();
    });

    it('should throw error when result is empty', async () => {
      // Arrange
      const generatedService = spectator.inject(GeneratedLogisticianService);
      generatedService.LogisticianGetLogisticians.mockReturnValue(
        of({ result: [], error: false }),
      );

      // Act & Assert
      await expect(spectator.service.getLogistician2470()).rejects.toThrow(
        'Logistician 2470 not found',
      );
    });

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

      const generatedService = spectator.inject(GeneratedLogisticianService);
      generatedService.LogisticianGetLogisticians.mockImplementation(() => {
        abortController.abort();
        return of({ result: logisticians, error: false });
      });

      // Act
      const promise = spectator.service.getLogistician2470(
        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 logisticians = [createMockLogistician('2470', 1)];

      const generatedService = spectator.inject(GeneratedLogisticianService);
      generatedService.LogisticianGetLogisticians.mockReturnValue(
        of({ result: logisticians, error: false }),
      );

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

      // 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(generatedService.LogisticianGetLogisticians).toHaveBeenCalled();
    });
  });
});
