/* @vitest-environment happy-dom */

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

import { mount } from '@vue/test-utils';
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
import { defineComponent, getCurrentInstance, h, inject, nextTick, provide } from 'vue';

const modalMocks = vi.hoisted(() => ({
  closeModal: vi.fn(),
  createConfirm: vi.fn(),
  openModal: vi.fn(),
  print: vi.fn(),
  register: vi.fn(),
  reload: vi.fn(),
}));

vi.mock('#/router', () => ({
  router: {
    push: vi.fn(),
  },
}));

vi.mock('@jnpf/hooks', () => ({
  useGlobSetting: () => ({ apiURL: '' }),
  useMessage: () => ({
    createConfirm: modalMocks.createConfirm,
  }),
}));

vi.mock('@jnpf/ui/modal', async () => {
  const { defineComponent, h, onMounted } = await import('vue');

  return {
    BasicModal: defineComponent({
      name: 'BasicModal',
      emits: ['register'],
      props: {
        closeFunc: Function,
      },
      setup(_, { emit, slots }) {
        onMounted(() => emit('register'));

        return () =>
          h('section', { 'data-test': 'basic-modal' }, [
            h('header', { 'data-test': 'modal-title' }, slots.title?.()),
            h('main', { 'data-test': 'modal-content' }, slots.default?.()),
          ]);
      },
    }),
    useModal: () => [modalMocks.register, { closeModal: modalMocks.closeModal, openModal: modalMocks.openModal }],
  };
});

vi.mock('ant-design-vue', async () => {
  const { defineComponent, h } = await import('vue');

  return {
    Button: defineComponent({
      name: 'AButton',
      props: {
        danger: Boolean,
        disabled: Boolean,
        loading: Boolean,
        type: String,
      },
      emits: ['click'],
      setup(props, { emit, slots }) {
        return () =>
          h(
            'button',
            {
              disabled: props.disabled || props.loading,
              type: 'button',
              onClick: () => emit('click'),
            },
            slots.default?.(),
          );
      },
    }),
    Space: defineComponent({
      name: 'ASpace',
      setup(_, { slots }) {
        return () => h('div', { 'data-test': 'space' }, slots.default?.());
      },
    }),
    Spin: defineComponent({
      name: 'ASpin',
      setup(_, { slots }) {
        return () => h('div', slots.default?.());
      },
    }),
  };
});

vi.mock('#/components/CustomPrint/CustomPrintPreviewPane.vue', () => ({
  default: defineComponent({
    name: 'CustomPrintPreviewPane',
    emits: ['error', 'loaded', 'loading'],
    props: {
      config: {
        required: true,
        type: Object,
      },
    },
    setup(_, { expose }) {
      expose({ print: modalMocks.print, reload: modalMocks.reload });

      return () => h('div', { 'data-test': 'custom-print-preview' });
    },
  }),
}));

vi.mock('../ReportAttachmentsPane.vue', () => ({
  default: defineComponent({
    name: 'ReportAttachmentsPane',
    setup() {
      return () => h('div', { 'data-test': 'attachments-pane' });
    },
  }),
}));

vi.mock('../ReportFormDetailPane.vue', () => ({
  default: defineComponent({
    name: 'ReportFormDetailPane',
    props: {
      tab: {
        required: true,
        type: Object,
      },
    },
    setup(props) {
      return () => h('div', { 'data-test': 'form-detail-pane' }, props.tab.id);
    },
  }),
}));

import { onlineUtils } from '#/utils/jnpf';

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

const customPrintTab = {
  key: 'customPrint',
  print: {
    data: { id: 'record-id' },
    template: 'template-id',
  },
} as const;

function mountModal() {
  return mount(GlobalReportViewModal, {
    global: {
      components: {
        'a-tab-pane': defineComponent({
          name: 'ATabPane',
          props: {
            tab: String,
          },
          setup(props) {
            const instance = getCurrentInstance();
            const setActiveKey = inject<(key: string) => void>('setActiveKey');

            return () =>
              h(
                'div',
                {
                  'data-test': 'tab-pane',
                  onClick: () => setActiveKey?.(String(instance?.vnode.key)),
                },
                props.tab,
              );
          },
        }),
        'a-tabs': defineComponent({
          name: 'ATabs',
          emits: ['update:activeKey'],
          props: {
            activeKey: String,
          },
          setup(_, { emit, slots }) {
            provide('setActiveKey', (key: string) => emit('update:activeKey', key));

            return () => h('div', { 'data-test': 'tabs' }, slots.default?.());
          },
        }),
      },
    },
  });
}

async function openReportView(config = {}) {
  onlineUtils.openReportView({
    tabs: [customPrintTab],
    title: '查看报告',
    ...config,
  });

  await nextTick();
  await nextTick();
}

async function emitPrintLoaded(wrapper: VueWrapper) {
  wrapper.findComponent({ name: 'CustomPrintPreviewPane' }).vm.$emit('loaded');
  await nextTick();
}

function getButton(wrapper: VueWrapper, text: string) {
  const button = wrapper.findAll('button').find(item => item.text() === text);

  expect(button, `button "${text}" exists`).toBeTruthy();
  return button!;
}

function queryButton(wrapper: VueWrapper, text: string) {
  return wrapper.findAll('button').find(item => item.text() === text);
}

function getFormDetailText(wrapper: VueWrapper) {
  return wrapper.find('[data-test="form-detail-pane"]').text();
}

describe('GlobalReportViewModal interactions', () => {
  let wrapper: VueWrapper<Component> | null = null;

  beforeEach(() => {
    modalMocks.closeModal.mockClear();
    modalMocks.createConfirm.mockClear();
    modalMocks.openModal.mockClear();
    modalMocks.print.mockClear();
    modalMocks.register.mockClear();
    modalMocks.reload.mockClear();
    wrapper = mountModal();
  });

  afterEach(() => {
    wrapper?.unmount();
    wrapper = null;
    vi.clearAllMocks();
  });

  it('无 actions 时显示默认打印按钮和关闭按钮', async () => {
    await openReportView();
    await emitPrintLoaded(wrapper!);

    expect(getButton(wrapper!, '导出PDF').attributes('disabled')).toBeUndefined();
    expect(getButton(wrapper!, '打印').attributes('disabled')).toBeUndefined();
    expect(getButton(wrapper!, '关闭').exists()).toBe(true);
  });

  it('有 actions 时显示业务动作且不显示默认打印按钮', async () => {
    await openReportView({
      actions: [
        {
          key: 'approve',
          label: '审核通过',
          onClick: vi.fn(),
          type: 'primary',
        },
      ],
    });

    expect(getButton(wrapper!, '审核通过').exists()).toBe(true);
    expect(queryButton(wrapper!, '导出PDF')).toBeUndefined();
    expect(queryButton(wrapper!, '打印')).toBeUndefined();
    expect(getButton(wrapper!, '关闭').exists()).toBe(true);
  });

  it('actions 为空数组时隐藏默认打印按钮', async () => {
    await openReportView({
      actions: [],
    });

    expect(queryButton(wrapper!, '导出PDF')).toBeUndefined();
    expect(queryButton(wrapper!, '打印')).toBeUndefined();
    expect(getButton(wrapper!, '关闭').exists()).toBe(true);
  });

  it('action 没有 confirmText 时点击后直接执行 action 且不弹确认', async () => {
    const onClick = vi.fn();

    await openReportView({
      actions: [
        {
          key: 'approve',
          label: '审核通过',
          onClick,
        },
      ],
    });

    await getButton(wrapper!, '审核通过').trigger('click');

    expect(modalMocks.createConfirm).not.toHaveBeenCalled();
    expect(onClick).toHaveBeenCalledTimes(1);
  });

  it('action 有 confirmText 时点击后弹出确认且不直接执行 action', async () => {
    const onClick = vi.fn();

    await openReportView({
      actions: [
        {
          confirmText: '确认审核通过吗？',
          key: 'approve',
          label: '审核通过',
          onClick,
        },
      ],
    });

    await getButton(wrapper!, '审核通过').trigger('click');

    expect(modalMocks.createConfirm).toHaveBeenCalledTimes(1);
    expect(modalMocks.createConfirm).toHaveBeenCalledWith(
      expect.objectContaining({
        content: '确认审核通过吗？',
      }),
    );
    expect(onClick).not.toHaveBeenCalled();

    const confirmOptions = modalMocks.createConfirm.mock.calls[0]?.[0] as { onOk: () => void } | undefined;
    expect(confirmOptions).toBeTruthy();
    confirmOptions!.onOk();
    await nextTick();

    expect(onClick).toHaveBeenCalledTimes(1);
  });

  it('点击打印和导出PDF时调用打印预览 expose 的 print', async () => {
    await openReportView();
    await emitPrintLoaded(wrapper!);

    await getButton(wrapper!, '打印').trigger('click');
    expect(modalMocks.print).toHaveBeenCalledTimes(1);

    await getButton(wrapper!, '导出PDF').trigger('click');
    expect(modalMocks.print).toHaveBeenCalledTimes(2);
  });

  it('onlineUtils.closeReportView 连续触发时只关闭一次并只调用一次 onClose', async () => {
    const onClose = vi.fn();

    await openReportView({ onClose });

    onlineUtils.closeReportView();
    onlineUtils.closeReportView();

    expect(modalMocks.closeModal).toHaveBeenCalledTimes(1);
    expect(onClose).toHaveBeenCalledTimes(1);
  });

  it('点击关闭按钮后再次触发 closeReportView 仍只关闭一次', async () => {
    const onClose = vi.fn();

    await openReportView({ onClose });

    await getButton(wrapper!, '关闭').trigger('click');
    onlineUtils.closeReportView();

    expect(modalMocks.closeModal).toHaveBeenCalledTimes(1);
    expect(onClose).toHaveBeenCalledTimes(1);
  });

  it('unmount 后清理 report view 事件监听', async () => {
    await openReportView();

    expect(modalMocks.openModal).toHaveBeenCalledTimes(1);

    wrapper!.unmount();
    wrapper = null;

    onlineUtils.openReportView({
      tabs: [customPrintTab],
      title: '卸载后打开',
    });
    onlineUtils.closeReportView();

    await nextTick();
    await nextTick();

    expect(modalMocks.openModal).toHaveBeenCalledTimes(1);
    expect(modalMocks.closeModal).not.toHaveBeenCalled();
  });

  it('loaded 前打印按钮 disabled，loaded 后启用', async () => {
    await openReportView();

    expect(getButton(wrapper!, '打印').attributes('disabled')).toBeDefined();

    await emitPrintLoaded(wrapper!);

    expect(getButton(wrapper!, '打印').attributes('disabled')).toBeUndefined();
  });

  it('支持在多个 formDetail tab 之间切换', async () => {
    await openReportView({
      activeKey: 'formDetail',
      tabs: [
        {
          id: 'form-id-1',
          key: 'formDetail',
          modelId: 'model-id-1',
          title: '委托信息',
        },
        {
          id: 'form-id-2',
          key: 'formDetail',
          modelId: 'model-id-2',
          title: '检验记录',
        },
      ],
    });

    expect(wrapper!.findAll('[data-test="tab-pane"]').map(item => item.text())).toEqual(['委托信息', '检验记录']);
    expect(getFormDetailText(wrapper!)).toContain('form-id-1');

    await wrapper!.findAll('[data-test="tab-pane"]')[1]!.trigger('click');

    expect(getFormDetailText(wrapper!)).toContain('form-id-2');
  });
});
