import { GlDropdown, GlDropdownItem, GlDropdownForm } from '@gitlab/ui';
import { createLocalVue, mount, shallowMount, createWrapper } from '@vue/test-utils';
import { nextTick } from 'vue';
import VueApollo from 'vue-apollo';
import { extendedWrapper } from 'helpers/vue_test_utils_helper';
import createMockApollo from 'helpers/mock_apollo_helper';

import RegistrationDropdown from '~/runner/components/registration/registration_dropdown.vue';
import RegistrationTokenResetDropdownItem from '~/runner/components/registration/registration_token_reset_dropdown_item.vue';

import { INSTANCE_TYPE, GROUP_TYPE, PROJECT_TYPE } from '~/runner/constants';

import getRunnerPlatformsQuery from '~/vue_shared/components/runner_instructions/graphql/queries/get_runner_platforms.query.graphql';
import getRunnerSetupInstructionsQuery from '~/vue_shared/components/runner_instructions/graphql/queries/get_runner_setup.query.graphql';

import {
  mockGraphqlRunnerPlatforms,
  mockGraphqlInstructions,
} from 'jest/vue_shared/components/runner_instructions/mock_data';

const mockToken = '0123456789';
const maskToken = '**********';

describe('RegistrationDropdown', () => {
  let wrapper;

  const findDropdown = () => wrapper.findComponent(GlDropdown);

  const findRegistrationInstructionsDropdownItem = () => wrapper.findComponent(GlDropdownItem);
  const findTokenDropdownItem = () => wrapper.findComponent(GlDropdownForm);
  const findTokenResetDropdownItem = () =>
    wrapper.findComponent(RegistrationTokenResetDropdownItem);

  const findToggleMaskButton = () => wrapper.findByTestId('toggle-masked');

  const createComponent = ({ props = {}, ...options } = {}, mountFn = shallowMount) => {
    wrapper = extendedWrapper(
      mountFn(RegistrationDropdown, {
        propsData: {
          registrationToken: mockToken,
          type: INSTANCE_TYPE,
          ...props,
        },
        ...options,
      }),
    );
  };

  it.each`
    type             | text
    ${INSTANCE_TYPE} | ${'Register an instance runner'}
    ${GROUP_TYPE}    | ${'Register a group runner'}
    ${PROJECT_TYPE}  | ${'Register a project runner'}
  `('Dropdown text for type $type is "$text"', () => {
    createComponent({ props: { type: INSTANCE_TYPE } }, mount);

    expect(wrapper.text()).toContain('Register an instance runner');
  });

  it('Passes attributes to the dropdown component', () => {
    createComponent({ attrs: { right: true } });

    expect(findDropdown().attributes()).toMatchObject({ right: 'true' });
  });

  describe('Instructions dropdown item', () => {
    it('Displays "Show runner" dropdown item', () => {
      createComponent();

      expect(findRegistrationInstructionsDropdownItem().text()).toBe(
        'Show runner installation and registration instructions',
      );
    });

    describe('When the dropdown item is clicked', () => {
      const localVue = createLocalVue();
      localVue.use(VueApollo);

      const requestHandlers = [
        [getRunnerPlatformsQuery, jest.fn().mockResolvedValue(mockGraphqlRunnerPlatforms)],
        [getRunnerSetupInstructionsQuery, jest.fn().mockResolvedValue(mockGraphqlInstructions)],
      ];

      const findModalInBody = () =>
        createWrapper(document.body).find('[data-testid="runner-instructions-modal"]');

      beforeEach(() => {
        createComponent(
          {
            localVue,
            // Mock load modal contents from API
            apolloProvider: createMockApollo(requestHandlers),
            // Use `attachTo` to find the modal
            attachTo: document.body,
          },
          mount,
        );

        findRegistrationInstructionsDropdownItem().trigger('click');
      });

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

      it('opens the modal with contents', () => {
        const modalText = findModalInBody()
          .text()
          .replace(/[\n\t\s]+/g, ' ');

        expect(modalText).toContain('Install a runner');

        // Environment selector
        expect(modalText).toContain('Environment');
        expect(modalText).toContain('Linux macOS Windows Docker Kubernetes');

        // Architecture selector
        expect(modalText).toContain('Architecture');
        expect(modalText).toContain('amd64 amd64 386 arm arm64');

        expect(modalText).toContain('Download and install binary');
      });
    });
  });

  describe('Registration token', () => {
    it('Displays dropdown form for the registration token', () => {
      createComponent();

      expect(findTokenDropdownItem().exists()).toBe(true);
    });

    it('Displays masked value by default', () => {
      createComponent({}, mount);

      expect(findTokenDropdownItem().text()).toMatchInterpolatedText(
        `Registration token ${maskToken}`,
      );
    });
  });

  describe('Reset token item', () => {
    it('Displays registration token reset item', () => {
      createComponent();

      expect(findTokenResetDropdownItem().exists()).toBe(true);
    });

    it.each([INSTANCE_TYPE, GROUP_TYPE, PROJECT_TYPE])('Set up token reset for %s', (type) => {
      createComponent({ props: { type } });

      expect(findTokenResetDropdownItem().props('type')).toBe(type);
    });
  });

  it('Updates the token when it gets reset', async () => {
    createComponent({}, mount);

    const newToken = 'mock1';

    findTokenResetDropdownItem().vm.$emit('tokenReset', newToken);
    findToggleMaskButton().vm.$emit('click', { stopPropagation: jest.fn() });
    await nextTick();

    expect(findTokenDropdownItem().text()).toMatchInterpolatedText(
      `Registration token ${newToken}`,
    );
  });
});
