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

describe('MPincode component', () => {
  if (typeof ClipboardEvent === 'undefined') {
    global.ClipboardEvent = class ClipboardEvent extends Event {
      clipboardData: DataTransfer;
      constructor(type: string, eventInitDict?: ClipboardEventInit) {
        super(type, eventInitDict);
        this.clipboardData =
          (eventInitDict && eventInitDict.clipboardData) || new DataTransfer();
      }
      // eslint-disable-next-line
    } as any;
  }

  it('renders correct number of input fields based on length', () => {
    const wrapper = mount(MPincode, {
      props: {
        id: 'otp',
        length: 6,
        modelValue: '',
      },
    });

    const inputs = wrapper.findAll('input');
    expect(inputs).toHaveLength(6);
  });

  it('updates modelValue as user types', async () => {
    const wrapper = mount(MPincode, {
      props: {
        id: 'otp',
        length: 4,
        modelValue: '',
        'onUpdate:modelValue': vi.fn(),
      },
    });

    const inputs = wrapper.findAll('input');
    await inputs[0].setValue('1');
    await inputs[1].setValue('2');
    await inputs[2].setValue('3');
    await inputs[3].setValue('4');

    expect(wrapper.emitted('update:modelValue')).toBeTruthy();
    const emitted = wrapper.emitted('update:modelValue');
    const lastEmitted = emitted?.[emitted.length - 1][0];
    expect(lastEmitted).toBe('1234');
  });

  it('moves focus to next input on input', async () => {
    const wrapper = mount(MPincode, {
      attachTo: document.body, // Needed for focus
      props: {
        id: 'otp',
        length: 4,
        modelValue: '',
      },
    });

    const inputs = wrapper.findAll('input');
    await inputs[0].setValue('5');
    expect(document.activeElement).toBe(inputs[1].element);
  });

  it('moves focus to previous input on backspace if current is empty', async () => {
    const wrapper = mount(MPincode, {
      attachTo: document.body,
      props: {
        id: 'otp',
        length: 4,
        modelValue: '',
      },
    });

    const inputs = wrapper.findAll('input');
    await inputs[0].setValue('5');
    await inputs[1].element.focus();
    const event = new KeyboardEvent('keydown', { key: 'Backspace' });
    inputs[1].element.dispatchEvent(event);
    await wrapper.vm.$nextTick();

    expect(document.activeElement).toBe(inputs[0].element);
  });

  it('renders invalid class when isInvalid is true', () => {
    const wrapper = mount(MPincode, {
      props: {
        id: 'otp',
        isInvalid: true,
      },
    });

    expect(wrapper.classes()).toContain('is-invalid');
  });

  it('disables inputs when disabled is true', () => {
    const wrapper = mount(MPincode, {
      props: {
        id: 'otp',
        disabled: true,
      },
    });

    const inputs = wrapper.findAll('input');
    for (const input of inputs) {
      expect(input.attributes('disabled')).toBeDefined();
    }
  });

  it('makes inputs readonly when readonly is true', () => {
    const wrapper = mount(MPincode, {
      props: {
        id: 'otp',
        readonly: true,
      },
    });

    const inputs = wrapper.findAll('input');
    for (const input of inputs) {
      expect(input.attributes('readonly')).toBeDefined();
    }
  });
});
