import sortBy from 'lodash/sortBy';

import {mountWithTheme} from 'sentry-test/enzyme';

import {openModal} from 'sentry/actionCreators/modal';
import GlobalModal from 'sentry/components/globalModal';
import convertRelayPiiConfig from 'sentry/views/settings/components/dataScrubbing/convertRelayPiiConfig';
import Add from 'sentry/views/settings/components/dataScrubbing/modals/add';
import {MethodType, RuleType} from 'sentry/views/settings/components/dataScrubbing/types';
import {
  getMethodLabel,
  getRuleLabel,
} from 'sentry/views/settings/components/dataScrubbing/utils';

const relayPiiConfig = TestStubs.DataScrubbingRelayPiiConfig();
const stringRelayPiiConfig = JSON.stringify(relayPiiConfig);
const organizationSlug = 'sentry';
const convertedRules = convertRelayPiiConfig(stringRelayPiiConfig);
const rules = convertedRules;
const successfullySaved = jest.fn();
const projectId = 'foo';
const endpoint = `/projects/${organizationSlug}/${projectId}/`;
const api = new MockApiClient();

async function renderComponent() {
  const wrapper = mountWithTheme(<GlobalModal />);

  openModal(modalProps => (
    <Add
      {...modalProps}
      projectId={projectId}
      savedRules={rules}
      api={api}
      endpoint={endpoint}
      orgSlug={organizationSlug}
      onSubmitSuccess={successfullySaved}
    />
  ));

  await tick();
  wrapper.update();

  return wrapper;
}

describe('Add Modal', () => {
  it('open Add Rule Modal', async () => {
    const wrapper = await renderComponent();

    expect(wrapper.find('Header').text()).toEqual('Add an advanced data scrubbing rule');

    const fieldGroup = wrapper.find('FieldGroup');
    expect(fieldGroup).toHaveLength(2);

    // Method Field
    const methodGroup = fieldGroup.at(0).find('Field');
    expect(methodGroup.find('FieldLabel').text()).toEqual('Method');
    const methodFieldHelp = 'What to do';
    expect(methodGroup.find('QuestionTooltip').prop('title')).toEqual(methodFieldHelp);
    expect(methodGroup.find('Tooltip').prop('title')).toEqual(methodFieldHelp);
    const methodField = methodGroup.find('SelectField');
    expect(methodField.exists()).toBe(true);
    const methodFieldProps = methodField.props();
    expect(methodFieldProps.value).toEqual(MethodType.MASK);
    const methodFieldOptions = sortBy(Object.values(MethodType)).map(value => ({
      ...getMethodLabel(value),
      value,
    }));
    expect(methodFieldProps.options).toEqual(methodFieldOptions);

    // Type Field
    const typeGroup = fieldGroup.at(1).find('Field');
    expect(typeGroup.find('FieldLabel').text()).toEqual('Data Type');
    const typeFieldHelp =
      'What to look for. Use an existing pattern or define your own using regular expressions.';
    expect(typeGroup.find('QuestionTooltip').prop('title')).toEqual(typeFieldHelp);
    expect(typeGroup.find('Tooltip').prop('title')).toEqual(typeFieldHelp);
    const typeField = typeGroup.find('SelectField');
    expect(typeField.exists()).toBe(true);
    const typeFieldProps = typeField.props();
    expect(typeFieldProps.value).toEqual(RuleType.CREDITCARD);

    const typeFieldOptions = sortBy(Object.values(RuleType)).map(value => ({
      label: getRuleLabel(value),
      value,
    }));
    expect(typeFieldProps.options).toEqual(typeFieldOptions);

    // Event ID
    expect(wrapper.find('Toggle').text()).toEqual('Use event ID for auto-completion');

    // Source Field
    const sourceGroup = wrapper.find('SourceGroup');
    expect(sourceGroup.exists()).toBe(true);
    expect(sourceGroup.find('EventIdField')).toHaveLength(0);
    const sourceField = sourceGroup.find('Field');
    expect(sourceField.find('FieldLabel').text()).toEqual('Source');
    const sourceFieldHelp =
      'Where to look. In the simplest case this can be an attribute name.';
    expect(sourceField.find('QuestionTooltip').prop('title')).toEqual(sourceFieldHelp);
    expect(sourceField.find('Tooltip').prop('title')).toEqual(sourceFieldHelp);

    // Close Modal
    const cancelButton = wrapper.find('[aria-label="Cancel"]').hostNodes();
    expect(cancelButton.exists()).toBe(true);
    cancelButton.simulate('click');

    await tick();
    wrapper.update();

    expect(wrapper.find('GlobalModal[visible=true]').exists()).toBe(false);
  });

  it('Display placeholder field', async () => {
    const wrapper = await renderComponent();

    const fieldGroup = wrapper.find('FieldGroup');
    expect(fieldGroup).toHaveLength(2);

    // Method Field
    const methodGroup = fieldGroup.at(0).find('Field');
    expect(methodGroup).toHaveLength(1);

    const methodField = methodGroup.find('[data-test-id="method-field"]');

    const methodFieldInput = methodField.find('input').at(1);
    methodFieldInput.simulate('keyDown', {key: 'ArrowDown'});

    const methodFieldMenuOptions = wrapper.find(
      '[data-test-id="method-field"] MenuList Option'
    );
    expect(methodFieldMenuOptions).toHaveLength(4);
    const replaceOption = methodFieldMenuOptions.at(3);

    expect(replaceOption.find('Label').text()).toEqual('Replace');
    expect(replaceOption.find('Details').text()).toEqual('(Replace with Placeholder)');

    // After the click the placeholder field MUST be in the DOM
    replaceOption.simulate('click');
    wrapper.update();

    expect(
      wrapper.find('[data-test-id="method-field"] input').at(1).prop('value')
    ).toEqual(MethodType.REPLACE);

    const updatedMethodGroup = wrapper.find('FieldGroup').at(0).find('Field');

    expect(updatedMethodGroup).toHaveLength(2);

    const placeholderField = updatedMethodGroup.at(1);
    expect(placeholderField.find('FieldLabel').text()).toEqual(
      'Custom Placeholder (Optional)'
    );
    const placeholderFieldHelp = 'It will replace the default placeholder [Filtered]';
    expect(placeholderField.find('QuestionTooltip').prop('title')).toEqual(
      placeholderFieldHelp
    );
    expect(placeholderField.find('Tooltip').prop('title')).toEqual(placeholderFieldHelp);

    // After the click, the placeholder field MUST NOT be in the DOM

    wrapper
      .find('[data-test-id="method-field"]')
      .find('input')
      .at(1)
      .simulate('keyDown', {key: 'ArrowDown'});

    const hashOption = wrapper
      .find('[data-test-id="method-field"] MenuList Option')
      .at(0);

    hashOption.simulate('click');

    expect(wrapper.find('[data-test-id="method-field"] input').at(1).prop('value')).toBe(
      MethodType.HASH
    );

    expect(wrapper.find('FieldGroup').at(0).find('Field')).toHaveLength(1);
  });

  it('Display regex field', async () => {
    const wrapper = await renderComponent();

    const fieldGroup = wrapper.find('FieldGroup');
    expect(fieldGroup).toHaveLength(2);

    // Type Field
    const typeGroup = fieldGroup.at(1).find('Field');
    expect(typeGroup).toHaveLength(1);

    const typeField = typeGroup.find('[data-test-id="type-field"]');

    const typeFieldInput = typeField.find('input').at(1);
    typeFieldInput.simulate('keyDown', {key: 'ArrowDown'});

    const typeFieldMenuOptions = wrapper.find(
      '[data-test-id="type-field"] MenuList Option'
    );

    expect(typeFieldMenuOptions).toHaveLength(13);
    const regexOption = typeFieldMenuOptions.at(7);

    expect(regexOption.find('Label').text()).toEqual('Regex matches');

    // After the click, the regex matches field MUST be in the DOM
    regexOption.simulate('click');
    wrapper.update();

    expect(wrapper.find('[data-test-id="type-field"] input').at(1).prop('value')).toEqual(
      RuleType.PATTERN
    );

    const updatedTypeGroup = wrapper.find('FieldGroup').at(1).find('Field');

    expect(updatedTypeGroup).toHaveLength(2);

    const regexField = updatedTypeGroup.at(1);
    expect(regexField.find('FieldLabel').text()).toEqual('Regex matches');
    const regexFieldHelp = 'Custom regular expression (see documentation)';
    expect(regexField.find('QuestionTooltip').prop('title')).toEqual(regexFieldHelp);
    expect(regexField.find('Tooltip').prop('title')).toEqual(regexFieldHelp);

    // After the click, the regex matches field MUST NOT be in the DOM
    wrapper
      .find('[data-test-id="type-field"]')
      .find('input')
      .at(1)
      .simulate('keyDown', {key: 'ArrowDown'});

    const creditCardOption = wrapper
      .find('[data-test-id="type-field"] MenuList Option')
      .at(1);

    creditCardOption.simulate('click');

    expect(wrapper.find('[data-test-id="type-field"] input').at(1).prop('value')).toBe(
      RuleType.CREDITCARD
    );

    expect(wrapper.find('FieldGroup').at(1).find('Field')).toHaveLength(1);
  });

  it('Display Event Id', async () => {
    const eventId = '12345678901234567890123456789012';

    MockApiClient.addMockResponse({
      url: `/organizations/${organizationSlug}/data-scrubbing-selector-suggestions/`,
      body: {
        suggestions: [
          {type: 'value', examples: ['34359738368'], value: "extra.'system.cpu.memory'"},
          {type: 'value', value: '$frame.abs_path'},
        ],
      },
    });

    const wrapper = await renderComponent();
    const eventIdToggle = wrapper.find('Toggle');
    eventIdToggle.simulate('click');

    const eventIdFieldInput = wrapper.find('[data-test-id="event-id-field"] input');
    eventIdFieldInput.simulate('change', {
      target: {value: eventId},
    });

    eventIdFieldInput.simulate('blur');

    await tick();

    // Loading
    expect(wrapper.find('[data-test-id="event-id-field"] FormSpinner')).toHaveLength(1);
    wrapper.update();

    // Fetched new suggestions successfully through the informed event ID
    expect(wrapper.find('[data-test-id="event-id-field"] IconCheckmark')).toHaveLength(1);
  });
});
