import { Mock } from 'ts-mockery';
import type { ShlinkApiClient } from '../../../src/api/services/ShlinkApiClient';
import type { ShlinkState } from '../../../src/container/types';
import type { EditTag } from '../../../src/tags/reducers/tagEdit';
import { editTag as editTagCreator, tagEdited, tagEditReducerCreator } from '../../../src/tags/reducers/tagEdit';
import type { ColorGenerator } from '../../../src/utils/services/ColorGenerator';

describe('tagEditReducer', () => {
  const oldName = 'foo';
  const newName = 'bar';
  const color = '#ff0000';
  const editTagCall = jest.fn();
  const buildShlinkApiClient = () => Mock.of<ShlinkApiClient>({ editTag: editTagCall });
  const colorGenerator = Mock.of<ColorGenerator>({ setColorForKey: jest.fn() });
  const editTag = editTagCreator(buildShlinkApiClient, colorGenerator);
  const { reducer } = tagEditReducerCreator(editTag);

  describe('reducer', () => {
    it('returns loading on EDIT_TAG_START', () => {
      expect(reducer(undefined, editTag.pending('', Mock.all<EditTag>()))).toEqual({
        editing: true,
        edited: false,
        error: false,
      });
    });

    it('returns error on EDIT_TAG_ERROR', () => {
      expect(reducer(undefined, editTag.rejected(null, '', Mock.all<EditTag>()))).toEqual({
        editing: false,
        edited: false,
        error: true,
      });
    });

    it('returns tag names on EDIT_TAG', () => {
      expect(reducer(undefined, editTag.fulfilled({ oldName, newName, color }, '', Mock.all<EditTag>()))).toEqual({
        editing: false,
        edited: true,
        error: false,
        oldName: 'foo',
        newName: 'bar',
      });
    });
  });

  describe('tagEdited', () => {
    it('returns action based on provided params', () => {
      const payload = { oldName: 'foo', newName: 'bar', color: '#ff0000' };
      expect(tagEdited(payload).payload).toEqual(payload);
    });
  });

  describe('editTag', () => {
    const dispatch = jest.fn();
    const getState = () => Mock.of<ShlinkState>();

    afterEach(jest.clearAllMocks);

    it('calls API on success', async () => {
      editTagCall.mockResolvedValue(undefined);

      await editTag({ oldName, newName, color })(dispatch, getState, {});

      expect(editTagCall).toHaveBeenCalledTimes(1);
      expect(editTagCall).toHaveBeenCalledWith(oldName, newName);

      expect(colorGenerator.setColorForKey).toHaveBeenCalledTimes(1);
      expect(colorGenerator.setColorForKey).toHaveBeenCalledWith(newName, color);

      expect(dispatch).toHaveBeenCalledTimes(2);
      expect(dispatch).toHaveBeenLastCalledWith(expect.objectContaining({
        payload: { oldName, newName, color },
      }));
    });
  });
});
