import { mount } from '@vue/test-utils';
import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest';
import BmsTooltip from './BmsTooltip.vue';

const mountOptions = {
  attachTo: document.body,
  global: {
    stubs: { Teleport: true },
  },
};

// jsdom does not implement elementFromPoint — define it once at module level.
const elementFromPointMock = vi.fn<() => Element | null>(() => null);
Object.defineProperty(document, 'elementFromPoint', {
  value: elementFromPointMock,
  writable: true,
  configurable: true,
});

beforeEach(() => {
  elementFromPointMock.mockReset();
  elementFromPointMock.mockReturnValue(null);
});

const mockAnchorVisible = (anchorEl: Element) => {
  elementFromPointMock.mockReturnValue(anchorEl);
};

const mockAnchorCovered = (coveringElement: Element) => {
  elementFromPointMock.mockReturnValue(coveringElement);
};

describe('BmsTooltip - activated prop', () => {
  it('does not render the wrapper div when activated is false', () => {
    const wrapper = mount(BmsTooltip, {
      ...mountOptions,
      props: { activated: false },
    });
    expect(wrapper.find('.bms-tooltip-anchor-wrapper').exists()).toBe(false);
  });

  it('renders the slot content directly when activated is false', () => {
    const wrapper = mount(BmsTooltip, {
      ...mountOptions,
      props: { activated: false },
      slots: { default: '<span class="content">text</span>' },
    });
    expect(wrapper.find('.content').exists()).toBe(true);
    expect(wrapper.findComponent({ name: 'UiTooltip' }).exists()).toBe(false);
  });
});

describe('BmsTooltip - toggle (defineExpose)', () => {
  it('shows tooltip via external toggle when anchor is uncovered', async () => {
    const wrapper = mount(BmsTooltip, mountOptions);
    mockAnchorVisible(wrapper.find('.bms-tooltip-anchor-wrapper').element);
    (wrapper.vm as any).toggle(true);
    await wrapper.vm.$nextTick();
    expect(wrapper.findComponent({ name: 'UiTooltip' }).exists()).toBe(true);
  });

  it('hides tooltip via external toggle', async () => {
    const wrapper = mount(BmsTooltip, mountOptions);
    mockAnchorVisible(wrapper.find('.bms-tooltip-anchor-wrapper').element);
    (wrapper.vm as any).toggle(true);
    await wrapper.vm.$nextTick();
    (wrapper.vm as any).toggle(false);
    await wrapper.vm.$nextTick();
    expect(wrapper.findComponent({ name: 'UiTooltip' }).exists()).toBe(false);
  });
});

describe('BmsTooltip - cleanup', () => {
  afterEach(() => {
    vi.restoreAllMocks();
  });

  it('removes pointermove listener on unmount', async () => {
    const wrapper = mount(BmsTooltip, mountOptions);
    mockAnchorVisible(wrapper.find('.bms-tooltip-anchor-wrapper').element);
    await wrapper.find('.bms-tooltip-anchor-wrapper').trigger('mouseenter');

    const removeSpy = vi.spyOn(document, 'removeEventListener');
    wrapper.unmount();
    expect(removeSpy).toHaveBeenCalledWith('pointermove', expect.any(Function));
  });
});

describe('BmsTooltip - bms-in-modal', () => {
  it('does not pass bms-in-modal class to UiTooltip when not inside a modal', async () => {
    const wrapper = mount(BmsTooltip, mountOptions);
    mockAnchorVisible(wrapper.find('.bms-tooltip-anchor-wrapper').element);
    await wrapper.find('.bms-tooltip-anchor-wrapper').trigger('mouseenter');
    expect(wrapper.findComponent({ name: 'UiTooltip' }).html()).not.toContain(
      'bms-in-modal',
    );
  });

  it('passes bms-in-modal class to UiTooltip when inside a modal', async () => {
    const wrapper = mount(BmsTooltip, {
      ...mountOptions,
      global: {
        ...mountOptions.global,
        provide: { 'bms-in-modal': true },
      },
    });
    mockAnchorVisible(wrapper.find('.bms-tooltip-anchor-wrapper').element);
    await wrapper.find('.bms-tooltip-anchor-wrapper').trigger('mouseenter');
    expect(wrapper.findComponent({ name: 'UiTooltip' }).html()).toContain(
      'bms-in-modal',
    );
  });
});

describe('BmsTooltip - hover', () => {
  it('does not render UiTooltip on mount (no hover)', () => {
    const wrapper = mount(BmsTooltip, mountOptions);
    expect(wrapper.findComponent({ name: 'UiTooltip' }).exists()).toBe(false);
  });

  it('shows tooltip on mouseenter when anchor is uncovered by any other element', async () => {
    const wrapper = mount(BmsTooltip, mountOptions);
    mockAnchorVisible(wrapper.find('.bms-tooltip-anchor-wrapper').element);
    await wrapper.find('.bms-tooltip-anchor-wrapper').trigger('mouseenter');
    expect(wrapper.findComponent({ name: 'UiTooltip' }).exists()).toBe(true);
    expect(wrapper.findComponent({ name: 'UiTooltip' }).props('visible')).toBe(
      true,
    );
  });

  it('does not show tooltip on mouseenter when anchor is covered by another element', async () => {
    const coveringElement = document.createElement('div');
    mockAnchorCovered(coveringElement);
    const wrapper = mount(BmsTooltip, mountOptions);
    await wrapper.find('.bms-tooltip-anchor-wrapper').trigger('mouseenter');
    expect(wrapper.findComponent({ name: 'UiTooltip' }).exists()).toBe(false);
  });

  it('hides tooltip on mouseleave', async () => {
    const wrapper = mount(BmsTooltip, mountOptions);
    mockAnchorVisible(wrapper.find('.bms-tooltip-anchor-wrapper').element);
    await wrapper.find('.bms-tooltip-anchor-wrapper').trigger('mouseenter');
    await wrapper.find('.bms-tooltip-anchor-wrapper').trigger('mouseleave');
    expect(wrapper.findComponent({ name: 'UiTooltip' }).exists()).toBe(false);
  });

  it('hides tooltip when a covering element appears on top after hover (pointermove)', async () => {
    const wrapper = mount(BmsTooltip, mountOptions);
    mockAnchorVisible(wrapper.find('.bms-tooltip-anchor-wrapper').element);
    await wrapper.find('.bms-tooltip-anchor-wrapper').trigger('mouseenter');
    expect(wrapper.findComponent({ name: 'UiTooltip' }).exists()).toBe(true);

    // Datalist opens on top — anchor is now covered
    const coveringElement = document.createElement('div');
    mockAnchorCovered(coveringElement);
    document.dispatchEvent(new PointerEvent('pointermove'));
    await wrapper.vm.$nextTick();

    expect(wrapper.findComponent({ name: 'UiTooltip' }).exists()).toBe(false);
  });
});
