import { shallowMount, mount } from '@vue/test-utils';
import { describe, it, expect } from 'vitest';
import { nextTick } from 'vue';
import MStarRating from './MStarRating.vue';
import { StarFilled24, StarHalf24 } from '@mozaic-ds/icons-vue';

describe('MStarRating', () => {
  it('renders 5 stars by default', () => {
    const wrapper = shallowMount(MStarRating, { props: { modelValue: 0 } });
    const stars = wrapper.findAll('.mc-star-rating__icon');
    expect(stars.length).toBe(5);
  });

  it('renders 1 star when compact mode is enabled', () => {
    const wrapper = shallowMount(MStarRating, {
      props: { modelValue: 0, compact: true },
    });
    const stars = wrapper.findAll('.mc-star-rating__icon');
    expect(stars.length).toBe(1);
  });

  it('does not render half star on input mode', async () => {
    const wrapper = shallowMount(MStarRating, {
      props: { modelValue: 0, readonly: false },
    });
    const stars = wrapper.findAll('.mc-star-rating__icon');
    const first = stars[0];
    await first.trigger('pointermove', { pointerType: 'mouse' });
    expect(wrapper.findComponent(StarHalf24).exists()).toBe(false);
  });

  it('render half star on readonly mode if possible', () => {
    const wrapper = shallowMount(MStarRating, {
      props: { modelValue: 1.5, readonly: true },
    });
    const filledStars = wrapper.findAllComponents(StarFilled24);
    const halfStars = wrapper.findAllComponents(StarHalf24);
    expect(filledStars.length).toBe(1);
    expect(halfStars.length).toBe(1);
  });

  it('emits update:modelValue with the correct value when clicking a star', async () => {
    const wrapper = shallowMount(MStarRating, {
      props: { modelValue: 2, readonly: false },
    });
    const stars = wrapper.findAll('.mc-star-rating__icon');
    const first = stars[0];
    await first.trigger('click');
    const emitted = wrapper.emitted('update:modelValue') || [];
    expect(emitted.length).toBe(1);
    expect(emitted[0][0]).toBe(1);
  });

  it('emits the correct values when pressing ArrowRight', async () => {
    const wrapper = shallowMount(MStarRating, {
      props: { modelValue: 2, readonly: false },
    });

    const ratingInput = wrapper.find('.mc-star-rating__wrapper');

    await ratingInput.trigger('keydown', { key: 'ArrowRight' });
    const emitted = wrapper.emitted('update:modelValue') || [];
    expect(emitted.length).toBe(1);
    expect(emitted[0][0]).toBe(3);
  });

  it('emits the correct values when pressing ArrowLeft', async () => {
    const wrapper = shallowMount(MStarRating, {
      props: { modelValue: 2, readonly: false },
    });

    const ratingInput = wrapper.find('.mc-star-rating__wrapper');
    await ratingInput.trigger('keydown', { key: 'ArrowLeft' });
    const emitted = wrapper.emitted('update:modelValue') || [];
    expect(emitted.length).toBe(1);
    expect(emitted[0][0]).toBe(1);
  });

  it('does not do anything if the wrong key is pressed', async () => {
    const wrapper = shallowMount(MStarRating, {
      props: { modelValue: 2, readonly: false },
    });
    const ratingInput = wrapper.find('.mc-star-rating__wrapper');
    await ratingInput.trigger('keydown', { key: 'ArrowUp' });
    const emitted = wrapper.emitted('update:modelValue') || [];
    expect(emitted.length).toBe(0);
  });

  it('resets hover to null on mouseleave (aria-label falls back to modelValue)', async () => {
    const wrapper = shallowMount(MStarRating, {
      props: { modelValue: 2, size: 'm', readonly: false },
    });

    const stars = wrapper.findAll('.mc-star-rating__icon');
    const first = stars[0];

    await first.trigger('pointermove', { pointerType: 'mouse' });
    await nextTick();

    // aria-label should reflect hovered value (0.5) not modelValue (2)
    const root = wrapper.find('[role="slider"]');
    expect(root.attributes('aria-label')).toContain('1');

    // trigger mouseleave on root, hover should be cleared and aria-label revert to modelValue
    await root.trigger('mouseleave');
    await nextTick();
    expect(root.attributes('aria-label')).toContain('2');
  });

  it('ignores non-mouse pointermove events for hover state', async () => {
    const wrapper = shallowMount(MStarRating, {
      props: { modelValue: 3, size: 'm', readonly: false },
    });

    const stars = wrapper.findAll('.mc-star-rating__icon');
    const first = stars[0];
    const root = wrapper.find('[role="slider"]');

    await first.trigger('pointermove', { pointerType: 'touch' });
    await nextTick();

    expect(root.attributes('aria-label')).toContain('3');
  });

  it('resets hover to null on blur (aria-label falls back to modelValue)', async () => {
    const wrapper = shallowMount(MStarRating, {
      props: { modelValue: 3, size: 'm', readonly: false },
    });

    const stars = wrapper.findAll('.mc-star-rating__icon');
    const secondStar = stars[1];

    const root = wrapper.find('[role="slider"]');

    await root.trigger('focus');
    await secondStar.trigger('pointermove', { pointerType: 'mouse' });
    await nextTick();
    expect(root.attributes('aria-label')).toContain('2');

    // blur should clear hover and revert aria-label to modelValue (3)
    await root.trigger('blur');
    await nextTick();
    expect(root.attributes('aria-label')).toContain('3');
  });

  it('renders information text when text prop is provided', () => {
    const wrapper = shallowMount(MStarRating, {
      props: { modelValue: 3, text: 'Note publique' },
    });

    const info = wrapper.find('.mc-star-rating__info');
    expect(info.exists()).toBe(true);
    expect(info.text()).toBe('Note publique');
  });

  it('renders href when href prop is provided and text is not', () => {
    const wrapper = shallowMount(MStarRating, {
      props: { modelValue: 2, href: '/voir' },
    });

    const info = wrapper.find('.mc-star-rating__info');
    expect(info.exists()).toBe(true);
    expect(info.text()).toBe('/voir');
  });

  it('does not render info span when neither text nor href is provided', () => {
    const wrapper = shallowMount(MStarRating, {
      props: { modelValue: 1 },
    });

    const info = wrapper.find('.mc-star-rating__info');
    expect(info.exists()).toBe(false);
  });

  it('renders router-link when href and router are provided', () => {
    const wrapper = mount(MStarRating, {
      props: { modelValue: 3, href: '/path', router: true },
      global: { stubs: ['router-link'] },
    });

    expect(wrapper.element.tagName.toLowerCase()).toBe('router-link-stub');
  });

  it('renders an anchor when href is provided and router is falsy', () => {
    const wrapper = shallowMount(MStarRating, {
      props: { modelValue: 2, href: '#', router: false },
    });

    expect(wrapper.element.tagName.toLowerCase()).toBe('a');
  });

  it('renders a div when no href is provided', () => {
    const wrapper = shallowMount(MStarRating, {
      props: { modelValue: 1 },
    });

    expect(wrapper.element.tagName.toLowerCase()).toBe('div');
  });
});
