import { GlEmptyState, GlLoadingIcon, GlTable } from '@gitlab/ui';
import { mount, shallowMount } from '@vue/test-utils';
import MockAdapter from 'axios-mock-adapter';
import axios from '~/lib/utils/axios_utils';
import PaginationBar from '~/import_entities/components/pagination_bar.vue';
import BulkImportsHistoryApp from '~/pages/import/bulk_imports/history/components/bulk_imports_history_app.vue';
import { extendedWrapper } from 'helpers/vue_test_utils_helper';

describe('BulkImportsHistoryApp', () => {
  const API_URL = '/api/v4/bulk_imports/entities';

  const DEFAULT_HEADERS = {
    'x-page': 1,
    'x-per-page': 20,
    'x-next-page': 2,
    'x-total': 22,
    'x-total-pages': 2,
    'x-prev-page': null,
  };
  const DUMMY_RESPONSE = [
    {
      id: 1,
      bulk_import_id: 1,
      status: 'finished',
      source_full_path: 'top-level-group-12',
      destination_name: 'top-level-group-12',
      destination_namespace: 'h5bp',
      created_at: '2021-07-08T10:03:44.743Z',
      failures: [],
    },
    {
      id: 2,
      bulk_import_id: 2,
      status: 'failed',
      source_full_path: 'autodevops-demo',
      destination_name: 'autodevops-demo',
      destination_namespace: 'flightjs',
      parent_id: null,
      namespace_id: null,
      project_id: null,
      created_at: '2021-07-13T12:52:26.664Z',
      updated_at: '2021-07-13T13:34:49.403Z',
      failures: [
        {
          pipeline_class: 'BulkImports::Groups::Pipelines::GroupPipeline',
          pipeline_step: 'loader',
          exception_class: 'ActiveRecord::RecordNotUnique',
          correlation_id_value: '01FAFYSYZ7XPF3P9NSMTS693SZ',
          created_at: '2021-07-13T13:34:49.344Z',
        },
      ],
    },
  ];

  let wrapper;
  let mock;

  function createComponent({ shallow = true } = {}) {
    const mountFn = shallow ? shallowMount : mount;
    wrapper = mountFn(BulkImportsHistoryApp);
  }

  const originalApiVersion = gon.api_version;
  beforeAll(() => {
    gon.api_version = 'v4';
  });

  afterAll(() => {
    gon.api_version = originalApiVersion;
  });

  beforeEach(() => {
    mock = new MockAdapter(axios);
  });

  afterEach(() => {
    mock.restore();
    wrapper.destroy();
  });

  describe('general behavior', () => {
    it('renders loading state when loading', () => {
      createComponent();
      expect(wrapper.find(GlLoadingIcon).exists()).toBe(true);
    });

    it('renders empty state when no data is available', async () => {
      mock.onGet(API_URL).reply(200, [], DEFAULT_HEADERS);
      createComponent();
      await axios.waitForAll();

      expect(wrapper.find(GlLoadingIcon).exists()).toBe(false);
      expect(wrapper.find(GlEmptyState).exists()).toBe(true);
    });

    it('renders table with data when history is available', async () => {
      mock.onGet(API_URL).reply(200, DUMMY_RESPONSE, DEFAULT_HEADERS);
      createComponent();
      await axios.waitForAll();

      const table = wrapper.find(GlTable);
      expect(table.exists()).toBe(true);
      // can't use .props() or .attributes() here
      expect(table.vm.$attrs.items).toHaveLength(DUMMY_RESPONSE.length);
    });

    it('changes page when requested by pagination bar', async () => {
      const NEW_PAGE = 4;

      mock.onGet(API_URL).reply(200, DUMMY_RESPONSE, DEFAULT_HEADERS);
      createComponent();
      await axios.waitForAll();
      mock.resetHistory();

      wrapper.findComponent(PaginationBar).vm.$emit('set-page', NEW_PAGE);
      await axios.waitForAll();

      expect(mock.history.get.length).toBe(1);
      expect(mock.history.get[0].params).toStrictEqual(expect.objectContaining({ page: NEW_PAGE }));
    });
  });

  it('changes page size when requested by pagination bar', async () => {
    const NEW_PAGE_SIZE = 4;

    mock.onGet(API_URL).reply(200, DUMMY_RESPONSE, DEFAULT_HEADERS);
    createComponent();
    await axios.waitForAll();
    mock.resetHistory();

    wrapper.findComponent(PaginationBar).vm.$emit('set-page-size', NEW_PAGE_SIZE);
    await axios.waitForAll();

    expect(mock.history.get.length).toBe(1);
    expect(mock.history.get[0].params).toStrictEqual(
      expect.objectContaining({ per_page: NEW_PAGE_SIZE }),
    );
  });

  describe('details button', () => {
    beforeEach(() => {
      mock.onGet(API_URL).reply(200, DUMMY_RESPONSE, DEFAULT_HEADERS);
      createComponent({ shallow: false });
      return axios.waitForAll();
    });

    it('renders details button if relevant item has failures', async () => {
      expect(
        extendedWrapper(wrapper.find('tbody').findAll('tr').at(1)).findByText('Details').exists(),
      ).toBe(true);
    });

    it('does not render details button if relevant item has no failures', () => {
      expect(
        extendedWrapper(wrapper.find('tbody').findAll('tr').at(0)).findByText('Details').exists(),
      ).toBe(false);
    });

    it('expands details when details button is clicked', async () => {
      const ORIGINAL_ROW_INDEX = 1;
      await extendedWrapper(wrapper.find('tbody').findAll('tr').at(ORIGINAL_ROW_INDEX))
        .findByText('Details')
        .trigger('click');

      const detailsRowContent = wrapper
        .find('tbody')
        .findAll('tr')
        .at(ORIGINAL_ROW_INDEX + 1)
        .find('pre');

      expect(detailsRowContent.exists()).toBe(true);
      expect(JSON.parse(detailsRowContent.text())).toStrictEqual(DUMMY_RESPONSE[1].failures);
    });
  });
});
