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

const options = [
  { label: 'Option 1', value: 1 },
  { label: 'Option 2', value: 2 },
];

const stubs = {
  MOptionListbox: {
    name: 'MOptionListbox',
    props: ['modelValue', 'options', 'multiple', 'id'],
    emits: ['update:modelValue'],
    template: '<div class="option-listbox-stub" />',
  },
};

describe('MPageHeaderContext', () => {
  it('renders selected option label in single mode', () => {
    const wrapper = mount(MPageHeaderContext, {
      props: {
        modelValue: 2,
        options,
      },
      global: { stubs },
    });

    expect(wrapper.find('.mc-page-header__context-switcher').text()).toContain(
      'Option 2',
    );
  });

  it('falls back to provided label when no option matches modelValue', () => {
    const wrapper = mount(MPageHeaderContext, {
      props: {
        modelValue: 999,
        options,
        label: 'Fallback context',
      },
      global: { stubs },
    });

    expect(wrapper.find('.mc-page-header__context-switcher').text()).toContain(
      'Fallback context',
    );
  });

  it('always uses label in multiple mode', () => {
    const wrapper = mount(MPageHeaderContext, {
      props: {
        modelValue: [1],
        options,
        multiple: true,
        label: 'Multi context',
      },
      global: { stubs },
    });

    expect(wrapper.find('.mc-page-header__context-switcher').text()).toContain(
      'Multi context',
    );
  });

  it('toggles listbox visibility when switcher button is clicked', async () => {
    const wrapper = mount(MPageHeaderContext, {
      props: {
        modelValue: null,
        options,
      },
      global: { stubs },
    });

    const listbox = wrapper.find('.mc-page-header__context-switcher-listbox');
    expect(listbox.classes()).toContain(
      'mc-page-header__context-switcher-listbox--hidden',
    );

    await wrapper.find('.mc-page-header__context-switcher').trigger('click');
    expect(listbox.classes()).not.toContain(
      'mc-page-header__context-switcher-listbox--hidden',
    );
  });

  it('emits update:modelValue when the listbox emits an update', async () => {
    const wrapper = mount(MPageHeaderContext, {
      props: {
        modelValue: null,
        options,
      },
      global: { stubs },
    });

    wrapper
      .findComponent({ name: 'MOptionListbox' })
      .vm.$emit('update:modelValue', 1);
    await nextTick();

    expect(wrapper.emitted('update:modelValue')?.[0]).toEqual([1]);
  });

  it('closes the listbox on outside click', async () => {
    const wrapper = mount(MPageHeaderContext, {
      props: {
        modelValue: null,
        options,
      },
      global: { stubs },
      attachTo: document.body,
    });

    await wrapper.find('.mc-page-header__context-switcher').trigger('click');
    expect(
      wrapper
        .find('.mc-page-header__context-switcher-listbox')
        .classes()
        .includes('mc-page-header__context-switcher-listbox--hidden'),
    ).toBe(false);

    document.body.dispatchEvent(new MouseEvent('click', { bubbles: true }));
    await nextTick();

    expect(
      wrapper
        .find('.mc-page-header__context-switcher-listbox')
        .classes()
        .includes('mc-page-header__context-switcher-listbox--hidden'),
    ).toBe(true);

    wrapper.unmount();
  });

  it('registers and removes click listener on mount lifecycle', () => {
    const addSpy = vi.spyOn(document, 'addEventListener');
    const removeSpy = vi.spyOn(document, 'removeEventListener');

    const wrapper = mount(MPageHeaderContext, {
      props: {
        modelValue: null,
        options,
      },
      global: { stubs },
    });

    expect(addSpy).toHaveBeenCalledWith('click', expect.any(Function));

    wrapper.unmount();

    expect(removeSpy).toHaveBeenCalledWith('click', expect.any(Function));

    addSpy.mockRestore();
    removeSpy.mockRestore();
  });
});
