/* @vitest-environment happy-dom */
/* eslint-disable vue/one-component-per-file */

import type { VueWrapper } from '@vue/test-utils';

import { mount } from '@vue/test-utils';
import { defineComponent, h, nextTick, onMounted } from 'vue';

import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';

import GlobalDmsFolderSelector from '../index.vue';

const mocks = vi.hoisted(() => ({
  closeModal: vi.fn(),
  error: vi.fn(),
  getDmsFolderTree: vi.fn(),
  openModal: vi.fn(),
  register: vi.fn(),
}));

vi.mock('#/router', () => ({ router: { push: vi.fn() } }));
vi.mock('#/api/request', () => ({ defHttp: { get: vi.fn() } }));
vi.mock('#/api/x/dms/fileCenter', () => ({ getDmsFolderTree: mocks.getDmsFolderTree }));
vi.mock('@jnpf/hooks', () => ({ useMessage: () => ({ createMessage: { error: mocks.error } }) }));

vi.mock('@jnpf/ui/modal', () => ({
  BasicModal: defineComponent({
    props: { okButtonProps: { default: () => ({}), type: Object } },
    emits: ['ok', 'register'],
    setup(props, { emit, slots }) {
      onMounted(() => emit('register'));
      return () =>
        h('section', [slots.default?.(), h('button', { 'data-test': 'confirm', disabled: props.okButtonProps?.disabled, onClick: () => emit('ok') }, '确定')]);
    },
  }),
  useModal: () => [mocks.register, { closeModal: mocks.closeModal, openModal: mocks.openModal }],
}));

vi.mock('ant-design-vue', () => ({
  Tree: defineComponent({
    props: { treeData: { required: true, type: Array } },
    emits: ['select'],
    setup(props, { emit }) {
      return () =>
        h(
          'div',
          (props.treeData as any[]).map((node) => h('button', { 'data-test': `folder-${node.key}`, onClick: () => emit('select', [node.key]) }, node.title)),
        );
    },
  }),
}));

const { onlineUtils } = await import('#/utils/jnpf');

let wrapper: VueWrapper<any>;

async function flush(times = 3) {
  for (let index = 0; index < times; index++) await nextTick();
}

describe('globalDmsFolderSelector', () => {
  beforeEach(() => {
    mocks.closeModal.mockReset();
    mocks.error.mockReset();
    mocks.getDmsFolderTree.mockReset();
    mocks.openModal.mockReset();
    mocks.getDmsFolderTree.mockResolvedValue({
      data: {
        hasMore: false,
        list: [{ hasChildren: false, id: 'folder_001', name: '质量管理部' }],
      },
    });
    wrapper = mount(GlobalDmsFolderSelector, { global: { directives: { loading: {} } } });
  });

  afterEach(() => {
    wrapper.unmount();
  });

  it('selects a folder and invokes onConfirm when the modal confirm action is clicked', async () => {
    const onConfirm = vi.fn();

    onlineUtils.openDmsFolderSelector(undefined, onConfirm);
    await flush();
    await wrapper.get('[data-test="folder-folder_001"]').trigger('click');
    await wrapper.get('[data-test="confirm"]').trigger('click');

    expect(onConfirm).toHaveBeenCalledWith('folder_001', '质量管理部');
    expect(mocks.closeModal).toHaveBeenCalledTimes(1);
  });

  it('renders the modal and root directories while locating a nested initial folder', async () => {
    mocks.getDmsFolderTree
      .mockResolvedValueOnce({
        data: {
          hasMore: false,
          list: [{ hasChildren: true, id: 'folder_001', name: '根目录' }],
        },
      })
      .mockImplementationOnce(() => new Promise(() => {}));

    onlineUtils.openDmsFolderSelector('folder_999');
    await flush();

    expect(mocks.openModal).toHaveBeenCalledWith(true);
    expect(wrapper.get('[data-test="folder-folder_001"]').text()).toBe('根目录');
  });

  it('returns a nested folder path without spaces around separators', async () => {
    const onConfirm = vi.fn();
    mocks.getDmsFolderTree
      .mockResolvedValueOnce({
        data: {
          hasMore: false,
          list: [{ hasChildren: true, id: 'folder_001', name: '根目录' }],
        },
      })
      .mockResolvedValueOnce({
        data: {
          hasMore: false,
          list: [{ hasChildren: true, id: 'folder_002', name: 'A' }],
        },
      })
      .mockResolvedValueOnce({
        data: {
          hasMore: false,
          list: [{ hasChildren: false, id: 'folder_003', name: 'B' }],
        },
      });

    onlineUtils.openDmsFolderSelector('folder_003', onConfirm);
    await vi.waitFor(() => expect(wrapper.get('[data-test="confirm"]').attributes('disabled')).toBeUndefined());
    await wrapper.get('[data-test="confirm"]').trigger('click');

    expect(onConfirm).toHaveBeenCalledWith('folder_003', '根目录/A/B');
  });

  it('closes the current selector before a confirmation callback opens another selector', async () => {
    const onConfirm = vi.fn(() => onlineUtils.openDmsFolderSelector());

    onlineUtils.openDmsFolderSelector({ onConfirm });
    await flush();
    await wrapper.get('[data-test="folder-folder_001"]').trigger('click');
    await wrapper.get('[data-test="confirm"]').trigger('click');
    await vi.waitFor(() => expect(mocks.openModal).toHaveBeenCalledTimes(2));

    expect(onConfirm).toHaveBeenCalledOnce();
    expect(mocks.openModal).toHaveBeenCalledTimes(2);
    expect(mocks.closeModal).toHaveBeenCalledTimes(1);
    expect(mocks.closeModal.mock.invocationCallOrder[0]).toBeLessThan(mocks.openModal.mock.invocationCallOrder[1]!);
  });
});
