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

// Stubs for child components
const stubs = {
  ArrowBack24: { template: '<svg />' },
  Cross24: { template: '<svg />' },
  MIconButton: {
    template: `<button @click="$emit('click')"><slot name="icon"/></button>`,
  },
  MOverlay: {
    name: 'MOverlay',
    template: `<div class="overlay" @click="$emit('click', $event)"><slot/></div>`,
  },
};

describe('MDrawer component', () => {
  it('renders title and contentTitle when provided', () => {
    const wrapper = mount(MDrawer, {
      props: {
        open: true,
        title: 'Main Drawer Title',
        contentTitle: 'Drawer Content Title',
      },
      slots: {
        default: '<p>Drawer content</p>',
      },
      global: { stubs },
    });

    expect(wrapper.find('.mc-drawer__title').text()).toBe('Main Drawer Title');
    expect(wrapper.find('.mc-drawer__content__title').text()).toBe(
      'Drawer Content Title',
    );
    expect(wrapper.find('.mc-drawer__content').text()).toContain(
      'Drawer content',
    );
  });

  it('emits update:open false when close button is clicked', async () => {
    const wrapper = mount(MDrawer, {
      props: {
        open: true,
        title: 'Test Title',
      },
      global: { stubs },
    });

    const closeButton = wrapper.find('.mc-drawer__close');
    await closeButton.trigger('click');

    const emitted = wrapper.emitted('update:open');
    expect(emitted).toBeTruthy();
    expect(emitted![emitted!.length - 1]).toEqual([false]);
  });

  it('emits back event when back button is clicked', async () => {
    const wrapper = mount(MDrawer, {
      props: {
        open: true,
        title: 'Test Title',
        back: true,
      },
      global: { stubs },
    });

    const backButton = wrapper.find('.mc-drawer__back');
    await backButton.trigger('click');

    expect(wrapper.emitted('back')).toBeTruthy();
  });

  it('renders footer slot when provided', () => {
    const wrapper = mount(MDrawer, {
      props: {
        open: true,
        title: 'Test Title',
      },
      slots: {
        footer: '<button>Footer Button</button>',
      },
      global: { stubs },
    });

    expect(wrapper.find('.mc-drawer__footer').exists()).toBe(true);
    expect(wrapper.find('.mc-drawer__footer button').text()).toBe(
      'Footer Button',
    );
  });

  it('applies correct class when extended and position is left', () => {
    const wrapper = mount(MDrawer, {
      props: {
        open: true,
        title: 'Test Title',
        extended: true,
        position: 'left',
      },
      global: { stubs },
    });

    const section = wrapper.find('section.mc-drawer');
    expect(section.classes()).toContain('mc-drawer--extend');
    expect(section.classes()).toContain('mc-drawer--left');
    expect(section.classes()).toContain('is-open');
  });

  it('does not render back button if prop `back` is false', () => {
    const wrapper = mount(MDrawer, {
      props: {
        open: true,
        title: 'Test Title',
      },
      global: { stubs },
    });

    expect(wrapper.find('.mc-drawer__back').exists()).toBe(false);
  });

  it('sets the focus on the title when the drawer opens', async () => {
    const wrapper = mount(MDrawer, {
      props: {
        title: 'Test Title',
      },
      attachTo: document.body,
      global: { stubs },
    });

    const titleElement = wrapper.find('.mc-drawer__title').element;
    expect(document.activeElement).not.toBe(titleElement);
    await wrapper.setProps({ open: true });
    expect(document.activeElement).toBe(titleElement);
  });

  it('does not refocus the title when the drawer closes', async () => {
    const wrapper = mount(MDrawer, {
      props: {
        title: 'Test Title',
        open: true,
      },
      attachTo: document.body,
      global: { stubs },
    });

    const titleElement = wrapper.find('.mc-drawer__title').element;

    expect(document.activeElement).toBe(titleElement);

    await wrapper.setProps({ open: false });
    await wrapper.vm.$nextTick();

    expect(document.activeElement).toBe(titleElement);
  });

  it('emits update:open false when overlay is clicked and closeOnOverlay is true', async () => {
    const wrapper = mount(MDrawer, {
      props: {
        open: true,
        title: 'Test Title',
        closeOnOverlay: true,
      },
      global: { stubs },
    });

    await wrapper.find('.overlay').trigger('click');

    const emitted = wrapper.emitted('update:open');
    expect(emitted).toBeTruthy();
    expect(emitted![emitted!.length - 1]).toEqual([false]);
  });

  it('does not emit update:open when overlay is clicked and closeOnOverlay is false', async () => {
    const wrapper = mount(MDrawer, {
      props: {
        open: true,
        title: 'Test Title',
        closeOnOverlay: false,
      },
      global: { stubs },
    });

    await wrapper.find('.overlay').trigger('click');

    const emitted = wrapper.emitted('update:open');
    expect(emitted).toBeTruthy();
    expect(emitted?.length).toBe(1);
  });

  it('does not emit update:open when overlay is clicked and closeOnOverlay is not set', async () => {
    const wrapper = mount(MDrawer, {
      props: {
        open: true,
        title: 'Test Title',
      },
      global: { stubs },
    });

    await wrapper.find('.overlay').trigger('click');

    const emitted = wrapper.emitted('update:open');
    expect(emitted).toBeTruthy();
  });

  it('emits update:open false when pressing ESC key', async () => {
    const wrapper = mount(MDrawer, {
      props: {
        open: true,
        title: 'Test Title',
      },
      global: { stubs },
    });

    await wrapper
      .find('section.mc-drawer')
      .trigger('keydown', { key: 'Escape' });
    expect(wrapper.emitted('update:open')).toBeTruthy();
    expect(wrapper.emitted('update:open')!.at(-1)).toEqual([false]);
  });

  it('stops Escape propagation after closing', async () => {
    const onDocumentKeydown = vi.fn();
    document.addEventListener('keydown', onDocumentKeydown);

    const wrapper = mount(MDrawer, {
      props: {
        open: true,
        title: 'Test Title',
      },
      attachTo: document.body,
      global: { stubs },
    });

    wrapper
      .find('section.mc-drawer')
      .element.dispatchEvent(
        new KeyboardEvent('keydown', { key: 'Escape', bubbles: true }),
      );

    expect(wrapper.emitted('update:open')!.at(-1)).toEqual([false]);
    expect(onDocumentKeydown).not.toHaveBeenCalled();

    document.removeEventListener('keydown', onDocumentKeydown);
    wrapper.unmount();
  });

  it('locks and unlocks scroll when scroll=false and open changes', async () => {
    const wrapper = mount(MDrawer, {
      props: {
        title: 'Scroll Test',
        open: false,
        scroll: false,
      },
      global: { stubs },
    });

    expect(document.body.style.overflow).toBe('');

    await wrapper.setProps({ open: true });
    expect(document.body.style.overflow).toBe('hidden');

    await wrapper.setProps({ open: false });
    expect(document.body.style.overflow).toBe('');
  });

  it('restores scroll when unmounted', async () => {
    const wrapper = mount(MDrawer, {
      props: {
        open: true,
        title: 'Unmount Test',
        scroll: false,
      },
      global: { stubs },
    });

    await wrapper.setProps({ open: true });
    expect(document.body.style.overflow).toBe('hidden');

    wrapper.unmount();
    expect(document.body.style.overflow).toBe('');
  });

  it('sets inert on section when closed', async () => {
    const wrapper = mount(MDrawer, {
      props: { open: false, title: 'Test' },
      global: { stubs },
    });
    // JSDOM renders :inert="true" as "true" — not.toBeUndefined() is the correct check
    expect(
      wrapper.find('section.mc-drawer').attributes('inert'),
    ).not.toBeUndefined();

    await wrapper.setProps({ open: true });
    expect(
      wrapper.find('section.mc-drawer').attributes('inert'),
    ).toBeUndefined();
  });

  it('does not render aria-modal when closed', async () => {
    const wrapper = mount(MDrawer, {
      props: { open: false, title: 'Test' },
      global: { stubs },
    });
    expect(
      wrapper.find('section.mc-drawer').attributes('aria-modal'),
    ).toBeUndefined();

    await wrapper.setProps({ open: true });
    expect(wrapper.find('section.mc-drawer').attributes('aria-modal')).toBe(
      'true',
    );
  });

  it('traps focus forward: Tab from last focusable wraps to first', async () => {
    const wrapper = mount(MDrawer, {
      props: { open: true, title: 'Test', back: true },
      attachTo: document.body,
      global: { stubs },
    });

    const buttons = wrapper.findAll('button');
    const lastEl = buttons[buttons.length - 1].element as HTMLElement;
    lastEl.focus();

    await wrapper
      .find('section.mc-drawer')
      .trigger('keydown', { key: 'Tab', shiftKey: false });

    // focus should wrap to the first focusable button (back button)
    expect(document.activeElement).toBe(buttons[0].element);
    wrapper.unmount();
  });

  it('traps focus backward: Shift+Tab from first focusable wraps to last', async () => {
    const wrapper = mount(MDrawer, {
      props: { open: true, title: 'Test', back: true },
      attachTo: document.body,
      global: { stubs },
    });

    const buttons = wrapper.findAll('button');
    const firstEl = buttons[0].element as HTMLElement;
    firstEl.focus();

    await wrapper
      .find('section.mc-drawer')
      .trigger('keydown', { key: 'Tab', shiftKey: true });

    // focus should wrap to the last focusable button (close button)
    expect(document.activeElement).toBe(buttons[buttons.length - 1].element);
    wrapper.unmount();
  });

  it('emits update:open on mount reflecting initial state', () => {
    const wrapper = mount(MDrawer, {
      props: {
        open: true,
        title: 'Initial Test',
      },
      global: { stubs },
    });

    expect(wrapper.emitted('update:open')).toBeTruthy();
    expect(wrapper.emitted('update:open')![0]).toEqual([true]);
  });
});
