import { shallow } from 'enzyme';
import { propEq } from 'ramda';
import React from 'react';
import { Placeholder } from 'semantic-ui-react';

import { TermChooser, mapStateToProps } from './TermChooser';
import { Site } from '../../../tests/storyshare';

describe('mapStateToProps', () => {
  it('retrieves terms from the DataLayer cache', () => {
    const site = { getCollectionByRoute: jest.fn() };
    mapStateToProps(null, { site, taxonomySlug: 'test-slug' });
    expect(site.getCollectionByRoute).toHaveBeenCalled();
    expect(site.getCollectionByRoute)
      .toHaveBeenCalledWith('test-slug', { per_page: 100 });
  });

  it('passes retrieved terms to the component', () => {
    const site = {
      getCollectionByRoute: jest.fn().mockReturnValue('test terms'),
    };
    expect(mapStateToProps(null, { site }).terms).toBe('test terms');
  });
});

describe('TermChooser component', () => {
  const onChange = jest.fn();

  const TERMS_FIXTURE = [
    { id: 2827, name: 'Corporate' },
    { id: 2828, name: 'Employment' },
    { id: 2829, name: 'Finance' },
  ];

  let wrapper;

  beforeEach(() => {
    onChange.mockClear();
    wrapper = shallow(
      <TermChooser
        onChange={ onChange }
        site={ new Site() }
        taxonomySlug="test-slug"
        terms={ TERMS_FIXTURE }
      />
    );
  });

  const EVENT_FIXTURE = { };

  /**
   * Simulates a click on one of the term labels
   * @param {number} index - Numeric index of the label (0-indexed).
   * @param {object} extra - Any extra props to pass through.
   * @returns {void}
   */
  const click = (index, extra = { }) => wrapper
    .find('Label')
    .at(index)
    .simulate('click', EVENT_FIXTURE, {
      value: TERMS_FIXTURE[index].id,
      ...extra,
    });

  /**
   * Checks whether one of the term labels is selected
   * @param {number} index - Numeric index of the label (0-indexed).
   * @returns {boolean} whether the label is selected
   */
  const isSelected = (index) => !wrapper.find('Label').at(index).prop('basic');

  /**
   * Returns the most recent arguments passed to onChange
   * @returns {any[]} arguments
   */
  const getOnChangeArgs = () =>
    onChange.mock.calls[onChange.mock.calls.length - 1];

  /**
   * Returns the most recent event passed to onChange
   * @returns {any} click object
   */
  const getOnChangeEvent = () => getOnChangeArgs()[0];

  /**
   * Returns the most recent data passed to onChange
   * @returns {Object} passed data
   */
  const getOnChangeData = () => getOnChangeArgs()[1];

  it('renders', () => {
    expect(wrapper).toExist();
  });

  it('wraps content in a Label.Group', () => {
    expect(wrapper.name()).toBe('LabelGroup');
  });

  it('renders three labels', () => {
    expect(wrapper.find('Label').length).toBe(3);
  });

  it('renders a label for each term', () => {
    const getContent = (index) =>
      wrapper
        .find('Label')
        .at(index)
        .find('span')
        .prop('dangerouslySetInnerHTML')
        .__html;

    expect(getContent(0)).toBe('Corporate');
    expect(getContent(1)).toBe('Employment');
    expect(getContent(2)).toBe('Finance');
  });

  it('defaults to having no label selected', () => {
    expect(isSelected(0)).toBe(false);
    expect(isSelected(1)).toBe(false);
    expect(isSelected(2)).toBe(false);
  });

  it('can select a label based on the value prop', () => {
    wrapper.setProps({ value: 2827 });
    expect(isSelected(0)).toBe(true);
    expect(isSelected(1)).toBe(false);
    expect(isSelected(2)).toBe(false);
  });

  it('calls onChange when a value is selected', () => {
    click(0);
    expect(onChange).toHaveBeenCalled();
  });

  it('passes the SyntheticEvent to onChange', () => {
    click(0);
    expect(getOnChangeEvent()).toBe(EVENT_FIXTURE);
  });

  it('passes the selected ID to onChange', () => {
    click(0);
    expect(getOnChangeData().value).toBe(2827);
  });

  it('passes any unknown props to onChange', () => {
    wrapper.setProps({ test: 'value' });
    click(0);
    const data = getOnChangeData();
    expect(data.taxonomySlug).toBe('test-slug');
    expect(data.test).toBe('value');
  });

  it('avoids passing internal props to onChange', () => {
    click(0);
    const data = getOnChangeData();
    expect(data.site).toBeUndefined();
    expect(data.terms).toBeUndefined();
  });

  it('avoids calling onChange when clicking a value again', () => {
    wrapper.setProps({ value: 2827 });
    click(0);
    expect(onChange).not.toHaveBeenCalled();
  });

  it('allows custom props to be used for labels', () => {
    wrapper.setProps({
      computeLabelProps: (selected) =>
        selected ? { 'selectedProp': true } : { 'otherProp': 'value' },
      value: 2827,
    });
    expect(wrapper.find('Label').at(0)).toHaveProp('selectedProp', true);
    expect(wrapper.find('Label').at(1)).toHaveProp('otherProp', 'value');
    expect(wrapper.find('Label').at(2)).toHaveProp('otherProp', 'value');
  });

  it('can select multiple labels based on the value prop', () => {
    wrapper.setProps({ multiple: true, value: [2827, 2829] });
    expect(isSelected(0)).toBe(true);
    expect(isSelected(1)).toBe(false);
    expect(isSelected(2)).toBe(true);
  });

  it('calls onChange to toggle a value on', () => {
    wrapper.setProps({ multiple: true, value: [] });
    click(0);
    expect(onChange).toHaveBeenCalled();
    expect(getOnChangeData().value).toEqual([2827]);
  });

  it('calls onChange to toggle a value off', () => {
    wrapper.setProps({ multiple: true, value: [2827] });
    click(0);
    expect(onChange).toHaveBeenCalled();
    expect(getOnChangeData().value).toEqual([]);
  });

  it('passes correct props to onChange for multiple select', () => {
    wrapper.setProps({ multiple: true, test: 'value', value: [] });
    click(0);
    expect(getOnChangeEvent()).toBe(EVENT_FIXTURE);

    const data = getOnChangeData();
    expect(data.multiple).toBe(true);
    expect(data.taxonomySlug).toBe('test-slug');
    expect(data.test).toBe('value');
    expect(data.site).toBeUndefined();
    expect(data.terms).toBeUndefined();
  });

  it('copes if multiple select clicked without a value defined', () => {
    wrapper.setProps({ multiple: true });
    click(0);
    expect(getOnChangeData().value).toEqual([2827]);
  });

  it('copes if a multiple value is not yet set', () => {
    wrapper.setProps({ multiple: true });
    expect(wrapper).toExist();
  });

  it('can filter terms', () => {
    wrapper.setProps({ filterPredicate: propEq('id', 2828) });
    const label = wrapper.find('Label');
    expect(label.length).toBe(1);
    expect(label).toHaveProp('value', 2828);
  });

  it('renders a Loader if terms have not yet loaded', () => {
    wrapper.setProps({ terms: undefined });
    expect(wrapper.name()).not.toBe('LabelGroup');
    const loader = wrapper.find('Loader');
    expect(loader).toExist();
    expect(loader).toHaveProp('active');
    expect(loader).toHaveProp('inline');
  });

  it('renders a Loader if no terms are defined', () => {
    wrapper.setProps({ terms: [] });
    expect(wrapper.name()).not.toBe('LabelGroup');
    const loader = wrapper.find('Loader');
    expect(loader).toExist();
    expect(loader).toHaveProp('active');
    expect(loader).toHaveProp('inline');
  });

  it('allows the placeholder to be customised', () => {
    wrapper.setProps({ terms: undefined, placeholder: <Placeholder /> });
    expect(wrapper.find('Loader')).not.toExist();
    expect(wrapper.find('Placeholder')).toExist();
  });

  it('renders an anchor tag Label using "as=a" prop', () => {
    wrapper.find('Label').forEach((label) => {
      expect(label).toHaveProp('as', 'a');
    });
  });
});
