import { TestBed } from '@angular/core/testing';
import { of } from 'rxjs';
import { RemissionReasonService } from './remission-reason.service';
import { ReturnService } from '@generated/swagger/inventory-api';
import { RemissionStockService } from './remission-stock.service';
import { ResponseArgsError } from '@isa/common/data-access';
import { KeyValueStringAndString } from '../models';

describe('RemissionReasonService', () => {
  let service: RemissionReasonService;
  let returnServiceSpy: jest.Mocked<ReturnService>;
  let stockServiceSpy: jest.Mocked<RemissionStockService>;

  beforeEach(() => {
    const returnServiceMock = {
      ReturnGetReturnReasons: jest.fn(),
    };

    const stockServiceMock = {
      fetchAssignedStock: jest.fn(),
    };

    TestBed.configureTestingModule({
      providers: [
        RemissionReasonService,
        { provide: ReturnService, useValue: returnServiceMock },
        { provide: RemissionStockService, useValue: stockServiceMock },
      ],
    });

    service = TestBed.inject(RemissionReasonService);
    returnServiceSpy = TestBed.inject(ReturnService) as jest.Mocked<ReturnService>;
    stockServiceSpy = TestBed.inject(RemissionStockService) as jest.Mocked<RemissionStockService>;
  });

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

  describe('fetchReturnReasons', () => {
    it('should return reasons when successful', async () => {
      // Arrange
      const mockStock = { id: 123, name: 'Test Stock' };
      const mockReasons: KeyValueStringAndString[] = [
        { key: 'reason1', value: 'Defective' },
        { key: 'reason2', value: 'Wrong size' },
      ];
      const mockResponse = {
        error: false,
        result: mockReasons,
      };

      stockServiceSpy.fetchAssignedStock.mockResolvedValue(mockStock);
      returnServiceSpy.ReturnGetReturnReasons.mockReturnValue(of(mockResponse));

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

      // Assert
      expect(result).toEqual(mockReasons);
      expect(stockServiceSpy.fetchAssignedStock).toHaveBeenCalled();
      expect(returnServiceSpy.ReturnGetReturnReasons).toHaveBeenCalledWith({
        stockId: 123,
      });
    });

    it('should return empty array when no assigned stock', async () => {
      // Arrange
      stockServiceSpy.fetchAssignedStock.mockResolvedValue(null as any);

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

      // Assert
      expect(result).toEqual([]);
      expect(stockServiceSpy.fetchAssignedStock).toHaveBeenCalled();
      expect(returnServiceSpy.ReturnGetReturnReasons).not.toHaveBeenCalled();
    });

    it('should throw ResponseArgsError when API request fails', async () => {
      // Arrange
      const mockStock = { id: 123, name: 'Test Stock' };
      const mockErrorResponse = {
        error: true,
        message: 'API Error',
      };

      stockServiceSpy.fetchAssignedStock.mockResolvedValue(mockStock);
      returnServiceSpy.ReturnGetReturnReasons.mockReturnValue(of(mockErrorResponse));

      // Act & Assert
      await expect(service.fetchReturnReasons()).rejects.toThrow(ResponseArgsError);
      expect(stockServiceSpy.fetchAssignedStock).toHaveBeenCalled();
      expect(returnServiceSpy.ReturnGetReturnReasons).toHaveBeenCalledWith({
        stockId: 123,
      });
    });

    it('should handle abort signal', async () => {
      // Arrange
      const mockStock = { id: 123, name: 'Test Stock' };
      const mockReasons: KeyValueStringAndString[] = [
        { key: 'reason1', value: 'Defective' },
      ];
      const mockResponse = {
        error: false,
        result: mockReasons,
      };

      stockServiceSpy.fetchAssignedStock.mockResolvedValue(mockStock);
      returnServiceSpy.ReturnGetReturnReasons.mockReturnValue(of(mockResponse));

      const abortController = new AbortController();

      // Act
      const result = await service.fetchReturnReasons(abortController.signal);

      // Assert
      expect(result).toEqual(mockReasons);
      expect(stockServiceSpy.fetchAssignedStock).toHaveBeenCalledWith(abortController.signal);
      expect(returnServiceSpy.ReturnGetReturnReasons).toHaveBeenCalledWith({
        stockId: 123,
      });
    });

    it('should handle undefined assigned stock', async () => {
      // Arrange
      stockServiceSpy.fetchAssignedStock.mockResolvedValue(undefined as any);

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

      // Assert
      expect(result).toEqual([]);
      expect(stockServiceSpy.fetchAssignedStock).toHaveBeenCalled();
      expect(returnServiceSpy.ReturnGetReturnReasons).not.toHaveBeenCalled();
    });

    it('should handle empty reasons response', async () => {
      // Arrange
      const mockStock = { id: 123, name: 'Test Stock' };
      const mockResponse = {
        error: false,
        result: [],
      };

      stockServiceSpy.fetchAssignedStock.mockResolvedValue(mockStock);
      returnServiceSpy.ReturnGetReturnReasons.mockReturnValue(of(mockResponse));

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

      // Assert
      expect(result).toEqual([]);
      expect(stockServiceSpy.fetchAssignedStock).toHaveBeenCalled();
      expect(returnServiceSpy.ReturnGetReturnReasons).toHaveBeenCalledWith({
        stockId: 123,
      });
    });

    it('should handle API error without message', async () => {
      // Arrange
      const mockStock = { id: 123, name: 'Test Stock' };
      const mockErrorResponse = {
        error: true,
        message: undefined,
      };

      stockServiceSpy.fetchAssignedStock.mockResolvedValue(mockStock);
      returnServiceSpy.ReturnGetReturnReasons.mockReturnValue(of(mockErrorResponse));

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