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

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

describe('MModal component', () => {
  it('renders title and description', () => {
    const wrapper = mount(MModal, {
      props: {
        open: true,
        title: 'Test Modal',
        description: 'This is a description',
      },
      global: { stubs },
    });

    expect(wrapper.text()).toContain('Test Modal');
    expect(wrapper.text()).toContain('This is a description');
  });

  it('renders close button if closable is true', () => {
    const wrapper = mount(MModal, {
      props: { open: true, title: 'Title', closable: true },
      global: { stubs },
    });

    expect(wrapper.find('button.mc-modal__close').exists()).toBe(true);
  });

  it('does not render close button if closable is false', () => {
    const wrapper = mount(MModal, {
      props: { open: true, title: 'Title', closable: false },
      global: { stubs },
    });

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

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

    await wrapper.find('button.mc-modal__close').trigger('click');

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

  it('renders slots content', () => {
    const wrapper = mount(MModal, {
      props: { open: true, title: 'Title' },
      slots: {
        default: '<div class="default-slot">Default Content</div>',
        icon: '<span class="icon-slot">ICON</span>',
        footer: '<div class="footer-slot">Footer Content</div>',
        link: '<a href="#" class="link-slot">Link Content</a>',
      },
      global: { stubs },
    });

    expect(wrapper.find('.icon-slot').exists()).toBe(true);
    expect(wrapper.find('.default-slot').exists()).toBe(true);
    expect(wrapper.find('.footer-slot').exists()).toBe(true);
    expect(wrapper.find('.link-slot').exists()).toBe(true);
  });

  it('has inert attribute set correctly based on open prop', async () => {
    const wrapper = mount(MModal, {
      props: { open: false, title: 'Title' },
      global: { stubs },
    });

    expect(wrapper.find('.mc-modal').attributes('inert')).toBeDefined();

    await wrapper.setProps({ open: true });

    expect(wrapper.find('.mc-modal').attributes('inert')).toBeUndefined();
  });

  it('adds "is-open" class when open is true', async () => {
    const wrapper = mount(MModal, {
      props: { open: false, title: 'Title' },
      global: { stubs },
    });

    expect(wrapper.find('.mc-modal').classes()).not.toContain('is-open');

    await wrapper.setProps({ open: true });

    expect(wrapper.find('.mc-modal').classes()).toContain('is-open');
  });

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

    await wrapper.findComponent({ name: 'MOverlay' }).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 clicked and closeOnOverlay is false', async () => {
    const wrapper = mount(MModal, {
      props: { open: true, title: 'Title', closeOnOverlay: false },
      global: { stubs },
    });

    await wrapper.findComponent({ name: 'MOverlay' }).trigger('click');

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

    if (emitted) {
      const lastValue = emitted[emitted.length - 1][0];
      expect(lastValue).not.toBe(false);
    } else {
      expect(emitted).toBeFalsy();
    }
  });

  it('locks scroll when open and unlocks when closed', async () => {
    const wrapper = mount(MModal, {
      props: { open: false, title: 'Title', scroll: false },
      global: { stubs },
    });

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

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

  it('does not lock scroll if scroll prop is true', async () => {
    const wrapper = mount(MModal, {
      props: { open: false, title: 'Title', scroll: true },
      global: { stubs },
    });

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

  it('has role="dialog" on the modal element', () => {
    const wrapper = mount(MModal, {
      props: { open: true, title: 'Title' },
      global: { stubs },
    });

    expect(wrapper.find('.mc-modal').attributes('role')).toBe('dialog');
  });

  it('sets aria-modal based on open prop', async () => {
    const wrapper = mount(MModal, {
      props: { open: false, title: 'Title' },
      global: { stubs },
    });

    expect(wrapper.find('.mc-modal').attributes('aria-modal')).toBe('false');

    await wrapper.setProps({ open: true });

    expect(wrapper.find('.mc-modal').attributes('aria-modal')).toBe('true');
  });

  it('links aria-labelledby to the title element', () => {
    const wrapper = mount(MModal, {
      props: { open: true, title: 'Accessible Title' },
      global: { stubs },
    });

    const modal = wrapper.find('.mc-modal');
    const labelledBy = modal.attributes('aria-labelledby');
    expect(labelledBy).toMatch(/^modalTitle-/);

    const title = wrapper.find(`#${labelledBy}`);
    expect(title.exists()).toBe(true);
    expect(title.text()).toBe('Accessible Title');
  });

  it('closes when Escape key is pressed', async () => {
    const wrapper = mount(MModal, {
      props: { open: true, title: 'Title' },
      global: { stubs },
    });

    await wrapper.find('.mc-modal').trigger('keydown', { key: 'Escape' });

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

  it('moves focus to the modal when opened', async () => {
    const wrapper = mount(MModal, {
      props: { open: false, title: 'Title' },
      global: { stubs },
      attachTo: document.body,
    });

    await wrapper.setProps({ open: true });
    await nextTick();

    expect(document.activeElement).toBe(wrapper.find('.mc-modal').element);

    wrapper.unmount();
  });

  it('traps focus from last to first element on Tab', async () => {
    const wrapper = mount(MModal, {
      props: { open: true, title: 'Title', closable: true },
      slots: {
        default: '<button data-test="last-focusable">Last</button>',
      },
      global: { stubs },
      attachTo: document.body,
    });

    await nextTick();

    const closeButton = wrapper.find('button.mc-modal__close');
    const lastFocusable = wrapper.find('[data-test="last-focusable"]');

    (lastFocusable.element as HTMLButtonElement).focus();
    await lastFocusable.trigger('keydown', { key: 'Tab' });

    expect(document.activeElement).toBe(closeButton.element);

    wrapper.unmount();
  });

  it('traps focus from first to last element on Shift+Tab', async () => {
    const wrapper = mount(MModal, {
      props: { open: true, title: 'Title', closable: true },
      slots: {
        default: '<button data-test="last-focusable">Last</button>',
      },
      global: { stubs },
      attachTo: document.body,
    });

    await nextTick();

    const closeButton = wrapper.find('button.mc-modal__close');
    const lastFocusable = wrapper.find('[data-test="last-focusable"]');

    (closeButton.element as HTMLButtonElement).focus();
    await closeButton.trigger('keydown', { key: 'Tab', shiftKey: true });

    expect(document.activeElement).toBe(lastFocusable.element);

    wrapper.unmount();
  });
});
