import { shallowMount } from '@vue/test-utils';
import { GlFormSelect, GlFormTextarea, GlFormInput, GlLink, GlToken, GlButton } from '@gitlab/ui';
import {
  PERCENT_ROLLOUT_GROUP_ID,
  ROLLOUT_STRATEGY_ALL_USERS,
  ROLLOUT_STRATEGY_PERCENT_ROLLOUT,
  ROLLOUT_STRATEGY_USER_ID,
  ROLLOUT_STRATEGY_GITLAB_USER_LIST,
} from 'ee/feature_flags/constants';
import Strategy from 'ee/feature_flags/components/strategy.vue';
import NewEnvironmentsDropdown from 'ee/feature_flags/components/new_environments_dropdown.vue';

import { userList } from '../mock_data';

const provide = {
  strategyTypeDocsPagePath: 'link-to-strategy-docs',
  environmentsScopeDocsPath: 'link-scope-docs',
};

describe('Feature flags strategy', () => {
  let wrapper;

  const findStrategy = () => wrapper.find('[data-testid="strategy"]');
  const findDocsLinks = () => wrapper.findAll(GlLink);

  const factory = (
    opts = {
      propsData: {
        strategy: {},
        index: 0,
        endpoint: '',
        userLists: [userList],
      },
      provide,
    },
  ) => {
    if (wrapper) {
      wrapper.destroy();
      wrapper = null;
    }
    wrapper = shallowMount(Strategy, opts);
  };

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

  describe('helper links', () => {
    const propsData = { strategy: {}, index: 0, endpoint: '', userLists: [userList] };
    factory({ propsData, provide });

    it('should display 2 helper links', () => {
      const links = findDocsLinks();
      expect(links.exists()).toBe(true);
      expect(links.at(0).attributes('href')).toContain('docs');
      expect(links.at(1).attributes('href')).toContain('docs');
    });
  });

  describe.each`
    name                                | parameter       | value    | newValue   | input
    ${ROLLOUT_STRATEGY_ALL_USERS}       | ${null}         | ${null}  | ${null}    | ${null}
    ${ROLLOUT_STRATEGY_PERCENT_ROLLOUT} | ${'percentage'} | ${'50'}  | ${'20'}    | ${GlFormInput}
    ${ROLLOUT_STRATEGY_USER_ID}         | ${'userIds'}    | ${'1,2'} | ${'1,2,3'} | ${GlFormTextarea}
  `('with strategy $name', ({ name, parameter, value, newValue, input }) => {
    let propsData;
    let strategy;
    beforeEach(() => {
      const parameters = {};
      if (parameter !== null) {
        parameters[parameter] = value;
      }
      strategy = { name, parameters };
      propsData = { strategy, index: 0, endpoint: '' };
      factory({ propsData, provide });
    });

    it('should set the select to match the strategy name', () => {
      expect(wrapper.find(GlFormSelect).attributes('value')).toBe(name);
    });

    it('should not show inputs for other parameters', () => {
      [GlFormTextarea, GlFormInput, GlFormSelect]
        .filter(component => component !== input)
        .map(component => findStrategy().findAll(component))
        .forEach(inputWrapper => expect(inputWrapper).toHaveLength(0));
    });

    if (parameter !== null) {
      it(`should show the input for ${parameter} with the correct value`, () => {
        const inputWrapper = findStrategy().find(input);
        expect(inputWrapper.exists()).toBe(true);
        expect(inputWrapper.attributes('value')).toBe(value);
      });

      it(`should emit a change event when altering ${parameter}`, () => {
        const inputWrapper = findStrategy().find(input);
        inputWrapper.vm.$emit('input', newValue);
        expect(wrapper.emitted('change')).toEqual([
          [{ name, parameters: expect.objectContaining({ [parameter]: newValue }), scopes: [] }],
        ]);
      });
    }
  });

  describe('with strategy gitlabUserList', () => {
    let propsData;
    let strategy;
    beforeEach(() => {
      strategy = { name: ROLLOUT_STRATEGY_GITLAB_USER_LIST, userListId: '2', parameters: {} };
      propsData = { strategy, index: 0, endpoint: '', userLists: [userList] };
      factory({ propsData, provide });
    });

    it('should set the select to match the strategy name', () => {
      expect(wrapper.find(GlFormSelect).attributes('value')).toBe(
        ROLLOUT_STRATEGY_GITLAB_USER_LIST,
      );
    });

    it('should not show inputs for other parameters', () => {
      expect(
        findStrategy()
          .find(GlFormTextarea)
          .exists(),
      ).toBe(false);
      expect(
        findStrategy()
          .find(GlFormInput)
          .exists(),
      ).toBe(false);
    });

    it('should show the input for userListId with the correct value', () => {
      const inputWrapper = findStrategy().find(GlFormSelect);
      expect(inputWrapper.exists()).toBe(true);
      expect(inputWrapper.attributes('value')).toBe('2');
    });

    it('should emit a change event when altering the userListId', () => {
      const inputWrapper = findStrategy().find(GlFormSelect);
      inputWrapper.vm.$emit('input', '3');
      inputWrapper.vm.$emit('change', '3');
      return wrapper.vm.$nextTick().then(() => {
        expect(wrapper.emitted('change')).toEqual([
          [
            {
              name: ROLLOUT_STRATEGY_GITLAB_USER_LIST,
              userListId: '3',
              scopes: [],
              parameters: {},
            },
          ],
        ]);
      });
    });
  });

  describe('with a strategy', () => {
    describe('with a single environment scope defined', () => {
      let strategy;

      beforeEach(() => {
        strategy = {
          name: ROLLOUT_STRATEGY_PERCENT_ROLLOUT,
          parameters: { percentage: '50' },
          scopes: [{ environmentScope: 'production' }],
        };
        const propsData = { strategy, index: 0, endpoint: '' };
        factory({ propsData, provide });
      });

      it('should revert to all-environments scope when last scope is removed', () => {
        const token = wrapper.find(GlToken);
        token.vm.$emit('close');
        return wrapper.vm.$nextTick().then(() => {
          expect(wrapper.findAll(GlToken)).toHaveLength(0);
          expect(wrapper.emitted('change')).toEqual([
            [
              {
                name: ROLLOUT_STRATEGY_PERCENT_ROLLOUT,
                parameters: { percentage: '50', groupId: PERCENT_ROLLOUT_GROUP_ID },
                scopes: [{ environmentScope: '*' }],
              },
            ],
          ]);
        });
      });
    });

    describe('with an all-environments scope defined', () => {
      let strategy;

      beforeEach(() => {
        strategy = {
          name: ROLLOUT_STRATEGY_PERCENT_ROLLOUT,
          parameters: { percentage: '50' },
          scopes: [{ environmentScope: '*' }],
        };
        const propsData = { strategy, index: 0, endpoint: '' };
        factory({ propsData, provide });
      });

      it('should change the parameters if a different strategy is chosen', () => {
        const select = wrapper.find(GlFormSelect);
        select.vm.$emit('input', ROLLOUT_STRATEGY_ALL_USERS);
        select.vm.$emit('change', ROLLOUT_STRATEGY_ALL_USERS);
        return wrapper.vm.$nextTick().then(() => {
          expect(wrapper.find(GlFormInput).exists()).toBe(false);
          expect(wrapper.emitted('change')).toEqual([
            [
              {
                name: ROLLOUT_STRATEGY_ALL_USERS,
                parameters: {},
                scopes: [{ environmentScope: '*' }],
              },
            ],
          ]);
        });
      });

      it('should display selected scopes', () => {
        const dropdown = wrapper.find(NewEnvironmentsDropdown);
        dropdown.vm.$emit('add', 'production');
        return wrapper.vm.$nextTick().then(() => {
          expect(wrapper.findAll(GlToken)).toHaveLength(1);
          expect(wrapper.find(GlToken).text()).toBe('production');
        });
      });

      it('should display all selected scopes', () => {
        const dropdown = wrapper.find(NewEnvironmentsDropdown);
        dropdown.vm.$emit('add', 'production');
        dropdown.vm.$emit('add', 'staging');
        return wrapper.vm.$nextTick().then(() => {
          const tokens = wrapper.findAll(GlToken);
          expect(tokens).toHaveLength(2);
          expect(tokens.at(0).text()).toBe('production');
          expect(tokens.at(1).text()).toBe('staging');
        });
      });

      it('should emit selected scopes', () => {
        const dropdown = wrapper.find(NewEnvironmentsDropdown);
        dropdown.vm.$emit('add', 'production');
        return wrapper.vm.$nextTick().then(() => {
          expect(wrapper.emitted('change')).toEqual([
            [
              {
                name: ROLLOUT_STRATEGY_PERCENT_ROLLOUT,
                parameters: { percentage: '50', groupId: PERCENT_ROLLOUT_GROUP_ID },
                scopes: [
                  { environmentScope: '*', shouldBeDestroyed: true },
                  { environmentScope: 'production' },
                ],
              },
            ],
          ]);
        });
      });

      it('should emit a delete if the delete button is clicked', () => {
        wrapper.find(GlButton).vm.$emit('click');
        expect(wrapper.emitted('delete')).toEqual([[]]);
      });
    });

    describe('without scopes defined', () => {
      beforeEach(() => {
        const strategy = {
          name: ROLLOUT_STRATEGY_PERCENT_ROLLOUT,
          parameters: { percentage: '50' },
          scopes: [],
        };
        const propsData = { strategy, index: 0, endpoint: '' };
        factory({ propsData, provide });
      });

      it('should display selected scopes', () => {
        const dropdown = wrapper.find(NewEnvironmentsDropdown);
        dropdown.vm.$emit('add', 'production');
        return wrapper.vm.$nextTick().then(() => {
          expect(wrapper.findAll(GlToken)).toHaveLength(1);
          expect(wrapper.find(GlToken).text()).toBe('production');
        });
      });

      it('should display all selected scopes', () => {
        const dropdown = wrapper.find(NewEnvironmentsDropdown);
        dropdown.vm.$emit('add', 'production');
        dropdown.vm.$emit('add', 'staging');
        return wrapper.vm.$nextTick().then(() => {
          const tokens = wrapper.findAll(GlToken);
          expect(tokens).toHaveLength(2);
          expect(tokens.at(0).text()).toBe('production');
          expect(tokens.at(1).text()).toBe('staging');
        });
      });

      it('should emit selected scopes', () => {
        const dropdown = wrapper.find(NewEnvironmentsDropdown);
        dropdown.vm.$emit('add', 'production');
        return wrapper.vm.$nextTick().then(() => {
          expect(wrapper.emitted('change')).toEqual([
            [
              {
                name: ROLLOUT_STRATEGY_PERCENT_ROLLOUT,
                parameters: { percentage: '50', groupId: PERCENT_ROLLOUT_GROUP_ID },
                scopes: [{ environmentScope: 'production' }],
              },
            ],
          ]);
        });
      });
    });
  });
});
