import { getByText } from '@testing-library/dom';
import { mount } from '@vue/test-utils';
import { cloneDeep } from 'lodash';
import DiffExpansionCell from '~/diffs/components/diff_expansion_cell.vue';
import { INLINE_DIFF_VIEW_TYPE } from '~/diffs/constants';
import { getPreviousLineIndex } from '~/diffs/store/utils';
import { createStore } from '~/mr_notes/stores';
import diffFileMockData from '../mock_data/diff_file';

const EXPAND_UP_CLASS = '.js-unfold';
const EXPAND_DOWN_CLASS = '.js-unfold-down';
const lineSources = {
  [INLINE_DIFF_VIEW_TYPE]: 'highlighted_diff_lines',
};
const lineHandlers = {
  [INLINE_DIFF_VIEW_TYPE]: (line) => line,
};

function makeLoadMoreLinesPayload({
  sinceLine,
  toLine,
  oldLineNumber,
  diffViewType,
  fileHash,
  nextLineNumbers = {},
  unfold = false,
  bottom = false,
  isExpandDown = false,
}) {
  return {
    endpoint: 'contextLinesPath',
    params: {
      since: sinceLine,
      to: toLine,
      offset: toLine + 1 - oldLineNumber,
      view: diffViewType,
      unfold,
      bottom,
    },
    lineNumbers: {
      oldLineNumber,
      newLineNumber: toLine + 1,
    },
    nextLineNumbers,
    fileHash,
    isExpandDown,
  };
}

function getLine(file, type, index) {
  const source = lineSources[type];
  const handler = lineHandlers[type];

  return handler(file[source][index]);
}

describe('DiffExpansionCell', () => {
  let mockFile;
  let mockLine;
  let store;

  beforeEach(() => {
    mockFile = cloneDeep(diffFileMockData);
    mockLine = getLine(mockFile, INLINE_DIFF_VIEW_TYPE, 8);
    store = createStore();
    store.state.diffs.diffFiles = [mockFile];
    jest.spyOn(store, 'dispatch').mockReturnValue(Promise.resolve());
  });

  const createComponent = (options = {}) => {
    const defaults = {
      fileHash: mockFile.file_hash,
      contextLinesPath: 'contextLinesPath',
      line: mockLine,
      isTop: false,
      isBottom: false,
    };
    const propsData = { ...defaults, ...options };

    return mount(DiffExpansionCell, { store, propsData });
  };

  const findExpandUp = (wrapper) => wrapper.find(EXPAND_UP_CLASS);
  const findExpandDown = (wrapper) => wrapper.find(EXPAND_DOWN_CLASS);
  const findExpandAll = ({ element }) => getByText(element, 'Show all unchanged lines');

  describe('top row', () => {
    it('should have "expand up" and "show all" option', () => {
      const wrapper = createComponent({
        isTop: true,
      });

      expect(findExpandUp(wrapper).exists()).toBe(true);
      expect(findExpandDown(wrapper).exists()).toBe(false);
      expect(findExpandAll(wrapper)).not.toBe(null);
    });
  });

  describe('middle row', () => {
    it('should have "expand down", "show all", "expand up" option', () => {
      const wrapper = createComponent();

      expect(findExpandUp(wrapper).exists()).toBe(true);
      expect(findExpandDown(wrapper).exists()).toBe(true);
      expect(findExpandAll(wrapper)).not.toBe(null);
    });
  });

  describe('bottom row', () => {
    it('should have "expand down" and "show all" option', () => {
      const wrapper = createComponent({
        isBottom: true,
      });

      expect(findExpandUp(wrapper).exists()).toBe(false);
      expect(findExpandDown(wrapper).exists()).toBe(true);
      expect(findExpandAll(wrapper)).not.toBe(null);
    });
  });

  describe('any row', () => {
    [
      { diffViewType: INLINE_DIFF_VIEW_TYPE, lineIndex: 8, file: { parallel_diff_lines: [] } },
    ].forEach(({ diffViewType, file, lineIndex }) => {
      describe(`with diffViewType (${diffViewType})`, () => {
        beforeEach(() => {
          mockLine = getLine(mockFile, diffViewType, lineIndex);
          store.state.diffs.diffFiles = [{ ...mockFile, ...file }];
          store.state.diffs.diffViewType = diffViewType;
        });

        it('does not initially dispatch anything', () => {
          expect(store.dispatch).not.toHaveBeenCalled();
        });

        it('on expand all clicked, dispatch loadMoreLines', () => {
          const oldLineNumber = mockLine.meta_data.old_pos;
          const newLineNumber = mockLine.meta_data.new_pos;
          const previousIndex = getPreviousLineIndex(diffViewType, mockFile, {
            oldLineNumber,
            newLineNumber,
          });

          const wrapper = createComponent();

          findExpandAll(wrapper).click();

          expect(store.dispatch).toHaveBeenCalledWith(
            'diffs/loadMoreLines',
            makeLoadMoreLinesPayload({
              fileHash: mockFile.file_hash,
              toLine: newLineNumber - 1,
              sinceLine: previousIndex,
              oldLineNumber,
              diffViewType,
            }),
          );
        });

        it('on expand up clicked, dispatch loadMoreLines', () => {
          mockLine.meta_data.old_pos = 200;
          mockLine.meta_data.new_pos = 200;

          const oldLineNumber = mockLine.meta_data.old_pos;
          const newLineNumber = mockLine.meta_data.new_pos;

          const wrapper = createComponent();

          findExpandUp(wrapper).trigger('click');

          expect(store.dispatch).toHaveBeenCalledWith(
            'diffs/loadMoreLines',
            makeLoadMoreLinesPayload({
              fileHash: mockFile.file_hash,
              toLine: newLineNumber - 1,
              sinceLine: 179,
              oldLineNumber,
              diffViewType,
              unfold: true,
            }),
          );
        });

        it('on expand down clicked, dispatch loadMoreLines', () => {
          mockFile[lineSources[diffViewType]][lineIndex + 1] = cloneDeep(
            mockFile[lineSources[diffViewType]][lineIndex],
          );
          const nextLine = getLine(mockFile, diffViewType, lineIndex + 1);

          nextLine.meta_data.old_pos = 300;
          nextLine.meta_data.new_pos = 300;
          mockLine.meta_data.old_pos = 200;
          mockLine.meta_data.new_pos = 200;

          const wrapper = createComponent();

          findExpandDown(wrapper).trigger('click');

          expect(store.dispatch).toHaveBeenCalledWith('diffs/loadMoreLines', {
            endpoint: 'contextLinesPath',
            params: {
              since: 1,
              to: 21, // the load amount, plus 1 line
              offset: 0,
              view: diffViewType,
              unfold: true,
              bottom: true,
            },
            lineNumbers: {
              // when expanding down, these are based on the previous line, 0, in this case
              oldLineNumber: 0,
              newLineNumber: 0,
            },
            nextLineNumbers: { old_line: 200, new_line: 200 },
            fileHash: mockFile.file_hash,
            isExpandDown: true,
          });
        });
      });
    });
  });
});