/*
 * Tencent is pleased to support the open source community by making TMagicEditor available.
 *
 * Copyright (C) 2025 Tencent.
 */
import { afterEach, describe, expect, test, vi } from 'vitest';
import { mount } from '@vue/test-utils';

import ContentMenu from '@editor/components/ContentMenu.vue';

const provideServices = () => ({
  global: {
    provide: {
      services: {
        editorService: {},
        uiService: {},
      },
    },
  },
});

describe('ContentMenu.vue', () => {
  afterEach(() => {
    vi.useRealTimers();
  });

  test('show 后触发 show 事件', async () => {
    const wrapper = mount(ContentMenu as any, {
      ...provideServices(),
      props: {
        menuData: [{ id: '1', type: 'button', text: 'a' }] as any,
      },
    });
    (wrapper.vm as any).show({ clientX: 10, clientY: 20 });
    await new Promise((r) => setTimeout(r, 0));
    expect(wrapper.emitted('show')).toBeTruthy();
  });

  test('show 之后调用 hide 触发 hide 事件', async () => {
    const wrapper = mount(ContentMenu as any, {
      ...provideServices(),
      props: { menuData: [] as any },
    });
    (wrapper.vm as any).show();
    await new Promise((r) => setTimeout(r, 0));
    (wrapper.vm as any).hide();
    expect(wrapper.emitted('hide')).toBeTruthy();
  });

  test('未显示时调用 hide 不触发事件', () => {
    const wrapper = mount(ContentMenu as any, {
      ...provideServices(),
      props: { menuData: [] as any },
    });
    (wrapper.vm as any).hide();
    expect(wrapper.emitted('hide')).toBeFalsy();
  });

  test('setPosition 计算超出底部时回拢', () => {
    const wrapper = mount(ContentMenu as any, {
      ...provideServices(),
      props: { menuData: [] as any },
    });
    Object.defineProperty(document.body, 'clientHeight', { value: 100, configurable: true });
    (wrapper.vm as any).setPosition({ clientX: 10, clientY: 90 });
    expect((wrapper.vm as any).menuPosition.left).toBe(10);
    expect((wrapper.vm as any).menuPosition.top).toBeLessThanOrEqual(100);
  });

  test('contains 判断 DOM 是否在菜单内部', async () => {
    const wrapper = mount(ContentMenu as any, {
      ...provideServices(),
      props: { menuData: [] as any },
    });
    (wrapper.vm as any).show({ clientX: 0, clientY: 0 });
    await new Promise((r) => setTimeout(r, 0));
    const outside = document.createElement('div');
    expect((wrapper.vm as any).contains(outside)).toBeFalsy();
  });

  test('autoHide=false 时点击不会隐藏', async () => {
    const wrapper = mount(ContentMenu as any, {
      ...provideServices(),
      props: { menuData: [{ id: '1', type: 'button', text: 'a' }] as any, autoHide: false },
    });
    (wrapper.vm as any).show();
    await new Promise((r) => setTimeout(r, 0));
    expect(wrapper.emitted('hide')).toBeFalsy();
  });

  test('isSubMenu=true 不监听 mousedown', () => {
    const addSpy = vi.spyOn(globalThis, 'addEventListener');
    const wrapper = mount(ContentMenu as any, {
      ...provideServices(),
      props: { menuData: [] as any, isSubMenu: true },
    });
    expect(addSpy).not.toHaveBeenCalledWith('mousedown', expect.any(Function), true);
    wrapper.unmount();
    addSpy.mockRestore();
  });

  test('卸载时移除 mousedown 监听', () => {
    const removeSpy = vi.spyOn(globalThis, 'removeEventListener');
    const wrapper = mount(ContentMenu as any, {
      ...provideServices(),
      props: { menuData: [] as any },
    });
    wrapper.unmount();
    expect(removeSpy).toHaveBeenCalledWith('mousedown', expect.any(Function), true);
    removeSpy.mockRestore();
  });

  test('外部点击触发 hide', async () => {
    const wrapper = mount(ContentMenu as any, {
      ...provideServices(),
      props: { menuData: [{ id: '1', type: 'button', text: 'a' }] as any },
      attachTo: document.body,
    });
    (wrapper.vm as any).show({ clientX: 0, clientY: 0 });
    await new Promise((r) => setTimeout(r, 0));
    const outside = document.createElement('div');
    document.body.appendChild(outside);
    const event = new MouseEvent('mousedown');
    Object.defineProperty(event, 'target', { value: outside });
    globalThis.dispatchEvent(event);
    expect(wrapper.emitted('hide')).toBeTruthy();
  });

  test('外部点击在菜单内部时不 hide', async () => {
    const wrapper = mount(ContentMenu as any, {
      ...provideServices(),
      props: { menuData: [{ id: '1', type: 'button', text: 'a' }] as any },
      attachTo: document.body,
    });
    (wrapper.vm as any).show({ clientX: 0, clientY: 0 });
    await new Promise((r) => setTimeout(r, 0));
    const inside = wrapper.find('.magic-editor-content-menu').element as HTMLElement;
    const event = new MouseEvent('mousedown');
    Object.defineProperty(event, 'target', { value: inside });
    globalThis.dispatchEvent(event);
    expect(wrapper.emitted('hide')).toBeFalsy();
  });

  test('autoHide=false 时外部点击不 hide', async () => {
    const wrapper = mount(ContentMenu as any, {
      ...provideServices(),
      props: { menuData: [] as any, autoHide: false },
      attachTo: document.body,
    });
    (wrapper.vm as any).show({ clientX: 0, clientY: 0 });
    await new Promise((r) => setTimeout(r, 0));
    const outside = document.createElement('div');
    document.body.appendChild(outside);
    const event = new MouseEvent('mousedown');
    Object.defineProperty(event, 'target', { value: outside });
    globalThis.dispatchEvent(event);
    expect(wrapper.emitted('hide')).toBeFalsy();
  });

  test('mouseenter 触发 mouseenter 事件', async () => {
    const wrapper = mount(ContentMenu as any, {
      ...provideServices(),
      props: { menuData: [] as any },
    });
    (wrapper.vm as any).show();
    await new Promise((r) => setTimeout(r, 0));
    await wrapper.find('.magic-editor-content-menu').trigger('mouseenter');
    expect(wrapper.emitted('mouseenter')).toBeTruthy();
  });

  test('showSubMenu 设置 subMenuData', async () => {
    const wrapper = mount(ContentMenu as any, {
      ...provideServices(),
      props: {
        menuData: [{ id: '1', type: 'button', text: 'a', items: [{ id: '2', type: 'button', text: 'b' }] }] as any,
      },
    });
    (wrapper.vm as any).show({ clientX: 10, clientY: 20 });
    await new Promise((r) => setTimeout(r, 0));
    const buttons = wrapper.findAll('.tool-button');
    if (buttons.length > 0) {
      await buttons[0].trigger('mouseenter');
      await new Promise((r) => setTimeout(r, 10));
    }
    expect(true).toBe(true);
  });
});
