import React from 'react';
import { usePersistentState } from 'hooks';
import { render, mocked, fireEvent, waitFor } from 'test-utils';

jest.spyOn(Storage.prototype, 'setItem');
const setItemMock = mocked(localStorage.setItem);

jest.spyOn(Storage.prototype, 'getItem');
const getItemMock = mocked(localStorage.getItem);

interface TestCompProps {
  storagekey: string;
  defaultVal: any;
  onclick(dispatch: React.Dispatch<any>): void;
}
const TestComp: React.FC<TestCompProps> = ({
  storagekey,
  defaultVal,
  onclick
}) => {
  const [state, setState] = usePersistentState<any>(storagekey, defaultVal);

  return (
    <div>
      <div data-testid="state">{state}</div>
      <button data-testid="setState" onClick={(e) => onclick(setState)}>
        btn
      </button>
    </div>
  );
};

afterAll(() => {
  jest.restoreAllMocks();
});

afterEach(() => {
  setItemMock.mockReset();
  getItemMock.mockReset();
});

it('gives existing value if found', () => {
  getItemMock.mockReturnValue('1234');
  const { getByTestId } = render(
    <TestComp storagekey="testkey" defaultVal="5678" onclick={jest.fn()} />
  );
  expect(getByTestId('state')).toHaveTextContent('1234');
});

it('gives default value if none exists', () => {
  getItemMock.mockReturnValue(null);
  const { getByTestId } = render(
    <TestComp storagekey="testkey" defaultVal="zxcv" onclick={jest.fn()} />
  );
  expect(getByTestId('state')).toHaveTextContent('zxcv');
});

it('updates state with default value', () => {
  getItemMock.mockReturnValue(null);
  render(
    <TestComp storagekey="testkey" defaultVal="zxcv" onclick={jest.fn()} />
  );
  expect(setItemMock).toHaveBeenCalledTimes(1);
  expect(setItemMock).toHaveBeenCalledWith('testkey', JSON.stringify('zxcv'));
});

it('updates localStorage on call to setState', async () => {
  getItemMock.mockReturnValue(null);
  const onClick = (dispatch: React.Dispatch<any>) => dispatch('newval');
  const { getByTestId } = render(
    <TestComp storagekey="testkey" defaultVal="zxcv" onclick={onClick} />
  );
  expect(setItemMock).toHaveBeenCalledTimes(1);
  expect(setItemMock).toHaveBeenCalledWith('testkey', JSON.stringify('zxcv'));
  fireEvent.click(getByTestId('setState'));
  await waitFor(() => {
    expect(getByTestId('state')).toHaveTextContent('newval');
  });
  await waitFor(() => {
    expect(setItemMock).toHaveBeenCalledTimes(2);
    expect(setItemMock).toHaveBeenCalledWith(
      'testkey',
      JSON.stringify('newval')
    );
  });
});