import { createServiceFactory } from '@ngneat/spectator/jest';
import { ReturnSearchStore, ReturnSearchStatus } from './return-search.store';
import { ReturnSearchService } from '../services';
import { of, throwError } from 'rxjs';
import { ListResponseArgs } from '@isa/common/data-access';
import { ReceiptListItem } from '../models';
import { provideHttpClientTesting } from '@angular/common/http/testing';
import { provideHttpClient } from '@angular/common/http';
import { TabService } from '@isa/core/tabs';
import { SessionStorageProvider } from '@isa/core/storage';

describe('ReturnSearchStore', () => {
  const createService = createServiceFactory({
    service: ReturnSearchStore,
    providers: [
      provideHttpClient(),
      provideHttpClientTesting(),
    ],
    mocks: [ReturnSearchService, TabService, SessionStorageProvider],
  });

  it('should create the store', () => {
    const spectator = createService();
    expect(spectator.service).toBeTruthy();
  });

  describe('getEntity', () => {
    it('should return undefined if the entity does not exist', () => {
      const spectator = createService();
      const entity = spectator.service.getEntity(1);
      expect(entity).toBeUndefined();
    });

    it('should return the entity if it exists', () => {
      const spectator = createService();
      spectator.service.beforeSearch(1);
      const entity = spectator.service.getEntity(1);
      expect(entity).toBeDefined();
      expect(entity?.processId).toBe(1);
    });
  });

  describe('beforeSearch', () => {
    it('should initialize a new entity if it does not exist', () => {
      const spectator = createService();
      spectator.service.beforeSearch(1);
      const entity = spectator.service.getEntity(1);
      expect(entity).toEqual({
        processId: 1,
        status: ReturnSearchStatus.Pending,
      });
    });

    it('should reset an existing entity', () => {
      const spectator = createService();
      spectator.service.beforeSearch(1);
      spectator.service.handleSearchSuccess({
        processId: 1,
        response: {
          hits: 10,
          result: [],
          error: false,
          skip: 0,
          take: 10,
          invalidProperties: {},
        },
      });

      spectator.service.beforeSearch(1);
      const entity = spectator.service.getEntity(1);
      expect(entity?.status).toBe(ReturnSearchStatus.Pending);
      expect(entity?.items).toEqual([]);
      expect(entity?.hits).toBe(0);
    });
  });

  describe('handleSearchSuccess', () => {
    it('should update the entity with search results', () => {
      const spectator = createService();
      const response: ListResponseArgs<ReceiptListItem> = {
        hits: 5,
        skip: 0,
        error: false,
        take: 10,
        invalidProperties: {},
        result: [{ id: 1 } as ReceiptListItem],
      };

      spectator.service.beforeSearch(1);
      spectator.service.handleSearchSuccess({ processId: 1, response });

      const entity = spectator.service.getEntity(1);
      expect(entity?.status).toBe(ReturnSearchStatus.Success);
      expect(entity?.hits).toBe(5);
      expect(entity?.items).toEqual(response.result);
    });
  });

  describe('handleSearchError', () => {
    it('should update the entity with an error status', () => {
      const spectator = createService();
      const error = new Error('Search failed');

      spectator.service.beforeSearch(1);
      spectator.service.handleSearchError({ processId: 1, error });

      const entity = spectator.service.getEntity(1);
      expect(entity?.status).toBe(ReturnSearchStatus.Error);
      expect(entity?.error).toBe(error);
    });
  });

  describe('search', () => {
    it('should call the search service and handle success', () => {
      const spectator = createService();
      const mockResponse: ListResponseArgs<ReceiptListItem> = {
        hits: 3,
        skip: 0,
        error: false,
        take: 10,
        invalidProperties: {},
        result: [{ id: 1 } as ReceiptListItem],
      };
      const returnSearchService = spectator.inject(ReturnSearchService);
      returnSearchService.search.mockReturnValue(of(mockResponse));

      spectator.service.search({
        processId: 1,
        clear: true,
        query: { filter: {}, input: {}, orderBy: [], skip: 0, take: 5 },
      });

      const entity = spectator.service.getEntity(1);
      expect(entity?.status).toBe(ReturnSearchStatus.Success);
      expect(entity?.hits).toBe(3);
      expect(entity?.items).toEqual(mockResponse.result);
    });

    it('should call the search service and handle error', () => {
      const spectator = createService();
      const error = new Error('Search failed');
      const returnSearchService = spectator.inject(ReturnSearchService);
      returnSearchService.search.mockReturnValue(throwError(() => error));

      spectator.service.search({
        processId: 1,
        clear: true,
        query: { filter: {}, input: {}, orderBy: [], skip: 0, take: 5 },
      });

      const entity = spectator.service.getEntity(1);
      expect(entity?.status).toBe(ReturnSearchStatus.Error);
      expect(entity?.error).toBe(error);
    });
  });
});
