import { reactive, ref, nextTick } from 'vue';
import { mount } from '@vue/test-utils';
import EditableSelect from '../src/editable-select';
import { useNamespace } from '../../shared/hooks/use-namespace';

const ns = useNamespace('editable-select', true);
const inputClasses = useNamespace('editable-select-input', true);

const createData = (len = 5) => {
  return reactive(
    Array.from({ length: len }).map((_, index) => {
      return {
        label: `Option${index}`,
        value: index,
      };
    })
  );
};

jest.mock('../../locale/create', () => ({
  createI18nTranslate: () => jest.fn(),
}));

describe('Basic EditableSelect', () => {
  it('Should render basic editable select correctly.', async () => {
    const wrapper = mount({
      setup() {
        return () => <EditableSelect />;
      },
    });
    await nextTick();

    expect(wrapper.find(ns.b()).exists()).toBeTruthy();
    expect(wrapper.find(ns.e('arrow-icon')).isVisible()).toBeTruthy();
    expect(wrapper.find(ns.e('clear-icon')).isVisible()).toBeFalsy();
    expect(wrapper.find(ns.e('dropdown')).exists()).toBeFalsy();

    const input = wrapper.find(inputClasses.e('inner'));
    expect(input.attributes('placeholder')).toBe('Select');
    await wrapper.trigger('click');

    expect(wrapper.find(inputClasses.b()).classes()).toContain(inputClasses.m('open').slice(1));
    expect(wrapper.find(ns.e('dropdown')).exists()).toBeTruthy();

    wrapper.unmount();
  });

  it('Should render correct number of item and item content.', async () => {
    const data = createData();
    const wrapper = mount({
      setup() {
        return () => <EditableSelect options={data} />;
      },
    });

    await nextTick();

    await wrapper.trigger('click');

    const options = wrapper.findAll(ns.e('item'));
    expect(options).toHaveLength(data.length);

    const res = options.every((option, index) => {
      return option.text() === data[index].label;
    });

    expect(res).toBeTruthy();
    wrapper.unmount();
  });

  it('Should render correct default value.', async () => {
    const wrapper = mount({
      setup() {
        const value = ref(4);
        const data = createData();
        return () => {
          return <EditableSelect v-model={value.value} options={data} />;
        };
      },
    });

    await nextTick();
    expect((wrapper.find(inputClasses.e('inner')).element as HTMLInputElement).value).toBe('Option4');
  });

  it('The dropdown item should be highlighted when clicked.', async () => {
    const wrapper = mount({
      setup() {
        const data = createData();
        const value = ref(2);
        return () => {
          return <EditableSelect v-model={value.value} options={data} />;
        };
      },
    });
    await nextTick();

    await wrapper.trigger('click');
    await wrapper.findAll(ns.e('item'))[1].trigger('click');
    await wrapper.trigger('click');

    expect(wrapper.findAll(ns.e('item'))[1].classes()).toContain(ns.em('item', 'selected').slice(1));
    expect((wrapper.find(inputClasses.e('inner')).element as HTMLInputElement).value).toBe('Option1');

    wrapper.unmount();
  });

  it('keyboard operations', async () => {
    const wrapper = mount({
      setup() {
        const data = createData();
        const value = ref(1);
        return () => {
          return <EditableSelect v-model={value.value} options={data} />;
        };
      },
    });

    await nextTick();

    await wrapper.trigger('click');
    const input = wrapper.find(inputClasses.e('inner'));
    await input.element.dispatchEvent(new KeyboardEvent('keydown', { key: 'ArrowDown' }));
    expect(wrapper.findAll(ns.e('item'))[2].classes()).toContain(ns.em('item', 'hover').slice(1));
    await input.element.dispatchEvent(new KeyboardEvent('keydown', { key: 'ArrowUp' }));
    expect(wrapper.findAll(ns.e('item'))[1].classes()).toContain(ns.em('item', 'hover').slice(1));

    await input.element.dispatchEvent(new KeyboardEvent('keydown', { key: 'Enter' }));
    expect(wrapper.find('input').element.value).toBe('Option1');

    await input.element.dispatchEvent(new KeyboardEvent('keydown', { key: 'Escape' }));
    expect(wrapper.findComponent({ name: 'DEditableSelectDropdown' }).exists()).toBeFalsy();
    await input.element.dispatchEvent(new KeyboardEvent('keydown', { key: 'ArrowDown' }));
    expect(wrapper.findComponent({ name: 'DEditableSelectDropdown' }).exists()).toBeTruthy();

    await input.element.dispatchEvent(new KeyboardEvent('keydown', { key: 'Tab' }));
    expect(wrapper.findComponent({ name: 'DEditableSelectDropdown' }).exists()).toBeFalsy();

    wrapper.unmount();
  });
});