import { GlTokenSelector } from '@gitlab/ui';
import { shallowMount } from '@vue/test-utils';
import { nextTick } from 'vue';
import { stubComponent } from 'helpers/stub_component';
import waitForPromises from 'helpers/wait_for_promises';
import * as UserApi from '~/api/user_api';
import MembersTokenSelect from '~/invite_members/components/members_token_select.vue';

const label = 'testgroup';
const placeholder = 'Search for a member';
const user1 = { id: 1, name: 'John Smith', username: 'one_1', avatar_url: '' };
const user2 = { id: 2, name: 'Jane Doe', username: 'two_2', avatar_url: '' };
const allUsers = [user1, user2];

const createComponent = (props) => {
  return shallowMount(MembersTokenSelect, {
    propsData: {
      ariaLabelledby: label,
      placeholder,
      ...props,
    },
    stubs: {
      GlTokenSelector: stubComponent(GlTokenSelector),
    },
  });
};

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

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

  const findTokenSelector = () => wrapper.findComponent(GlTokenSelector);

  describe('rendering the token-selector component', () => {
    it('renders with the correct props', () => {
      wrapper = createComponent();

      const expectedProps = {
        ariaLabelledby: label,
        placeholder,
      };

      expect(findTokenSelector().props()).toEqual(expect.objectContaining(expectedProps));
    });
  });

  describe('users', () => {
    beforeEach(() => {
      jest.spyOn(UserApi, 'getUsers').mockResolvedValue({ data: allUsers });
      wrapper = createComponent();
    });

    describe('when input is focused for the first time (modal auto-focus)', () => {
      it('does not call the API', async () => {
        findTokenSelector().vm.$emit('focus');

        await waitForPromises();

        expect(UserApi.getUsers).not.toHaveBeenCalled();
      });
    });

    describe('when input is manually focused', () => {
      it('calls the API and sets dropdown items as request result', async () => {
        const tokenSelector = findTokenSelector();

        tokenSelector.vm.$emit('focus');
        tokenSelector.vm.$emit('blur');
        tokenSelector.vm.$emit('focus');

        await waitForPromises();

        expect(tokenSelector.props('dropdownItems')).toMatchObject(allUsers);
        expect(tokenSelector.props('hideDropdownWithNoItems')).toBe(false);
      });
    });

    describe('when text input is typed in', () => {
      let tokenSelector;

      beforeEach(() => {
        tokenSelector = findTokenSelector();
      });

      it('calls the API with search parameter', async () => {
        const searchParam = 'One';

        tokenSelector.vm.$emit('text-input', searchParam);

        await waitForPromises();

        expect(UserApi.getUsers).toHaveBeenCalledWith(searchParam, {
          active: true,
          exclude_internal: true,
        });
        expect(tokenSelector.props('hideDropdownWithNoItems')).toBe(false);
      });

      describe('when input text is an email', () => {
        it('allows user defined tokens', async () => {
          tokenSelector.vm.$emit('text-input', 'foo@bar.com');

          await nextTick();

          expect(tokenSelector.props('allowUserDefinedTokens')).toBe(true);
        });
      });
    });

    describe('when user is selected', () => {
      it('emits `input` event with selected users', () => {
        findTokenSelector().vm.$emit('input', [user1, user2]);

        expect(wrapper.emitted().input[0][0]).toEqual([user1, user2]);
      });
    });

    describe('when user is removed', () => {
      it('emits `clear` event', () => {
        findTokenSelector().vm.$emit('token-remove', [user1]);

        expect(wrapper.emitted('clear')).toEqual([[]]);
      });

      it('does not emit `clear` event when there are still tokens selected', () => {
        findTokenSelector().vm.$emit('input', [user1, user2]);
        findTokenSelector().vm.$emit('token-remove', [user1]);

        expect(wrapper.emitted('clear')).toBeUndefined();
      });
    });
  });

  describe('when text input is blurred', () => {
    it('clears text input', async () => {
      wrapper = createComponent();

      const tokenSelector = findTokenSelector();

      tokenSelector.vm.$emit('blur');

      await nextTick();

      expect(tokenSelector.props('hideDropdownWithNoItems')).toBe(false);
    });
  });

  describe('when component is mounted for a group using a saml provider', () => {
    const searchParam = 'name';
    const samlProviderId = 123;
    let resolveApiRequest;

    beforeEach(() => {
      jest.spyOn(UserApi, 'getUsers').mockImplementation(
        () =>
          new Promise((resolve) => {
            resolveApiRequest = resolve;
          }),
      );

      wrapper = createComponent({ filterId: samlProviderId, usersFilter: 'saml_provider_id' });

      findTokenSelector().vm.$emit('text-input', searchParam);
    });

    it('calls the API with the saml provider ID param', () => {
      resolveApiRequest({ data: allUsers });

      expect(UserApi.getUsers).toHaveBeenCalledWith(searchParam, {
        active: true,
        exclude_internal: true,
        saml_provider_id: samlProviderId,
      });
    });
  });
});
