import { mount } from '@vue/test-utils';
import { asyncExpect, sleep } from '../../../tests/utils';
import Table from '..';
jest.mock('../../_util/Portal');
describe('Table.rowSelection', () => {
  const errorSpy = jest.spyOn(console, 'error').mockImplementation(() => {});

  afterEach(() => {
    errorSpy.mockReset();
    document.body.innerHTML = '';
  });

  afterAll(() => {
    errorSpy.mockRestore();
  });
  const columns = [
    {
      title: 'Name',
      dataIndex: 'name',
    },
  ];

  const data = [
    { key: 0, name: 'Jack' },
    { key: 1, name: 'Lucy' },
    { key: 2, name: 'Tom' },
    { key: 3, name: 'Jerry' },
  ];
  function getTableOptions(props = {}) {
    return {
      props: {
        columns,
        dataSource: data,
        rowSelection: {},
        ...props,
      },
      sync: false,
      attachedToDocument: true,
    };
  }
  function renderedNames(wrapper) {
    return wrapper.findAllComponents({ name: 'BodyRow' }).map(row => {
      return row.props().record.name;
    });
  }

  function getSelections(wrapper) {
    return [...wrapper.vm.table.selectedKeySet].sort();
  }

  xit('select by checkbox', async () => {
    const wrapper = mount(Table, getTableOptions());
    const checkboxes = wrapper.findAll('input');
    const checkboxAll = checkboxes[0];
    checkboxAll.element.checked = true;
    checkboxAll.trigger('change');
    await asyncExpect(() => {
      expect(getSelections(wrapper)).toEqual([0, 1, 2, 3]);
    });
    checkboxes[1].element.checked = false;
    checkboxes[1].trigger('change');
    await asyncExpect(() => {
      expect(getSelections(wrapper)).toEqual([1, 2, 3]);
    });
    checkboxes[1].element.checked = true;
    checkboxes[1].trigger('change');
    await asyncExpect(() => {
      expect(getSelections(wrapper)).toEqual([0, 1, 2, 3]);
    });
  });

  xit('select by radio', async () => {
    const wrapper = mount(Table, getTableOptions({ rowSelection: { type: 'radio' } }));
    const radios = wrapper.findAll('input');

    expect(radios.length).toBe(4);
    radios[0].element.checked = true;
    radios[0].trigger('change');
    await asyncExpect(() => {
      expect(getSelections(wrapper)).toEqual([0]);
    });
    radios[radios.length - 1].element.checked = true;
    radios[radios.length - 1].trigger('change');
    await asyncExpect(() => {
      expect(getSelections(wrapper)).toEqual([3]);
    });
  });

  it('pass getCheckboxProps to checkbox', async () => {
    const rowSelection = {
      getCheckboxProps: record => ({
        disabled: record.name === 'Lucy',
        name: record.name,
      }),
    };

    const wrapper = mount(Table, getTableOptions({ rowSelection }));
    const checkboxes = wrapper.findAll('input');
    await sleep();
    expect(checkboxes[1].wrapperElement.disabled).toBe(false);
    expect(checkboxes[1].wrapperElement.name).toEqual(data[0].name);
    expect(checkboxes[2].wrapperElement.disabled).toBe(true);
    expect(checkboxes[2].wrapperElement.name).toEqual(data[1].name);
  });

  it('works with pagination', async () => {
    const wrapper = mount(Table, getTableOptions({ pagination: { pageSize: 2 } }));

    await sleep();
    const checkboxAll = wrapper.find('input');
    checkboxAll.wrapperElement.checked = true;
    checkboxAll.trigger('change');
    const pagers = wrapper.findAllComponents({ name: 'Pager' });
    await asyncExpect(() => {
      expect(wrapper.findComponent({ name: 'ACheckbox' }).props()).toEqual(
        expect.objectContaining({ checked: true, indeterminate: false }),
      );
    });
    pagers[1].trigger('click');
    await asyncExpect(() => {
      expect(wrapper.findComponent({ name: 'ACheckbox' }).props()).toEqual(
        expect.objectContaining({ checked: false, indeterminate: false }),
      );
    });
    pagers[0].trigger('click');
    await asyncExpect(() => {
      expect(wrapper.findComponent({ name: 'ACheckbox' }).props()).toEqual(
        expect.objectContaining({ checked: true, indeterminate: false }),
      );
    });
  });

  // https://github.com/ant-design/ant-design/issues/4020
  it('handles defaultChecked', async () => {
    const rowSelection = {
      getCheckboxProps: record => {
        return {
          defaultChecked: record.key === 0,
        };
      },
    };

    mount(Table, getTableOptions({ rowSelection }));

    expect(errorSpy).toHaveBeenCalledWith(
      'Warning: [ant-design-vue: Table] Do not set `checked` or `defaultChecked` in `getCheckboxProps`. Please use `selectedRowKeys` instead.',
    );
  });

  xit('can be controlled', async () => {
    const wrapper = mount(Table, getTableOptions({ rowSelection: { selectedRowKeys: [0] } }));

    expect(getSelections(wrapper)).toEqual([0]);

    wrapper.setProps({ rowSelection: { selectedRowKeys: [1] } });
    await asyncExpect(() => {
      expect(getSelections(wrapper)).toEqual([1]);
    });
  });

  it('fires change & select events', async () => {
    const handleChange = jest.fn();
    const handleSelect = jest.fn();
    const rowSelection = {
      onChange: handleChange,
      onSelect: handleSelect,
    };
    const wrapper = mount(Table, getTableOptions({ rowSelection }));
    const checkboxs = wrapper.findAll('input');
    checkboxs[checkboxs.length - 1].element.checked = true;
    checkboxs[checkboxs.length - 1].trigger('change');
    await asyncExpect(() => {
      expect(handleChange).toBeCalledWith([3], [{ key: 3, name: 'Jerry' }]);
      expect(handleSelect.mock.calls.length).toBe(1);
      expect(handleSelect.mock.calls[0][0]).toEqual({ key: 3, name: 'Jerry' });
      expect(handleSelect.mock.calls[0][1]).toEqual(true);
      expect(handleSelect.mock.calls[0][2]).toEqual([{ key: 3, name: 'Jerry' }]);
    });
  });

  it('fires selectAll event', async () => {
    const handleSelectAll = jest.fn();
    const rowSelection = {
      onSelectAll: handleSelectAll,
    };
    const wrapper = mount(Table, getTableOptions({ rowSelection }));
    const checkboxs = wrapper.findAll('input');
    checkboxs[0].element.checked = true;
    checkboxs[0].trigger('change');
    await asyncExpect(() => {
      expect(handleSelectAll).toBeCalledWith(true, data, data);
    });
    checkboxs[0].element.checked = false;
    checkboxs[0].trigger('change');
    await asyncExpect(() => {
      expect(handleSelectAll).toBeCalledWith(false, [], data);
    });
  });

  it('render with default selection correctly', async () => {
    const rowSelection = {
      selections: true,
    };
    const wrapper = mount(Table, getTableOptions({ rowSelection }));
    const dropdownWrapper = mount(
      {
        render() {
          return wrapper.findComponent({ name: 'Trigger' }).vm.getComponent();
        },
      },
      { sync: false },
    );
    await asyncExpect(() => {
      expect(dropdownWrapper.html()).toMatchSnapshot();
    });
  });

  it('click select all selection', () => {
    const handleChange = jest.fn();
    const rowSelection = {
      onChange: handleChange,
      selections: true,
    };
    const wrapper = mount(Table, getTableOptions({ rowSelection }));

    const dropdownWrapper = mount(
      {
        render() {
          return wrapper.findComponent({ name: 'Trigger' }).vm.getComponent();
        },
      },
      { sync: false },
    );
    dropdownWrapper.findAll('.ant-dropdown-menu-item')[0].trigger('click');

    expect(handleChange.mock.calls[0][0]).toEqual([0, 1, 2, 3]);
  });

  it('fires selectInvert event', async () => {
    const handleSelectInvert = jest.fn();
    const rowSelection = {
      onSelectInvert: handleSelectInvert,
      selections: true,
    };
    const wrapper = mount(Table, getTableOptions({ rowSelection }));
    const checkboxes = wrapper.findAll('input');
    checkboxes[1].element.checked = true;
    checkboxes[1].trigger('change');
    await sleep();
    const dropdownWrapper = mount(
      {
        render() {
          return wrapper.findComponent({ name: 'Trigger' }).vm.getComponent();
        },
      },
      { sync: false },
    );
    const div = dropdownWrapper.findAll('li.ant-dropdown-menu-item');
    div[1].trigger('click');

    expect(handleSelectInvert).toBeCalledWith([1, 2, 3]);
  });

  it('fires selection event', async () => {
    const handleSelectOdd = jest.fn();
    const handleSelectEven = jest.fn();
    const rowSelection = {
      selections: [
        Table.SELECTION_ALL,
        Table.SELECTION_INVERT,
        {
          key: 'odd',
          text: '奇数项',
          onSelect: handleSelectOdd,
        },
        {
          key: 'even',
          text: '偶数项',
          onSelect: handleSelectEven,
        },
      ],
    };
    const wrapper = mount(Table, getTableOptions({ rowSelection }));

    const dropdownWrapper = mount(
      {
        render() {
          return wrapper.findComponent({ name: 'Trigger' }).vm.getComponent();
        },
      },
      { sync: false },
    );
    await sleep();
    expect(dropdownWrapper.findAll('.ant-dropdown-menu-item').length).toBe(4);

    dropdownWrapper.findAll('.ant-dropdown-menu-item')[2].trigger('click');
    expect(handleSelectOdd).toBeCalledWith([0, 1, 2, 3]);

    dropdownWrapper.findAll('.ant-dropdown-menu-item')[3].trigger('click');
    expect(handleSelectEven).toBeCalledWith([0, 1, 2, 3]);
  });

  it('could hide default selection options', () => {
    const rowSelection = {
      hideDefaultSelections: true,
      selections: [
        {
          key: 'odd',
          text: '奇数项',
        },
        {
          key: 'even',
          text: '偶数项',
        },
      ],
    };
    const wrapper = mount(Table, getTableOptions({ rowSelection }));
    const dropdownWrapper = mount(
      {
        render() {
          return wrapper.findComponent({ name: 'Trigger' }).vm.getComponent();
        },
      },
      { sync: false },
    );
    expect(dropdownWrapper.findAll('.ant-dropdown-menu-item').length).toBe(2);
  });

  it('handle custom selection onSelect correctly when hide default selection options', () => {
    const handleSelectOdd = jest.fn();
    const handleSelectEven = jest.fn();
    const rowSelection = {
      hideDefaultSelections: true,
      selections: [
        {
          key: 'odd',
          text: '奇数项',
          onSelect: handleSelectOdd,
        },
        {
          key: 'even',
          text: '偶数项',
          onSelect: handleSelectEven,
        },
      ],
    };
    const wrapper = mount(Table, getTableOptions({ rowSelection }));

    const dropdownWrapper = mount(
      {
        render() {
          return wrapper.findComponent({ name: 'Trigger' }).vm.getComponent();
        },
      },
      { sync: false },
    );
    expect(dropdownWrapper.findAll('.ant-dropdown-menu-item').length).toBe(2);

    dropdownWrapper.findAll('.ant-dropdown-menu-item')[0].trigger('click');
    expect(handleSelectOdd).toBeCalledWith([0, 1, 2, 3]);

    dropdownWrapper.findAll('.ant-dropdown-menu-item')[1].trigger('click');
    expect(handleSelectEven).toBeCalledWith([0, 1, 2, 3]);
  });

  // https:// github.com/ant-design/ant-design/issues/4245
  it('handles disabled checkbox correctly when dataSource changes', async () => {
    const rowSelection = {
      getCheckboxProps: record => {
        return { disabled: record.disabled };
      },
    };
    const wrapper = mount(Table, getTableOptions({ rowSelection }));
    const newData = [
      { key: 7, name: 'Jack', disabled: true },
      { key: 8, name: 'Lucy', disabled: true },
    ];
    await asyncExpect(() => {
      wrapper.setProps({ dataSource: newData });
    });
    await asyncExpect(() => {
      wrapper.findAll('input').forEach(checkbox => {
        expect(checkbox.wrapperElement.disabled).toBe(true);
      });
    });
  });

  // https://github.com/ant-design/ant-design/issues/4779
  it('should not switch pagination when select record', async () => {
    const newData = [];
    for (let i = 0; i < 20; i += 1) {
      newData.push({
        key: i.toString(),
        name: i.toString(),
      });
    }
    const wrapper = mount(
      Table,
      getTableOptions({
        rowSelection: {},
        dataSource: newData,
      }),
    );
    await sleep();
    const pager = wrapper.findAllComponents({ name: 'Pager' });
    pager[pager.length - 1].trigger('click'); // switch to second page
    wrapper.findAll('input')[0].element.checked = true;
    wrapper.findAll('input')[0].trigger('change');
    await asyncExpect(() => {
      expect(renderedNames(wrapper)).toEqual([
        '10',
        '11',
        '12',
        '13',
        '14',
        '15',
        '16',
        '17',
        '18',
        '19',
      ]);
    });
  });

  it('highlight selected row', async () => {
    const wrapper = mount(Table, getTableOptions());
    wrapper.findAll('input')[1].element.checked = true;
    wrapper.findAll('input')[1].trigger('change');
    await asyncExpect(() => {
      expect(wrapper.findAll('tbody tr')[0].classes()).toContain('ant-table-row-selected');
    });
  });

  it('fix selection column on the left', async () => {
    const wrapper = mount(
      Table,
      getTableOptions({
        rowSelection: { fixed: true },
      }),
    );
    await asyncExpect(() => {
      expect(wrapper.html()).toMatchSnapshot();
    });
  });

  // https://github.com/ant-design/ant-design/issues/10629
  it('should keep all checked state when remove item from dataSource', async () => {
    const wrapper = mount(Table, {
      props: {
        columns,
        dataSource: data,
        rowSelection: {
          selectedRowKeys: [0, 1, 2, 3],
        },
      },
      sync: false,
    });
    await asyncExpect(() => {
      expect(wrapper.findAllComponents({ name: 'ACheckbox' }).length).toBe(5);
      const allCheckbox = wrapper.findAllComponents({ name: 'ACheckbox' });
      Array(allCheckbox.length).forEach((_, index) => {
        const checkbox = allCheckbox[index];
        expect(checkbox.vm.checked).toBe(true);
        expect(checkbox.vm.indeterminate).toBe(false);
      });
      wrapper.setProps({
        dataSource: data.slice(1),
        rowSelection: {
          selectedRowKeys: [1, 2, 3],
        },
      });
    });
    await asyncExpect(() => {
      expect(wrapper.findAllComponents({ name: 'ACheckbox' }).length).toBe(4);
      const allCheckbox = wrapper.findAllComponents({ name: 'ACheckbox' });
      Array(allCheckbox.length).forEach((_, index) => {
        const checkbox = allCheckbox[index];
        expect(checkbox.vm.checked).toBe(true);
        expect(checkbox.vm.indeterminate).toBe(false);
      });
    });
  });

  // https://github.com/ant-design/ant-design/issues/11042
  it('add columnTitle for rowSelection', async () => {
    const wrapper = mount(Table, {
      props: {
        columns,
        dataSource: data,
        rowSelection: {
          columnTitle: '多选',
        },
      },
      sync: false,
    });
    await asyncExpect(() => {
      expect(wrapper.findAll('thead tr th')[0].text()).toBe('多选');
    });
    await asyncExpect(() => {
      wrapper.setProps({
        rowSelection: {
          type: 'radio',
          columnTitle: '单选',
        },
      });
    });
    await asyncExpect(() => {
      expect(wrapper.findAll('thead tr th')[0].text()).toBe('单选');
    });
  });

  // https://github.com/ant-design/ant-design/issues/11384
  it('should keep item even if in filter', async () => {
    const filterColumns = [
      {
        title: 'Name',
        dataIndex: 'name',
        filters: [
          {
            text: 'Jack',
            value: 'Jack',
          },
          {
            text: 'Lucy',
            value: 'Lucy',
          },
        ],
        filterDropdownVisible: true,
        onFilter: (value, record) => record.name.indexOf(value) === 0,
      },
    ];

    const onChange = jest.fn();
    const rowSelection = {
      onChange,
    };

    const wrapper = mount(Table, {
      props: {
        columns: filterColumns,
        dataSource: data,
        rowSelection,
      },
      sync: false,
    });

    const dropdownWrapper = mount(
      {
        render() {
          return wrapper.findComponent({ name: 'Trigger' }).vm.getComponent();
        },
      },
      { sync: false },
    );
    await sleep();
    function clickFilter(indexList) {
      indexList.forEach(index => {
        dropdownWrapper
          .findAll('.ant-dropdown-menu-item .ant-checkbox-wrapper')
          [index].trigger('click');
      });
      dropdownWrapper.find('.ant-table-filter-dropdown-btns .ant-btn-primary').trigger('click');
    }

    function clickItem() {
      wrapper.findAll(
        'tbody .ant-table-selection-column .ant-checkbox-input',
      )[0].element.checked = true;
      wrapper.findAll('tbody .ant-table-selection-column .ant-checkbox-input')[0].trigger('change');
    }

    // Check Jack
    clickFilter([0]);
    await asyncExpect(() => {
      expect(wrapper.findAll('tbody tr').length).toBe(1);
    });
    await asyncExpect(() => {
      clickItem();
    });
    await asyncExpect(() => {
      expect(onChange.mock.calls[0][0].length).toBe(1);
      expect(onChange.mock.calls[0][1].length).toBe(1);
    });

    // Check Lucy
    clickFilter([0]);
    await sleep();
    clickFilter([1]);
    await asyncExpect(() => {
      expect(wrapper.findAll('tbody tr').length).toBe(1);
    });
    await asyncExpect(() => {
      clickItem();
    });
    await asyncExpect(() => {
      expect(onChange.mock.calls[1][0].length).toBe(2);
      expect(onChange.mock.calls[1][1].length).toBe(2);
    });
  });

  it('render correctly when set childrenColumnName', async () => {
    const newDatas = [
      {
        key: 1,
        name: 'Jack',
        children: [
          {
            key: 11,
            name: 'John Brown',
          },
        ],
      },
      {
        key: 2,
        name: 'Lucy',
        children: [
          {
            key: 21,
            name: 'Lucy Brown',
          },
        ],
      },
    ];

    const wrapper = mount(Table, {
      props: {
        columns,
        dataSource: newDatas,
        rowSelection: {},
        childrenColumnName: 'test',
      },
      sync: false,
    });

    const checkboxes = wrapper.findAll('input');
    checkboxes[1].element.checked = true;
    checkboxes[1].trigger('change');
    await sleep();
    expect(wrapper.findComponent({ name: 'ACheckbox' }).props()).toEqual(
      expect.objectContaining({ checked: false, indeterminate: true }),
    );
    checkboxes[2].element.checked = true;
    checkboxes[2].trigger('change');
    await asyncExpect(() => {
      expect(wrapper.findComponent({ name: 'ACheckbox' }).props()).toEqual(
        expect.objectContaining({ checked: true, indeterminate: false }),
      );
    });
  });
});