import * as React from 'react';
import { render, screen } from '@testing-library/react';
import userEvent from '@testing-library/user-event';

import { Input } from '../Input';
import { ComponentClassNames } from '../../shared';

describe('Input component', () => {
  it('should render custom classname for Input', async () => {
    render(<Input className="custom-class" />);

    const input = await screen.findByRole('textbox');
    expect(input).toHaveClass('custom-class');
    expect(input).toHaveClass(ComponentClassNames.Input);
  });

  it('should render variation classes for Input', async () => {
    render(
      <div>
        <Input testId="quiet" variation="quiet" />
      </div>
    );

    const quiet = await screen.findByTestId('quiet');

    expect(quiet.classList).toContain(`${ComponentClassNames['Input']}--quiet`);
  });

  it('should render error classes for Input', async () => {
    render(
      <div>
        <Input testId="error" hasError />
      </div>
    );

    const error = await screen.findByTestId('error');

    expect(error.classList).toContain(`${ComponentClassNames['Input']}--error`);
  });

  it('should render size classes for Input', async () => {
    render(
      <div>
        <Input testId="small" size="small" />
        <Input testId="large" size="large" />
      </div>
    );

    const small = await screen.findByTestId('small');
    const large = await screen.findByTestId('large');

    expect(small.classList).toContain(`${ComponentClassNames['Input']}--small`);
    expect(large.classList).toContain(`${ComponentClassNames['Input']}--large`);
  });

  it('should render expected classname, id Input field', async () => {
    render(
      <Input
        id="testField"
        testId="testId"
        className="my-input"
        defaultValue="Hello there"
      />
    );

    const input = await screen.findByRole('textbox');
    expect(input).toHaveClass('my-input');
    expect(input.id).toBe('testField');
  });

  it('should forward ref to DOM element', async () => {
    const ref = React.createRef<HTMLInputElement>();

    render(<Input ref={ref} />);
    await screen.findByRole('textbox');
    expect(ref.current?.nodeName).toBe('INPUT');
  });

  it('should render the state attributes', async () => {
    render(<Input size="small" hasError isDisabled isReadOnly isRequired />);

    const input = await screen.findByRole('textbox');
    expect(input).toHaveAttribute('disabled');
    expect(input).toHaveAttribute('readonly');
    expect(input).toHaveAttribute('required');
  });

  it('should set size and variation data attributes', async () => {
    render(<Input size="small" variation="quiet" />);

    const input = await screen.findByRole('textbox');
    expect(input.dataset['size']).toBe('small');
    expect(input.dataset['variation']).toBe('quiet');
  });

  it('can set defaultChecked (uncontrolled)', async () => {
    render(<Input type="radio" defaultChecked />);

    const radio = await screen.findByRole('radio');
    expect(radio).toBeChecked();
  });

  it('can set defaultValue (uncontrolled)', async () => {
    render(<Input defaultValue="test" />);

    const input = await screen.findByRole<HTMLInputElement>('textbox');
    expect(input.value).toBe('test');
  });

  it('can set checked (controlled component)', async () => {
    const onChange = jest.fn();
    render(<Input type="radio" checked onChange={onChange} />);

    const radio = await screen.findByRole('radio');
    expect(radio).toBeChecked();
  });

  it('can set value (controlled component)', async () => {
    render(<Input value="test" onChange={jest.fn()} />);

    const input = await screen.findByRole<HTMLInputElement>('textbox');
    expect(input.value).toBe('test');
  });

  it('show add aria-invalid attribute to input when hasError', async () => {
    render(<Input id="testField" hasError />);
    const input = await screen.findByRole('textbox');
    expect(input).toHaveAttribute('aria-invalid');
  });

  it('should fire event handlers', async () => {
    const onChange = jest.fn();
    const onInput = jest.fn();
    const onPaste = jest.fn();
    render(<Input onChange={onChange} onInput={onInput} onPaste={onPaste} />);
    const input = await screen.findByRole('textbox');
    userEvent.type(input, 'hello');
    userEvent.paste(input, 'there');
    expect(onChange).toHaveBeenCalled();
    expect(onInput).toHaveBeenCalled();
    expect(onPaste).toHaveBeenCalled();
  });
});