import { fireEvent, render } from '@testing-library/react';
import React from 'react';
import { act } from 'react-dom/test-utils';
import { describe, expect, it, vi } from 'vitest';
import { noop } from '../../../../../helpers/utils';
import { CreateInputRendererConfig } from '../renderers.model';
import { createInputRenderer } from './createInputRenderer';

interface RendererWrapperProps extends CreateInputRendererConfig {
  currentValue?: unknown;
  error?: string | undefined;
  onValueChange?: (value: unknown) => void;
  disabled?: boolean;
}

const RendererWrapper: React.FC<RendererWrapperProps> = ({
  id,
  name,
  type,
  placeholder,

  transform,
  currentValue,
  error,
  onValueChange = noop,
  disabled = false,
}) => {
  return (
    <>
      {createInputRenderer({
        id,
        name,
        type,
        placeholder,
        transform,
      })(currentValue, error, onValueChange, disabled)}
    </>
  );
};

describe('createInputRenderer', () => {
  it('renders the component without crashing', () => {
    const { container } = render(<RendererWrapper />);
    expect(container).toBeTruthy();
  });

  it(`sets 'input' value to current value`, () => {
    const mockValue = 'test-value';
    const { container } = render(<RendererWrapper currentValue={mockValue} />);

    const input = container.querySelector('input') as HTMLInputElement;

    expect(input.value).toBe(mockValue);
  });

  it(`emits 'onValueChange' with the new value when 'input' value has changed`, () => {
    const onValueChangeSpy = vi.fn();
    const mockValueUpdated = 'updated-test-value';
    const { container } = render(
      <RendererWrapper onValueChange={onValueChangeSpy} />,
    );

    const input = container.querySelector('input') as HTMLInputElement;

    expect(input.value).toBe('');

    act(() => {
      fireEvent.change(input, { target: { value: mockValueUpdated } });
    });

    expect(onValueChangeSpy).toHaveBeenCalledTimes(1);
    expect(onValueChangeSpy).toHaveBeenCalledWith(mockValueUpdated);
  });

  it(`formats the value when a 'transformer' is set`, () => {
    const onValueChangeSpy = vi.fn();
    const mockStringifiedNumber = '5';
    const stringToNumberTransformer = (value: string): number => Number(value);

    const { container } = render(
      <RendererWrapper
        onValueChange={onValueChangeSpy}
        transform={stringToNumberTransformer}
      />,
    );

    const input = container.querySelector('input') as HTMLInputElement;

    act(() => {
      fireEvent.change(input, {
        target: { value: mockStringifiedNumber },
      });
    });

    expect(onValueChangeSpy).toHaveBeenCalledTimes(1);
    expect(onValueChangeSpy).toHaveBeenCalledWith(
      Number(mockStringifiedNumber),
    );
  });
});
