import { mount } from '@vue/test-utils';
import { describe, it, expect } from 'vitest';
import MTabs from './MTabs.vue';
import { defineComponent, h, markRaw } from 'vue';

describe('MTabs.vue', () => {
  const tabs = [
    { id: '1', label: 'Tab 1' },
    { id: '2', label: 'Tab 2' },
    { id: '3', label: 'Tab 3' },
  ];

  it('renders tabs with correct labels', () => {
    const wrapper = mount(MTabs, {
      props: {
        tabs,
      },
    });

    const tabElements = wrapper.findAll('li.mc-tabs__item');
    expect(tabElements.length).toBe(tabs.length);

    tabs.forEach((tab, i) => {
      expect(tabElements[i].text()).toContain(tab.label);
    });
  });

  it('applies selected class and aria-selected attribute based on modelValue and updates on tab click - string type model', async () => {
    const wrapper = mount(MTabs, {
      props: {
        tabs,
        modelValue: '1',
      },
    });

    const buttons = wrapper.findAll('button.mc-tabs__tab');

    buttons.forEach((button, i) => {
      if (i === 0) {
        expect(button.classes()).toContain('mc-tabs__tab--selected');
        expect(button.attributes('aria-selected')).toBe('true');
      } else {
        expect(button.classes()).not.toContain('mc-tabs__tab--selected');
        expect(button.attributes('aria-selected')).toBe('false');
      }
    });

    await buttons[1].trigger('click');

    expect(wrapper.emitted('update:modelValue')).toBeTruthy();
    expect(wrapper.emitted('update:modelValue')![0]).toEqual(['2']);

    await wrapper.setProps({ modelValue: '2' });

    const updatedButtons = wrapper.findAll('button.mc-tabs__tab');
    updatedButtons.forEach((button, i) => {
      if (i === 1) {
        expect(button.classes()).toContain('mc-tabs__tab--selected');
        expect(button.attributes('aria-selected')).toBe('true');
      } else {
        expect(button.classes()).not.toContain('mc-tabs__tab--selected');
        expect(button.attributes('aria-selected')).toBe('false');
      }
    });
  });

  it('applies selected class and aria-selected attribute based on modelValue and updates on tab click - number type model', async () => {
    const wrapper = mount(MTabs, {
      props: {
        tabs,
        modelValue: 0,
      },
    });

    const buttons = wrapper.findAll('button.mc-tabs__tab');

    buttons.forEach((button, i) => {
      if (i === 0) {
        expect(button.classes()).toContain('mc-tabs__tab--selected');
        expect(button.attributes('aria-selected')).toBe('true');
      } else {
        expect(button.classes()).not.toContain('mc-tabs__tab--selected');
        expect(button.attributes('aria-selected')).toBe('false');
      }
    });

    await buttons[1].trigger('click');

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

    await wrapper.setProps({ modelValue: 1 });

    const updatedButtons = wrapper.findAll('button.mc-tabs__tab');
    updatedButtons.forEach((button, i) => {
      if (i === 1) {
        expect(button.classes()).toContain('mc-tabs__tab--selected');
        expect(button.attributes('aria-selected')).toBe('true');
      } else {
        expect(button.classes()).not.toContain('mc-tabs__tab--selected');
        expect(button.attributes('aria-selected')).toBe('false');
      }
    });
  });

  it('does not add size class when size is "m" (default)', () => {
    const wrapper = mount(MTabs, { props: { tabs } });
    expect(wrapper.classes()).not.toContain('mc-tabs--m');
    expect(wrapper.classes()).not.toContain('mc-tabs--s');
  });

  it('adds mc-tabs--s class when size is "s" and removes it when changed to "m"', async () => {
    const wrapper = mount(MTabs, {
      props: { tabs, size: 's' },
    });

    expect(wrapper.classes()).toContain('mc-tabs--s');

    await wrapper.setProps({ size: 'm' });

    expect(wrapper.classes()).not.toContain('mc-tabs--s');
    expect(wrapper.classes()).not.toContain('mc-tabs--m');
  });

  it('adds divider and centered classes based on props', async () => {
    const wrapper = mount(MTabs, {
      props: {
        tabs,
        divider: true,
        centered: true,
      },
    });

    expect(wrapper.classes()).toContain('mc-tabs--centered');
    expect(wrapper.findComponent({ name: 'MDivider' }).exists()).toBe(true);

    await wrapper.setProps({ divider: false, centered: false });

    expect(wrapper.classes()).not.toContain('mc-tabs--centered');
    expect(wrapper.findComponent({ name: 'MDivider' }).exists()).toBe(false);
  });

  it('sets aria-label on tablist based on description prop', () => {
    const description = 'Main tabs navigation';
    const wrapper = mount(MTabs, {
      props: {
        tabs,
        description,
      },
    });

    const ul = wrapper.find('ul[role="tablist"]');
    expect(ul.attributes('aria-label')).toBe(description);
  });

  it('renders no tabs if tabs prop is empty', () => {
    const wrapper = mount(MTabs, { props: { tabs: [] } });
    expect(wrapper.findAll('li.mc-tabs__item').length).toBe(0);
  });

  it('emits update:modelValue on tab click if tab is not disabled', async () => {
    const wrapper = mount(MTabs, {
      props: {
        tabs: [
          { id: '1', label: 'Tab 1' },
          { id: '2', label: 'Tab 2', disabled: true },
          { id: '3', label: 'Tab 3' },
        ],
        modelValue: '1',
      },
    });

    const buttons = wrapper.findAll('button.mc-tabs__tab');

    await buttons[2].trigger('click');
    expect(wrapper.emitted('update:modelValue')).toBeTruthy();
    expect(wrapper.emitted('update:modelValue')![0]).toEqual(['3']);

    await buttons[1].trigger('click');
    expect(wrapper.emitted('update:modelValue')!.length).toBe(1);
  });

  it('selected tab has tabindex="0", others have tabindex="-1"', () => {
    const wrapper = mount(MTabs, {
      props: { tabs, modelValue: '2' },
    });

    const buttons = wrapper.findAll('button.mc-tabs__tab');
    expect(buttons[0].attributes('tabindex')).toBe('-1');
    expect(buttons[1].attributes('tabindex')).toBe('0');
    expect(buttons[2].attributes('tabindex')).toBe('-1');
  });

  it('falls back to first enabled tab for tabindex when selected tab is disabled', () => {
    const wrapper = mount(MTabs, {
      props: {
        tabs: [
          { id: '1', label: 'Tab 1', disabled: true },
          { id: '2', label: 'Tab 2' },
          { id: '3', label: 'Tab 3' },
        ],
        modelValue: '1',
      },
    });

    const buttons = wrapper.findAll('button.mc-tabs__tab');
    expect(buttons[0].attributes('tabindex')).toBe('-1');
    expect(buttons[1].attributes('tabindex')).toBe('0');
    expect(buttons[2].attributes('tabindex')).toBe('-1');
  });

  it('falls back to first enabled tab for tabindex when modelValue is invalid', () => {
    const wrapper = mount(MTabs, {
      props: {
        tabs,
        modelValue: 'unknown-id',
      },
    });

    const buttons = wrapper.findAll('button.mc-tabs__tab');
    expect(buttons[0].attributes('tabindex')).toBe('0');
    expect(buttons[1].attributes('tabindex')).toBe('-1');
    expect(buttons[2].attributes('tabindex')).toBe('-1');
  });

  it('sets disabled and aria-disabled attributes on disabled tabs', () => {
    const wrapper = mount(MTabs, {
      props: {
        tabs: [
          { id: '1', label: 'Tab 1' },
          { id: '2', label: 'Tab 2', disabled: true },
        ],
        modelValue: '1',
      },
    });

    const buttons = wrapper.findAll('button.mc-tabs__tab');
    expect(buttons[0].attributes('aria-disabled')).toBeUndefined();
    expect(buttons[1].attributes('aria-disabled')).toBe('true');
  });

  it('ArrowRight moves selection to the next tab', async () => {
    const wrapper = mount(MTabs, {
      props: { tabs, modelValue: '1' },
    });

    const buttons = wrapper.findAll('button.mc-tabs__tab');
    await buttons[0].trigger('keydown', { key: 'ArrowRight' });

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

  it('ArrowLeft moves selection to the previous tab', async () => {
    const wrapper = mount(MTabs, {
      props: { tabs, modelValue: '2' },
    });

    const buttons = wrapper.findAll('button.mc-tabs__tab');
    await buttons[1].trigger('keydown', { key: 'ArrowLeft' });

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

  it('ArrowRight wraps from last to first tab', async () => {
    const wrapper = mount(MTabs, {
      props: { tabs, modelValue: '3' },
    });

    const buttons = wrapper.findAll('button.mc-tabs__tab');
    await buttons[2].trigger('keydown', { key: 'ArrowRight' });

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

  it('Home moves selection to the first tab', async () => {
    const wrapper = mount(MTabs, {
      props: { tabs, modelValue: '3' },
    });

    const buttons = wrapper.findAll('button.mc-tabs__tab');
    await buttons[2].trigger('keydown', { key: 'Home' });

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

  it('End moves selection to the last tab', async () => {
    const wrapper = mount(MTabs, {
      props: { tabs, modelValue: '1' },
    });

    const buttons = wrapper.findAll('button.mc-tabs__tab');
    await buttons[0].trigger('keydown', { key: 'End' });

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

  it('does not react to ArrowUp and ArrowDown keys', async () => {
    const wrapper = mount(MTabs, {
      props: { tabs, modelValue: '1' },
    });

    const buttons = wrapper.findAll('button.mc-tabs__tab');
    await buttons[0].trigger('keydown', { key: 'ArrowDown' });
    await buttons[0].trigger('keydown', { key: 'ArrowUp' });

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

  it('ArrowRight skips disabled tabs', async () => {
    const wrapper = mount(MTabs, {
      props: {
        tabs: [
          { id: '1', label: 'Tab 1' },
          { id: '2', label: 'Tab 2', disabled: true },
          { id: '3', label: 'Tab 3' },
        ],
        modelValue: '1',
      },
    });

    const buttons = wrapper.findAll('button.mc-tabs__tab');
    await buttons[0].trigger('keydown', { key: 'ArrowRight' });

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

  it('does not emit on keydown when all tabs are disabled', async () => {
    const wrapper = mount(MTabs, {
      props: {
        tabs: [
          { id: '1', label: 'Tab 1', disabled: true },
          { id: '2', label: 'Tab 2', disabled: true },
        ],
        modelValue: '1',
      },
    });

    const buttons = wrapper.findAll('button.mc-tabs__tab');
    await buttons[0].trigger('keydown', { key: 'ArrowRight' });
    await buttons[0].trigger('keydown', { key: 'Home' });

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

  it('renders icon component when icon prop is provided', () => {
    const DummyIcon = markRaw(
      defineComponent({
        name: 'DummyIcon',
        render() {
          return h('svg', { class: 'dummy-icon' }, [
            h('circle', { cx: 10, cy: 10, r: 10 }),
          ]);
        },
      }),
    );

    const tabsWithIcon = [
      { id: '1', label: 'Tab 1', icon: DummyIcon },
      { id: '2', label: 'Tab 2' },
    ];

    const wrapper = mount(MTabs, {
      props: {
        tabs: tabsWithIcon,
      },
    });

    const firstTabButton = wrapper.findAll('button.mc-tabs__tab')[0];
    expect(firstTabButton.findComponent(DummyIcon).exists()).toBe(true);
    expect(firstTabButton.find('svg.dummy-icon').exists()).toBe(true);

    const secondTabButton = wrapper.findAll('button.mc-tabs__tab')[1];
    expect(secondTabButton.findComponent(DummyIcon).exists()).toBe(false);
  });

  it('renders badge when badge prop is provided', () => {
    const tabsWithBadges = [
      { id: '1', label: 'Tab 1', badge: 5 },
      { id: '2', label: 'Tab 2' },
    ];

    const wrapper = mount(MTabs, {
      props: {
        tabs: tabsWithBadges,
      },
    });

    const firstTabButton = wrapper.findAll('button.mc-tabs__tab')[0];
    expect(firstTabButton.find('span.mc-tabs__badge').exists()).toBe(true);
    expect(
      firstTabButton.find('span.mc-tabs__badge .mc-number-badge').text(),
    ).toBe('5');

    const secondTabButton = wrapper.findAll('button.mc-tabs__tab')[1];
    expect(secondTabButton.find('span.mc-tabs__badge').exists()).toBe(false);
  });
});
