import { defineComponent, inject, ref } from 'vue';
import { mount } from '@vue/test-utils';
import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest';
import MSidebar from './MSidebar.vue';
import { EXPANDED_SIDEBAR_KEY, TOGGLE_SIDEBAR_KEY } from './MSidebar.const';

const isMobile = ref(false);

vi.mock('../../utils/use-is-mobile.composable.ts', () => {
  return {
    useIsMobile: () => {
      return {
        isMobile: isMobile.value,
      };
    },
  };
});

const MockedFooter = defineComponent({
  name: 'MockedFooter',
  setup() {
    const expanded = inject(EXPANDED_SIDEBAR_KEY);
    const toggle = inject(TOGGLE_SIDEBAR_KEY);
    return { expanded, toggle };
  },
  template: `
    <div>
      <button data-test="toggle" @click="toggle">Toggle</button>
    </div>
  `,
});

describe('MSidebar', () => {
  it('toggles expanded state and emits event when consumer calls toggle', async () => {
    const wrapper = mount(MSidebar, {
      global: { components: { MockedFooter } },
      props: {
        modelValue: true,
      },
      slots: {
        header: '',
        shortcuts: '',
        nav: '',
        footer: '<MockedFooter />',
      },
    });

    const toggleBtn = wrapper.find('[data-test="toggle"]');
    await toggleBtn.trigger('click');

    const emitted = wrapper.emitted('update:modelValue') || [];
    expect(emitted.length).toBe(1);
    expect(emitted[0]).toEqual([false]);

    await wrapper.setProps({ modelValue: emitted[0][0] as boolean });

    const aside = wrapper.find('aside');
    expect(aside.classes()).toContain('mc-sidebar--collapsed');
  });

  it('emits "close" when the close button is clicked', async () => {
    const wrapper = mount(MSidebar, {
      props: { modelValue: true },
      global: {
        stubs: ['MIconButton', 'Cross24'],
      },
    });

    const btn = wrapper.find('.mc-sidebar__close-button');
    await btn.trigger('click');

    expect(wrapper.emitted()).toHaveProperty('close');
    expect(wrapper.emitted('close')?.length).toBe(1);
  });

  describe('Resize handling', () => {
    let addSpy: ReturnType<typeof vi.spyOn>;
    let removeSpy: ReturnType<typeof vi.spyOn>;

    beforeEach(() => {
      vi.resetModules();
      addSpy = vi.spyOn(window, 'addEventListener');
      removeSpy = vi.spyOn(window, 'removeEventListener');
    });

    afterEach(() => {
      addSpy.mockRestore();
      removeSpy.mockRestore();
      vi.clearAllMocks();
    });

    it('registers/unregisters resize listener and does not emit when isMobile is false', async () => {
      isMobile.value = false;

      const wrapper = mount(MSidebar, {
        props: { modelValue: false },
      });

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

      window.dispatchEvent(new Event('resize'));

      const emitted = wrapper.emitted('update:modelValue');
      expect(emitted).toBeUndefined();

      wrapper.unmount();
      expect(removeSpy).toHaveBeenCalledWith('resize', expect.any(Function));
    });
  });
});
