import { GlButton } from '@gitlab/ui';
import { shallowMount } from '@vue/test-utils';
import { nextTick } from 'vue';
import { useMockLocationHelper } from 'helpers/mock_window_location_helper';
import { extendedWrapper } from 'helpers/vue_test_utils_helper';
import createFlash from '~/flash';
import IntegrationView from '~/profile/preferences/components/integration_view.vue';
import ProfilePreferences from '~/profile/preferences/components/profile_preferences.vue';
import { i18n } from '~/profile/preferences/constants';
import {
  integrationViews,
  userFields,
  bodyClasses,
  themes,
  lightModeThemeId1,
  darkModeThemeId,
  lightModeThemeId2,
} from '../mock_data';

jest.mock('~/flash');
const expectedUrl = '/foo';

useMockLocationHelper();

describe('ProfilePreferences component', () => {
  let wrapper;
  const defaultProvide = {
    integrationViews: [],
    userFields,
    bodyClasses,
    themes,
    profilePreferencesPath: '/update-profile',
    formEl: document.createElement('form'),
  };

  function createComponent(options = {}) {
    const { props = {}, provide = {}, attachTo } = options;
    return extendedWrapper(
      shallowMount(ProfilePreferences, {
        provide: {
          ...defaultProvide,
          ...provide,
        },
        propsData: props,
        attachTo,
      }),
    );
  }

  function findIntegrationsDivider() {
    return wrapper.findByTestId('profile-preferences-integrations-rule');
  }

  function findIntegrationsHeading() {
    return wrapper.findByTestId('profile-preferences-integrations-heading');
  }

  function findSubmitButton() {
    return wrapper.findComponent(GlButton);
  }

  function createThemeInput(themeId = lightModeThemeId1) {
    const input = document.createElement('input');
    input.setAttribute('name', 'user[theme_id]');
    input.setAttribute('type', 'radio');
    input.setAttribute('value', themeId.toString());
    input.setAttribute('checked', 'checked');
    return input;
  }

  function createForm(themeInput = createThemeInput()) {
    const form = document.createElement('form');
    form.setAttribute('url', expectedUrl);
    form.setAttribute('method', 'put');
    form.appendChild(themeInput);
    return form;
  }

  function setupBody() {
    const div = document.createElement('div');
    div.classList.add('container-fluid');
    document.body.appendChild(div);
    document.body.classList.add('content-wrapper');
  }

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

  it('should not render Integrations section', () => {
    wrapper = createComponent();
    const views = wrapper.findAll(IntegrationView);
    const divider = findIntegrationsDivider();
    const heading = findIntegrationsHeading();

    expect(divider.exists()).toBe(false);
    expect(heading.exists()).toBe(false);
    expect(views).toHaveLength(0);
  });

  it('should render Integration section', () => {
    wrapper = createComponent({ provide: { integrationViews } });
    const divider = findIntegrationsDivider();
    const heading = findIntegrationsHeading();
    const views = wrapper.findAll(IntegrationView);

    expect(divider.exists()).toBe(true);
    expect(heading.exists()).toBe(true);
    expect(views).toHaveLength(integrationViews.length);
  });

  describe('form submit', () => {
    let form;

    beforeEach(() => {
      setupBody();
      form = createForm();
      wrapper = createComponent({ provide: { formEl: form }, attachTo: document.body });
      const beforeSendEvent = new CustomEvent('ajax:beforeSend');
      form.dispatchEvent(beforeSendEvent);
    });

    it('disables the submit button', async () => {
      await nextTick();
      const button = findSubmitButton();
      expect(button.props('disabled')).toBe(true);
    });

    it('success re-enables the submit button', async () => {
      const successEvent = new CustomEvent('ajax:success');
      form.dispatchEvent(successEvent);

      await nextTick();
      const button = findSubmitButton();
      expect(button.props('disabled')).toBe(false);
    });

    it('error re-enables the submit button', async () => {
      const errorEvent = new CustomEvent('ajax:error');
      form.dispatchEvent(errorEvent);

      await nextTick();
      const button = findSubmitButton();
      expect(button.props('disabled')).toBe(false);
    });

    it('displays the default success message', () => {
      const successEvent = new CustomEvent('ajax:success');
      form.dispatchEvent(successEvent);

      expect(createFlash).toHaveBeenCalledWith({ message: i18n.defaultSuccess, type: 'notice' });
    });

    it('displays the custom success message', () => {
      const message = 'foo';
      const successEvent = new CustomEvent('ajax:success', { detail: [{ message }] });
      form.dispatchEvent(successEvent);

      expect(createFlash).toHaveBeenCalledWith({ message, type: 'notice' });
    });

    it('displays the default error message', () => {
      const errorEvent = new CustomEvent('ajax:error');
      form.dispatchEvent(errorEvent);

      expect(createFlash).toHaveBeenCalledWith({ message: i18n.defaultError, type: 'alert' });
    });

    it('displays the custom error message', () => {
      const message = 'bar';
      const errorEvent = new CustomEvent('ajax:error', { detail: [{ message }] });
      form.dispatchEvent(errorEvent);

      expect(createFlash).toHaveBeenCalledWith({ message, type: 'alert' });
    });
  });

  describe('theme changes', () => {
    let themeInput;
    let form;

    function setupWrapper() {
      wrapper = createComponent({ provide: { formEl: form }, attachTo: document.body });
    }

    function selectThemeId(themeId) {
      themeInput.setAttribute('value', themeId.toString());
    }

    function dispatchBeforeSendEvent() {
      const beforeSendEvent = new CustomEvent('ajax:beforeSend');
      form.dispatchEvent(beforeSendEvent);
    }

    function dispatchSuccessEvent() {
      const successEvent = new CustomEvent('ajax:success');
      form.dispatchEvent(successEvent);
    }

    beforeEach(() => {
      setupBody();
      themeInput = createThemeInput();
      form = createForm(themeInput);
    });

    it('reloads the page when switching from light to dark mode', async () => {
      selectThemeId(lightModeThemeId1);
      setupWrapper();

      selectThemeId(darkModeThemeId);
      dispatchBeforeSendEvent();
      await nextTick();

      dispatchSuccessEvent();
      await nextTick();

      expect(window.location.reload).toHaveBeenCalledTimes(1);
    });

    it('reloads the page when switching from dark to light mode', async () => {
      selectThemeId(darkModeThemeId);
      setupWrapper();

      selectThemeId(lightModeThemeId1);
      dispatchBeforeSendEvent();
      await nextTick();

      dispatchSuccessEvent();
      await nextTick();

      expect(window.location.reload).toHaveBeenCalledTimes(1);
    });

    it('does not reload the page when switching between light mode themes', async () => {
      selectThemeId(lightModeThemeId1);
      setupWrapper();

      selectThemeId(lightModeThemeId2);
      dispatchBeforeSendEvent();
      await nextTick();

      dispatchSuccessEvent();
      await nextTick();

      expect(window.location.reload).not.toHaveBeenCalled();
    });
  });
});
