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

describe('MSegmentedControl.vue', () => {
  const segments = [
    { id: '1', label: 'First' },
    { id: '2', label: 'Second' },
    { id: '3', label: 'Third' },
  ];

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

    const buttons = wrapper.findAll('button');
    expect(buttons).toHaveLength(segments.length);
    buttons.forEach((btn, i) => {
      expect(btn.text()).toBe(segments[i].label);
    });
  });

  it('sets default active segment based on modelValue prop', () => {
    const wrapper = mount(MSegmentedControl, {
      props: { segments, modelValue: '1' },
    });

    const buttons = wrapper.findAll('button');
    expect(buttons[0].classes()).toContain(
      'mc-segmented-control__segment--selected',
    );
    expect(buttons[0].attributes('aria-checked')).toBe('true');
  });

  describe('Segment selection - string type model', () => {
    it('emits update:modelValue and changes active segment on click', async () => {
      const wrapper = mount(MSegmentedControl, {
        props: { segments, modelValue: '1' },
      });

      const buttons = wrapper.findAll('button');
      await buttons[2].trigger('click');

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

      expect(buttons[2].classes()).toContain(
        'mc-segmented-control__segment--selected',
      );
      expect(buttons[2].attributes('aria-checked')).toBe('true');
    });

    it('does not emit update event if clicking already active segment', async () => {
      const wrapper = mount(MSegmentedControl, {
        props: { segments, modelValue: '2' },
      });

      const buttons = wrapper.findAll('button');
      await buttons[1].trigger('click');

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

  describe('Segment selection - number type model', () => {
    it('emits update:modelValue and changes active segment on click', async () => {
      const wrapper = mount(MSegmentedControl, {
        props: { segments, modelValue: 0 },
      });

      const buttons = wrapper.findAll('button');
      await buttons[2].trigger('click');

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

      expect(buttons[2].classes()).toContain(
        'mc-segmented-control__segment--selected',
      );
      expect(buttons[2].attributes('aria-checked')).toBe('true');
    });

    it('does not emit update event if clicking already active segment', async () => {
      const wrapper = mount(MSegmentedControl, {
        props: { segments, modelValue: 1 },
      });

      const buttons = wrapper.findAll('button');
      await buttons[1].trigger('click');

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

  it('applies full width class when full prop is true', () => {
    const wrapper = mount(MSegmentedControl, {
      props: { segments, full: true },
    });

    expect(wrapper.classes()).toContain('mc-segmented-control--full');
  });

  it('applies size class when size prop is "m"', () => {
    const wrapper = mount(MSegmentedControl, {
      props: { segments, size: 'm' },
    });

    expect(wrapper.classes()).toContain('mc-segmented-control--m');
  });

  it('does not apply size class when size is "s" (default)', () => {
    const wrapper = mount(MSegmentedControl, {
      props: { segments },
    });

    expect(wrapper.classes()).not.toContain('mc-segmented-control--s');
  });

  it('updates active segment when modelValue prop changes', async () => {
    const wrapper = mount(MSegmentedControl, {
      props: { segments, modelValue: '1' },
    });

    const buttons = wrapper.findAll('button');
    expect(buttons[0].classes()).toContain(
      'mc-segmented-control__segment--selected',
    );

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

    expect(buttons[2].classes()).toContain(
      'mc-segmented-control__segment--selected',
    );
    expect(buttons[2].attributes('aria-checked')).toBe('true');
  });

  it('adds role="radio" attribute to buttons', () => {
    const wrapper = mount(MSegmentedControl, {
      props: { segments },
    });

    const buttons = wrapper.findAll('button');
    buttons.forEach((button) => {
      expect(button.attributes('role')).toBe('radio');
    });
  });

  it('forwards attrs to the radiogroup so callers can name the group', () => {
    const wrapper = mount(MSegmentedControl, {
      props: { segments },
      attrs: { 'aria-label': 'View options' },
    });
    expect(wrapper.find('[role="radiogroup"]').attributes('aria-label')).toBe(
      'View options',
    );
  });

  describe('Keyboard navigation (radiogroup pattern)', () => {
    it('selected button has tabindex="0", others have tabindex="-1"', () => {
      const wrapper = mount(MSegmentedControl, {
        props: { segments, modelValue: '2' },
      });
      const buttons = wrapper.findAll('button');
      expect(buttons[0].attributes('tabindex')).toBe('-1');
      expect(buttons[1].attributes('tabindex')).toBe('0');
      expect(buttons[2].attributes('tabindex')).toBe('-1');
    });

    it('keeps the first segment tabbable when modelValue does not match any segment', () => {
      const wrapper = mount(MSegmentedControl, {
        props: { segments, modelValue: 'unknown' },
      });
      const buttons = wrapper.findAll('button');
      expect(buttons[0].attributes('tabindex')).toBe('0');
      expect(buttons[1].attributes('tabindex')).toBe('-1');
      expect(buttons[2].attributes('tabindex')).toBe('-1');
      expect(buttons[0].attributes('aria-checked')).toBe('false');
    });

    it('ArrowRight selects and focuses the next segment', async () => {
      const wrapper = mount(MSegmentedControl, {
        attachTo: document.body,
        props: { segments, modelValue: '1' },
      });
      const buttons = wrapper.findAll('button');
      await buttons[0].element.focus();
      await buttons[0].trigger('keydown', { key: 'ArrowRight' });
      await nextTick();
      expect(wrapper.emitted('update:modelValue')![0]).toEqual(['2']);
      expect(document.activeElement).toBe(buttons[1].element);
      wrapper.unmount();
    });

    it('ArrowLeft selects and focuses the previous segment', async () => {
      const wrapper = mount(MSegmentedControl, {
        attachTo: document.body,
        props: { segments, modelValue: '2' },
      });
      const buttons = wrapper.findAll('button');
      await buttons[1].trigger('keydown', { key: 'ArrowLeft' });
      expect(wrapper.emitted('update:modelValue')![0]).toEqual(['1']);
      wrapper.unmount();
    });

    it('ArrowRight wraps around from last to first segment', async () => {
      const wrapper = mount(MSegmentedControl, {
        attachTo: document.body,
        props: { segments, modelValue: '3' },
      });
      const buttons = wrapper.findAll('button');
      await buttons[2].trigger('keydown', { key: 'ArrowRight' });
      expect(wrapper.emitted('update:modelValue')![0]).toEqual(['1']);
      wrapper.unmount();
    });

    it('Home selects the first segment', async () => {
      const wrapper = mount(MSegmentedControl, {
        attachTo: document.body,
        props: { segments, modelValue: '3' },
      });
      const buttons = wrapper.findAll('button');
      await buttons[2].trigger('keydown', { key: 'Home' });
      expect(wrapper.emitted('update:modelValue')![0]).toEqual(['1']);
      wrapper.unmount();
    });

    it('End selects the last segment', async () => {
      const wrapper = mount(MSegmentedControl, {
        attachTo: document.body,
        props: { segments, modelValue: '1' },
      });
      const buttons = wrapper.findAll('button');
      await buttons[0].trigger('keydown', { key: 'End' });
      expect(wrapper.emitted('update:modelValue')![0]).toEqual(['3']);
      wrapper.unmount();
    });
  });
});
