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

const onTriggerKeydown = vi.fn();
const onListboxKeydown = vi.fn();
const hideFloatingItem = vi.fn();

vi.mock('../sidebar/use-floating-item.composable', () => {
  return {
    useFloatingItem: () => {
      const floatingItemIsDisplayed = ref(false);
      return {
        floatingItemIsDisplayed,
        showFloatingItem: () => (floatingItemIsDisplayed.value = true),
        hideFloatingItem: () => {
          floatingItemIsDisplayed.value = false;
          hideFloatingItem();
        },
        onTriggerKeydown,
        onListboxKeydown,
      };
    },
  };
});

import MSidebarExpandableItem from './MSidebarExpandableItem.vue';
import { EXPANDED_SIDEBAR_KEY } from '../sidebar/MSidebar.const';

describe('MSidebarExpandableItem', () => {
  beforeEach(() => {
    vi.clearAllMocks();
  });

  it('renders expanded details when expanded is true', async () => {
    const wrapper = mount(MSidebarExpandableItem, {
      props: { label: 'My Label', menuLabel: 'Menu' },
      global: {
        provide: { [EXPANDED_SIDEBAR_KEY]: true },
      },
      slots: {
        default: '<li>Child item</li>',
      },
    });

    expect(wrapper.find('li.mc-sidebar__item').exists()).toBe(true);
    expect(
      wrapper.find('summary.mc-sidebar__link').attributes('aria-label'),
    ).toBe('My Label');
    expect(wrapper.text()).toContain('My Label');
    expect(wrapper.find('ul.mc-sidebar__sublist').html()).toContain(
      'Child item',
    );
  });

  it('renders trigger and toggles listbox when not expanded', async () => {
    const wrapper = mount(MSidebarExpandableItem, {
      props: { label: 'My Label', menuLabel: 'Overlay Menu' },
      global: {
        provide: { [EXPANDED_SIDEBAR_KEY]: false },
      },
      slots: {
        default: '<li>Overlay item</li>',
      },
    });

    const button = wrapper.find('button.mc-sidebar__trigger');
    expect(button.exists()).toBe(true);
    expect(button.attributes('aria-haspopup')).toBe('listbox');
    expect(button.attributes('aria-expanded')).toBe('false');

    await button.trigger('mouseenter');
    await nextTick();
    expect(button.attributes('aria-expanded')).toBe('true');

    const floating = wrapper.find('.mc-sidebar__floating-item');
    expect(floating!.text()).toContain('Overlay Menu');
    expect(floating!.text()).toContain('Overlay item');

    await button.trigger('mouseleave');
    await nextTick();
    expect(button.attributes('aria-expanded')).toBe('false');
    expect(floating.classes()).toContain('mc-sidebar__floating-item--hidden');
  });

  it('handles listbox trigger with keyboard accessibility', async () => {
    const wrapper = mount(MSidebarExpandableItem, {
      props: { label: 'My Label', menuLabel: 'Overlay Menu' },
      global: {
        provide: { [EXPANDED_SIDEBAR_KEY]: false },
      },
      slots: {
        default: `
          <li>Overlay item</li>
          <li>Overlay item 2</li>
        `,
      },
    });

    const button = wrapper.find('button.mc-sidebar__trigger');
    expect(button.exists()).toBe(true);

    await button.trigger('focus');
    await nextTick();
    expect(button.attributes('aria-expanded')).toBe('true');

    await button.trigger('keydown', { key: 'ArrowDown' });
    expect(onTriggerKeydown).toHaveBeenCalled();

    const listboxEl = wrapper.find('.mc-sidebar__floating-item');

    await listboxEl.trigger('keydown', { key: 'Escape' });

    await button.trigger('blur');
    await nextTick();
    expect(button.attributes('aria-expanded')).toBe('false');
  });

  it('calls onListboxKeydown when keydown is emitted on the listbox', async () => {
    const wrapper = mount(MSidebarExpandableItem, {
      props: { label: 'Item', menuLabel: 'Menu' },
      global: {
        provide: { [EXPANDED_SIDEBAR_KEY]: false },
      },
    });

    const listbox = wrapper.find('.mc-sidebar__floating-item');
    expect(listbox.exists()).toBe(true);

    await listbox.trigger('keydown');
    expect(onListboxKeydown).toHaveBeenCalled();
  });

  it('collapsed trigger button has aria-label equal to label prop', () => {
    const wrapper = mount(MSidebarExpandableItem, {
      props: { label: 'Products', menuLabel: 'Products menu' },
      global: {
        provide: { [EXPANDED_SIDEBAR_KEY]: false },
      },
    });

    const button = wrapper.find('button.mc-sidebar__trigger');
    expect(button.attributes('aria-label')).toBe('Products');
  });

  it('calls hideFloatingItem on mouseleave and blur on the listbox', async () => {
    const wrapper = mount(MSidebarExpandableItem, {
      props: { label: 'Item', menuLabel: 'Menu' },
      global: {
        provide: { [EXPANDED_SIDEBAR_KEY]: false },
      },
    });

    const listbox = wrapper.find('.mc-sidebar__floating-item');
    expect(listbox.exists()).toBe(true);

    await listbox.trigger('mouseleave');
    expect(hideFloatingItem).toHaveBeenCalledTimes(1);

    await listbox.trigger('blur');
    expect(hideFloatingItem).toHaveBeenCalledTimes(2);
  });
});
