import { GlEmptyState, GlLoadingIcon } from '@gitlab/ui';
import { within } from '@testing-library/dom';
import { mount, createWrapper } from '@vue/test-utils';
import Vue from 'vue';
import Vuex from 'vuex';
import waitForPromises from 'helpers/wait_for_promises';
import Api from '~/api';
import UserListsComponent from '~/user_lists/components/user_lists.vue';
import UserListsTable from '~/user_lists/components/user_lists_table.vue';
import createStore from '~/user_lists/store/index';
import TablePagination from '~/vue_shared/components/pagination/table_pagination.vue';
import { userList } from '../../feature_flags/mock_data';

jest.mock('~/api');

Vue.use(Vuex);

describe('~/user_lists/components/user_lists.vue', () => {
  const mockProvide = {
    newUserListPath: '/user-lists/new',
    featureFlagsHelpPagePath: '/help/feature-flags',
    errorStateSvgPath: '/assets/illustrations/feature_flag.svg',
  };

  const mockState = {
    projectId: '1',
  };

  let wrapper;
  let store;

  const factory = (provide = mockProvide, fn = mount) => {
    store = createStore(mockState);
    wrapper = fn(UserListsComponent, {
      store,
      provide,
    });
  };

  const newButton = () => within(wrapper.element).queryAllByText('New user list');

  afterEach(() => {
    wrapper.destroy();
    wrapper = null;
  });

  describe('without permissions', () => {
    const provideData = {
      ...mockProvide,
      newUserListPath: null,
    };

    beforeEach(() => {
      Api.fetchFeatureFlagUserLists.mockResolvedValue({ data: [], headers: {} });
      factory(provideData);
    });

    it('does not render new user list button', () => {
      expect(newButton()).toHaveLength(0);
    });
  });

  describe('loading state', () => {
    it('renders a loading icon', () => {
      Api.fetchFeatureFlagUserLists.mockReturnValue(new Promise(() => {}));

      factory();

      const loadingElement = wrapper.findComponent(GlLoadingIcon);

      expect(loadingElement.exists()).toBe(true);
      expect(loadingElement.props('label')).toEqual('Loading user lists');
    });
  });

  describe('successful request', () => {
    describe('without user lists', () => {
      let emptyState;

      beforeEach(async () => {
        Api.fetchFeatureFlagUserLists.mockResolvedValue({ data: [], headers: {} });

        factory();
        await waitForPromises();
        await Vue.nextTick();

        emptyState = wrapper.findComponent(GlEmptyState);
      });

      it('should render the empty state', async () => {
        expect(emptyState.exists()).toBe(true);
      });

      it('renders new feature flag button', () => {
        expect(newButton()).not.toHaveLength(0);
      });

      it('renders generic title', () => {
        const title = createWrapper(
          within(emptyState.element).getByText('Get started with user lists'),
        );
        expect(title.exists()).toBe(true);
      });

      it('renders generic description', () => {
        const description = createWrapper(
          within(emptyState.element).getByText(
            'User lists allow you to define a set of users to use with Feature Flags.',
          ),
        );
        expect(description.exists()).toBe(true);
      });
    });

    describe('with paginated user lists', () => {
      let table;

      beforeEach(async () => {
        Api.fetchFeatureFlagUserLists.mockResolvedValue({
          data: [userList],
          headers: {
            'x-next-page': '2',
            'x-page': '1',
            'X-Per-Page': '2',
            'X-Prev-Page': '',
            'X-TOTAL': '37',
            'X-Total-Pages': '5',
          },
        });

        factory();
        jest.spyOn(store, 'dispatch');
        await Vue.nextTick();
        table = wrapper.findComponent(UserListsTable);
      });

      it('should render a table with feature flags', () => {
        expect(table.exists()).toBe(true);
        expect(table.props('userLists')).toEqual([userList]);
      });

      it('renders new feature flag button', () => {
        expect(newButton()).not.toHaveLength(0);
      });

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

        beforeEach(() => {
          pagination = wrapper.findComponent(TablePagination);
        });

        it('should render pagination', () => {
          expect(pagination.exists()).toBe(true);
        });

        it('should make an API request when page is clicked', () => {
          jest.spyOn(store, 'dispatch');
          pagination.vm.change('4');

          expect(store.dispatch).toHaveBeenCalledWith('setUserListsOptions', {
            page: '4',
          });
        });
      });
    });
  });

  describe('unsuccessful request', () => {
    beforeEach(async () => {
      Api.fetchFeatureFlagUserLists.mockRejectedValue();
      factory();

      await Vue.nextTick();
    });

    it('should render error state', () => {
      const emptyState = wrapper.findComponent(GlEmptyState);
      const title = createWrapper(
        within(emptyState.element).getByText('There was an error fetching the user lists.'),
      );
      expect(title.exists()).toBe(true);
      const description = createWrapper(
        within(emptyState.element).getByText(
          'Try again in a few moments or contact your support team.',
        ),
      );
      expect(description.exists()).toBe(true);
    });

    it('renders new feature flag button', () => {
      expect(newButton()).not.toHaveLength(0);
    });
  });
});
