import { flushPromises, mount, type VueWrapper } from '@vue/test-utils';
import Antd, { Select } from 'ant-design-vue';
import { afterEach, beforeAll, describe, expect, it } from 'vitest';

import DropdownOptions from '../../src/components/DropdownOptions.vue';
import type { DropdownOption } from '../../src/domain/types';

beforeAll(() => {
  window.matchMedia ??= () =>
    ({
      addEventListener() {},
      addListener() {},
      dispatchEvent: () => false,
      matches: false,
      media: '',
      onchange: null,
      removeEventListener() {},
      removeListener() {},
    }) as MediaQueryList;

  globalThis.ResizeObserver ??= class ResizeObserver {
    disconnect() {}
    observe() {}
    unobserve() {}
  };
});

function mountOptions(options: DropdownOption[], defaultValue = '', disabled = false, errorField?: 'dropdown-default' | 'dropdown-options', errorId?: string) {
  const wrapper = mount(DropdownOptions, {
    props: { defaultValue, disabled, errorField, errorId, options },
    attachTo: document.body,
    global: { plugins: [Antd] },
  });
  mountedWrappers.push(wrapper);
  return wrapper;
}

const mountedWrappers: VueWrapper[] = [];

afterEach(() => {
  mountedWrappers.splice(0).forEach((wrapper) => wrapper.unmount());
  document.body.replaceChildren();
});

describe('DropdownOptions', () => {
  it('两项时禁用删除，添加第三项后允许删除且不修改 props', async () => {
    const options = [
      { display: '', value: '' },
      { display: '', value: '' },
    ];
    const wrapper = mountOptions(options);

    expect(wrapper.findAll('[data-test^="remove-option-"]')).toHaveLength(2);
    expect(wrapper.findAll('[data-test^="remove-option-"]').every((button) => button.attributes('disabled') !== undefined)).toBe(true);

    await wrapper.get('[data-test="add-option"]').trigger('click');
    const added = wrapper.emitted<DropdownOption[][]>('update:options')?.at(-1)?.[0];
    expect(added).toEqual([...options, { display: '', value: '' }]);
    expect(options).toHaveLength(2);

    await wrapper.setProps({ options: added });
    expect(wrapper.findAll('[data-test^="remove-option-"]').every((button) => button.attributes('disabled') === undefined)).toBe(true);
    expect(wrapper.get('[data-test="option-display-0"]').attributes('aria-label')).toBe('第1项显示名称');
    expect(wrapper.get('[data-test="option-value-1"]').attributes('aria-label')).toBe('第2项业务值');
    expect(wrapper.get('[data-test="remove-option-2"]').attributes('aria-label')).toBe('删除第3项');
    await wrapper.get('[data-test="remove-option-2"]').trigger('click');
    expect(wrapper.emitted<DropdownOption[][]>('update:options')?.at(-1)?.[0]).toEqual(options);
  });

  it('编辑选项时发送新数组，并在默认业务值失效时清空默认项', async () => {
    const options = [
      { display: '甲', value: 'a' },
      { display: '乙', value: 'b' },
    ];
    const wrapper = mountOptions(options, 'a');

    await wrapper.get('[data-test="option-display-0"]').setValue('甲项');
    const displayUpdated = wrapper.emitted<DropdownOption[][]>('update:options')?.at(-1)?.[0];
    expect(displayUpdated).toEqual([
      { display: '甲项', value: 'a' },
      { display: '乙', value: 'b' },
    ]);
    expect(options[0]?.display).toBe('甲');

    await wrapper.setProps({ options: displayUpdated });
    await wrapper.get('[data-test="option-value-0"]').setValue('changed');
    expect(wrapper.emitted<DropdownOption[][]>('update:options')?.at(-1)?.[0]).toEqual([
      { display: '甲项', value: 'changed' },
      { display: '乙', value: 'b' },
    ]);
    expect(wrapper.emitted('update:defaultValue')?.at(-1)).toEqual(['']);
  });

  it('删除当前默认项时清空默认值，默认下拉只列出完整选项', async () => {
    const options = [
      { display: '甲', value: 'a' },
      { display: '', value: 'incomplete' },
      { display: '丙', value: 'c' },
    ];
    const wrapper = mountOptions(options, 'c');
    const select = wrapper.findComponent(Select);

    expect(select.props('options')).toEqual([
      { label: '无默认项', value: '' },
      { label: '甲', value: 'a' },
      { label: '丙', value: 'c' },
    ]);

    await wrapper.get('[data-test="remove-option-2"]').trigger('click');
    expect(wrapper.emitted('update:defaultValue')?.at(-1)).toEqual(['']);
  });

  it('通过默认下拉发送业务值，并把弹层挂到组件根元素', async () => {
    const wrapper = mountOptions([
      { display: '甲', value: 'a' },
      { display: '乙', value: 'b' },
    ]);
    const select = wrapper.findComponent(Select);

    expect(wrapper.get('[data-test="option-display-0"]').attributes('id')).toBe('dropdown-option-display-0');
    expect(wrapper.get('[data-test="option-value-0"]').attributes('id')).toBe('dropdown-option-value-0');
    expect(select.attributes('aria-label')).toBe('默认项');
    expect(select.get('input').attributes('id')).toBe('dropdown-default');
    expect(wrapper.get('label.default-label').attributes('for')).toBe('dropdown-default');
    expect(wrapper.get('[data-test="option-display-0"]').attributes('aria-invalid')).toBeUndefined();
    expect(select.attributes('aria-invalid')).toBeUndefined();
    select.vm.$emit('update:value', 'b');
    await wrapper.vm.$nextTick();
    expect(wrapper.emitted('update:defaultValue')?.at(-1)).toEqual(['b']);
    expect(select.props('getPopupContainer')?.(document.body)).toBe(wrapper.get('[data-test="dropdown-options"]').element);
  });

  it('按错误字段关联首个选项输入或默认项 Select', () => {
    const options = [
      { display: '甲', value: 'a' },
      { display: '乙', value: 'b' },
    ];
    const optionsWrapper = mountOptions(options, '', false, 'dropdown-options', 'dropdown-options-error');
    expect(optionsWrapper.get('[data-test="option-display-0"]').attributes()).toMatchObject({
      'aria-describedby': 'dropdown-options-error',
      'aria-invalid': 'true',
    });
    expect(optionsWrapper.get('[data-test="option-display-1"]').attributes('aria-describedby')).toBeUndefined();

    const defaultWrapper = mountOptions(options, 'unknown', false, 'dropdown-default', 'dropdown-default-error');
    expect(defaultWrapper.findComponent(Select).attributes()).toMatchObject({
      'aria-describedby': 'dropdown-default-error',
      'aria-invalid': 'true',
    });
    expect(defaultWrapper.get('[data-test="option-display-0"]').attributes('aria-invalid')).toBeUndefined();
  });

  it('真实打开默认项下拉并点击选项更新业务值', async () => {
    const wrapper = mountOptions([
      { display: '甲', value: 'a' },
      { display: '乙', value: 'b' },
    ]);
    const root = wrapper.get('[data-test="dropdown-options"]');

    const selector = wrapper.get('[data-test="dropdown-default"] .ant-select-selector');
    await selector.trigger('mousedown');
    await selector.trigger('click');
    await flushPromises();
    const dropdown = root.get('.ant-select-dropdown');
    expect(root.element.contains(dropdown.element)).toBe(true);
    const option = dropdown.findAll('.ant-select-item-option').find((item) => item.text() === '乙')!;
    await option.trigger('click');
    await flushPromises();

    expect(wrapper.emitted('update:defaultValue')?.at(-1)).toEqual(['b']);
  });

  it('disabled 时禁止编辑、添加、删除和选择默认项', async () => {
    const wrapper = mountOptions(
      [
        { display: '甲', value: 'a' },
        { display: '乙', value: 'b' },
        { display: '丙', value: 'c' },
      ],
      '',
      true,
    );

    expect(wrapper.get('[data-test="option-display-0"]').attributes('disabled')).toBeDefined();
    expect(wrapper.get('[data-test="option-value-0"]').attributes('disabled')).toBeDefined();
    expect(wrapper.get('[data-test="add-option"]').attributes('disabled')).toBeDefined();
    expect(wrapper.get('[data-test="remove-option-2"]').attributes('disabled')).toBeDefined();
    expect(wrapper.findComponent(Select).props('disabled')).toBe(true);
    wrapper.findComponent(Select).vm.$emit('update:value', 'b');
    await wrapper.vm.$nextTick();
    expect(wrapper.emitted('update:defaultValue')).toBeUndefined();
  });
});
