import RecentSearchesService from '~/filtered_search/services/recent_searches_service';
import RecentSearchesServiceError from '~/filtered_search/services/recent_searches_service_error';
import AccessorUtilities from '~/lib/utils/accessor';
import { useLocalStorageSpy } from 'helpers/local_storage_helper';

useLocalStorageSpy();

describe('RecentSearchesService', () => {
  let service;

  beforeEach(() => {
    service = new RecentSearchesService();
    localStorage.removeItem(service.localStorageKey);
  });

  describe('fetch', () => {
    beforeEach(() => {
      jest.spyOn(RecentSearchesService, 'isAvailable').mockReturnValue(true);
    });

    it('should default to empty array', done => {
      const fetchItemsPromise = service.fetch();

      fetchItemsPromise
        .then(items => {
          expect(items).toEqual([]);
        })
        .then(done)
        .catch(done.fail);
    });

    it('should reject when unable to parse', done => {
      jest.spyOn(localStorage, 'getItem').mockReturnValue('fail');
      const fetchItemsPromise = service.fetch();

      fetchItemsPromise
        .then(done.fail)
        .catch(error => {
          expect(error).toEqual(expect.any(SyntaxError));
        })
        .then(done)
        .catch(done.fail);
    });

    it('should reject when service is unavailable', done => {
      RecentSearchesService.isAvailable.mockReturnValue(false);

      service
        .fetch()
        .then(done.fail)
        .catch(error => {
          expect(error).toEqual(expect.any(Error));
        })
        .then(done)
        .catch(done.fail);
    });

    it('should return items from localStorage', done => {
      jest.spyOn(localStorage, 'getItem').mockReturnValue('["foo", "bar"]');
      const fetchItemsPromise = service.fetch();

      fetchItemsPromise
        .then(items => {
          expect(items).toEqual(['foo', 'bar']);
        })
        .then(done)
        .catch(done.fail);
    });

    describe('if .isAvailable returns `false`', () => {
      beforeEach(() => {
        RecentSearchesService.isAvailable.mockReturnValue(false);

        jest.spyOn(Storage.prototype, 'getItem').mockImplementation(() => {});
      });

      it('should not call .getItem', done => {
        RecentSearchesService.prototype
          .fetch()
          .then(done.fail)
          .catch(err => {
            expect(err).toEqual(new RecentSearchesServiceError());
            expect(localStorage.getItem).not.toHaveBeenCalled();
          })
          .then(done)
          .catch(done.fail);
      });
    });
  });

  describe('setRecentSearches', () => {
    beforeEach(() => {
      jest.spyOn(RecentSearchesService, 'isAvailable').mockReturnValue(true);
    });

    it('should save things in localStorage', () => {
      jest.spyOn(localStorage, 'setItem');
      const items = ['foo', 'bar'];
      service.save(items);

      expect(localStorage.setItem).toHaveBeenCalledWith(expect.any(String), JSON.stringify(items));
    });
  });

  describe('save', () => {
    beforeEach(() => {
      jest.spyOn(localStorage, 'setItem');
      jest.spyOn(RecentSearchesService, 'isAvailable').mockImplementation(() => {});
    });

    describe('if .isAvailable returns `true`', () => {
      const searchesString = 'searchesString';
      const localStorageKey = 'localStorageKey';
      const recentSearchesService = {
        localStorageKey,
      };

      beforeEach(() => {
        RecentSearchesService.isAvailable.mockReturnValue(true);

        jest.spyOn(JSON, 'stringify').mockReturnValue(searchesString);
      });

      it('should call .setItem', () => {
        RecentSearchesService.prototype.save.call(recentSearchesService);

        expect(localStorage.setItem).toHaveBeenCalledWith(localStorageKey, searchesString);
      });
    });

    describe('if .isAvailable returns `false`', () => {
      beforeEach(() => {
        RecentSearchesService.isAvailable.mockReturnValue(false);
      });

      it('should not call .setItem', () => {
        RecentSearchesService.prototype.save();

        expect(localStorage.setItem).not.toHaveBeenCalled();
      });
    });
  });

  describe('isAvailable', () => {
    let isAvailable;

    beforeEach(() => {
      jest.spyOn(AccessorUtilities, 'isLocalStorageAccessSafe');

      isAvailable = RecentSearchesService.isAvailable();
    });

    it('should call .isLocalStorageAccessSafe', () => {
      expect(AccessorUtilities.isLocalStorageAccessSafe).toHaveBeenCalled();
    });

    it('should return a boolean', () => {
      expect(typeof isAvailable).toBe('boolean');
    });
  });
});
