import React from 'react';
import Enzyme from 'enzyme';
import Adapter from 'enzyme-adapter-react-16';
import Sinon from 'sinon';
import { FilterCategoryCheckbox } from '../src/FilterCategoryCheckbox';
import styles from '../src/Filters.scss';

jest.mock('react-virtualized/dist/commonjs/AutoSizer', () => {
  const width = 1;
  const height = 1;
  return ({ children }) =>
    <div>{children({ width, height })}</div>;
});


Enzyme.configure({ adapter: new Adapter() });

const filter = {
  endpoint: '/filterM',
  key: 'aaa',
  value: 'AAA'
};

const I18nText = 'I18n Translation Text';
const I18n = { t: () => I18nText };

const data = [
  {
    key: 'mountain_dogs',
    value: 'Mountain Dogs',
    children: [
      { key: 100, value: 'Newfoundland' },
      { key: 101, value: 'Bernese Mountain Dog' },
      { key: 102, value: 'Greater Swiss Mountain Dog (Large)' }
    ]
  },
  {
    key: 'scent_hounds',
    value: 'Scent Hounds',
    children: [
      { key: 200, value: 'Harrier' },
      { key: 201, value: 'Basset Hound' }
    ]
  }
];

const selected = ['100', '101'];

const shallowRender = (props = {}) => {
  const mergedProps = Object.assign({}, {
    currentIndex: -1,
    filter,
    filterCheckboxRowHeight: 24,
    I18n,
    keyListeners: () => {},
    onAnalytics: () => {},
    onFetch: () => {},
    onRemove: () => {},
    onSave: () => {},
    styles
  }, props);

  return Enzyme.shallow(
    <FilterCategoryCheckbox {...mergedProps} />
  );
};

const mount = (props = {}) => {
  const mergedProps = Object.assign({}, {
    keyListeners: () => {},
    currentIndex: -1,
    filter,
    filterCheckboxRowHeight: 24,
    I18n,
    onAnalytics: () => {},
    onFetch: () => {},
    onRemove: () => {},
    onSave: () => {},
    styles
  }, props);

  return Enzyme.mount(
    <FilterCategoryCheckbox {...mergedProps} />
  );
};

describe('FilterCategoryCheckbox', () => {
  it('renders the count of items selected', () => {
    const wrapper = shallowRender({ selected });
    const countElement = wrapper.find(`.${styles.filterCheckboxCount}`);
    expect(countElement.text()).toBe(`(${selected.length})`);
  });

  it('restores selected items', () => {
    const wrapper = mount({ selected });
    wrapper.setState({ filteredKeys: data });

    const checkedElements = wrapper.find(`.${styles.checked}`);
    expect(checkedElements.length).toBe(selected.length);
  });

  describe('if instantiated with nothing selected', () => {
    it('triggers fetch', () => {
      const onFetch = Sinon.spy();
      mount({ onFetch });
      expect(onFetch.called).toBe(true);
    });
  });

  describe('on fetch', () => {
    it('is loading', () => {
      const wrapper = mount();
      expect(wrapper.state('isLoading')).toBe(true);
    });

    it('passes current filter and onComplete callback to fetch', () => {
      const onFetch = Sinon.spy();
      const wrapper = mount({ onFetch });
      expect(onFetch.calledWithExactly(filter, wrapper.instance().onComplete)).toBe(true);
    });

    describe('when load complete', () => {
      it('is not loading', () => {
        const onFetch = (_, onComplete) => { onComplete(data); };
        const wrapper = mount({ onFetch });
        expect(wrapper.state('isLoading')).toBe(false);
      });

      it('toggles hasLoaded', () => {
        const onFetch = (_, onComplete) => { onComplete(data); };
        const wrapper = mount({ onFetch });
        expect(wrapper.state('hasLoaded')).toBe(true);
      });

      it('expands', () => {
        const onFetch = (_, onComplete) => { onComplete(data); };
        const wrapper = mount({ onFetch });
        expect(wrapper.state('expanded')).toBe(true);
      });
    });
  });

  describe('on click', () => {
    describe('if data has not loaded', () => {
      it('triggers fetch', () => {
        const onFetch = Sinon.spy();
        const wrapper = mount({ onFetch, selected });

        const containerElement = wrapper.find(`.${styles.tokenContainer}`);
        containerElement.simulate('click');

        expect(onFetch.called).toBe(true);
      });
    });

    describe('if data has loaded', () => {
      it('expands immediately', () => {
        const wrapper = mount({ selected });
        wrapper.setState({ hasLoaded: true });

        const containerElement = wrapper.find(`.${styles.tokenContainer}`);
        containerElement.simulate('click');

        expect(wrapper.state('expanded')).toBe(true);
      });
    });
  });

  describe('on blur', () => {
    it('is not expanded', () => {
      const wrapper = mount({ selected });
      wrapper.setState({ expanded: true });
      wrapper.instance().onBlur();
      expect(wrapper.state('expanded')).toBe(false);
    });

    it('is not loading', () => {
      const wrapper = mount({ selected });
      wrapper.setState({ isLoading: true });
      wrapper.instance().onBlur();
      expect(wrapper.state('isLoading')).toBe(false);
    });

    it('sends current filter and selected keys to save handler', () => {
      const onSave = Sinon.spy();
      const wrapper = mount({ onSave, selected });
      wrapper.instance().onBlur();

      expect(onSave.calledWithExactly(filter, selected, {})).toBe(true);
    });
  });

  describe('on check one', () => {
    const mockEvent = obj => Object.assign({ stopPropagation: () => {} }, obj);

    it('updates selected keys on check', () => {
      const wrapper = mount({ selected });
      wrapper.setState({ filteredKeys: data, expanded: true });

      const checkboxElement = wrapper.find(`.${styles.checkboxBox}`).at(3).parent();
      const key = checkboxElement.prop('data-key');

      const evt = mockEvent({ currentTarget: { dataset: { key } } });
      checkboxElement.props().onClick(evt);

      expect(Object.keys(wrapper.state('selectedKeys'))).toEqual([...selected, `${key}`]);
    });

    it('updates selected keys on uncheck', () => {
      const wrapper = mount({ selected });
      wrapper.setState({ filteredKeys: data, expanded: true });

      const checkboxElement = wrapper.find(`.${styles.checked}`).first().parent();
      const key = checkboxElement.prop('data-key');

      const evt = mockEvent({ currentTarget: { dataset: { key } } });
      checkboxElement.props().onClick(evt);

      const remainingChecked = Object.values(wrapper.state('selectedKeys')).filter(v => v);

      expect(remainingChecked.length).toBe(selected.length - 1);
    });
  });

  describe('select all', () => {
    it('updates selected keys', () => {
      const wrapper = mount({ selected });
      wrapper.setState({ filteredKeys: data, data });

      const selectAllElement = wrapper.find('[data-checkbox="select-all"]');
      selectAllElement.simulate('click', { stopPropagation: () => {} });

      const checkedItems = wrapper.find(`.${styles.checked}`);
      let checkableItems = [];

      data.forEach((category) => {
        checkableItems = checkableItems.concat(category.children);
      });

      expect(checkedItems.length).toBe(checkableItems.length);
    });

    it('triggers analytics', () => {
      const onAnalytics = Sinon.spy();

      const wrapper = shallowRender({ onAnalytics, selected });
      wrapper.setState({ filteredKeys: data });

      const selectAllElement = wrapper.find(`.${styles.filterCheckboxControls}`);
      selectAllElement.simulate('click', { stopPropagation: () => {} });

      expect(onAnalytics.called).toBe(true);
    });
  });

  describe('on search', () => {
    it('hides matching values', () => {
      const wrapper = mount({ selected });
      wrapper.setState({ data });

      const inputElement = wrapper.find(`.${styles.filterCheckboxInput}`);
      inputElement.simulate('change', { target: { value: data[0].children[0].value } });

      const remainingElements = wrapper.find(`.${styles.checkboxRow}`);
      expect(remainingElements.length).toBe(1);
    });

    it('displays empty message if no matching values', () => {
      const wrapper = shallowRender({ selected });
      wrapper.setState({ data });

      const inputElement = wrapper.find(`.${styles.filterCheckboxInput}`);
      inputElement.simulate('change', { target: { value: 'bogus' } });

      const emptyElement = wrapper.find(`.${styles.emptyMessage}`);
      expect(emptyElement.exists()).toBe(true);
    });
  });

  describe('while loading', () => {
    const wrapper = shallowRender({ selected });
    wrapper.setState({ isLoading: true });

    it('show a loading icon', () => {
      expect(wrapper.find(`.${styles.tokenLoading}`).exists()).toBe(true);
    });

    it('does not show the caret', () => {
      expect(wrapper.find(`.${styles.tokenCaret}`).exists()).toBe(false);
    });
  });


  describe('renders an empty message', () => {
    it('if no items', () => {
      const wrapper = shallowRender({ selected });
      wrapper.setState({ data: [] });
      const emptyElement = wrapper.find(`.${styles.emptyMessage}`);
      expect(emptyElement.exists()).toBe(true);
    });

    it('unless there are items', () => {
      const wrapper = shallowRender({ selected });
      wrapper.setState({ filteredKeys: data });
      const emptyElement = wrapper.find(`.${styles.emptyMessage}`);
      expect(emptyElement.exists()).toBe(false);
    });
  });
});
