import React, { FocusEvent, ReactElement } from 'react';
import { fireEvent } from '@testing-library/react';
import renderWithTheme from '../../../testUtils/renderWithTheme';

import Input from '..';
import Typography from '../../Typography';
import Button from '../../Button';

describe('rendering', () => {
  it('renders input', () => {
    const { getByLabelText } = renderWithTheme(
      <Typography.Text tagName="label">
        Email
        <Input />
      </Typography.Text>
    );
    expect(getByLabelText('Email')).toBeInTheDocument();
  });

  describe('Affixes rendering', () => {
    describe('Affixes are string', () => {
      it('renders icon with prefix', () => {
        const { container } = renderWithTheme(<Input prefix="user" />);
        expect(container.querySelectorAll('i')).toHaveLength(1);
      });

      it('renders icon with suffix', () => {
        const { container } = renderWithTheme(<Input suffix="user" />);
        expect(container.querySelectorAll('i')).toHaveLength(1);
      });

      it('renders icon with prefix & suffix', () => {
        const { container } = renderWithTheme(
          <Input prefix="heart" suffix="user" />
        );
        expect(container.querySelectorAll('i')).toHaveLength(2);
      });
    });

    describe('Affixes are custom react element', () => {
      it('renders icon with prefix', () => {
        const { getByText } = renderWithTheme(
          <Input prefix={<span>My prefix</span>} />
        );
        expect(getByText('My prefix')).toBeInTheDocument();
      });

      it('renders icon with suffix', () => {
        const { getByText } = renderWithTheme(
          <Input suffix={<span>My suffix</span>} />
        );
        expect(getByText('My suffix')).toBeInTheDocument();
      });

      it('renders icon with prefix & suffix', () => {
        const { getByText } = renderWithTheme(
          <Input
            suffix={<span>My suffix</span>}
            prefix={<span>My prefix</span>}
          />
        );
        expect(getByText('My prefix')).toBeInTheDocument();
        expect(getByText('My suffix')).toBeInTheDocument();
      });
    });
  });
});

describe('interaction', () => {
  it('works as uncontrolled component', () => {
    const { getByLabelText } = renderWithTheme(
      <Typography.Text tagName="label">
        Email
        <Input />
      </Typography.Text>
    );
    expect((getByLabelText('Email') as HTMLInputElement).value).toEqual('');
    fireEvent.change(getByLabelText('Email'), {
      target: { value: 'new value' },
    });
    expect((getByLabelText('Email') as HTMLInputElement).value).toEqual(
      'new value'
    );
  });

  it('works as controlled component', () => {
    const onChange = jest.fn();
    const { getByLabelText } = renderWithTheme(
      <Typography.Text tagName="label">
        Email
        <Input value="fixed" onChange={(e): void => onChange(e.target.value)} />
      </Typography.Text>
    );
    expect((getByLabelText('Email') as HTMLInputElement).value).toEqual(
      'fixed'
    );
    fireEvent.change(getByLabelText('Email'), {
      target: { value: 'new value' },
    });
    expect(onChange).toHaveBeenCalledTimes(1);
    expect(onChange).toHaveBeenCalledWith('new value');
    expect((getByLabelText('Email') as HTMLInputElement).value).toEqual(
      'fixed'
    );
  });

  it('can get auto focus', () => {
    const onFocus = jest.fn();
    renderWithTheme(<Input onFocus={onFocus} autoFocus />);
    expect(onFocus).toHaveBeenCalledTimes(1);
  });

  it('can be controlled via ref', () => {
    const mockOnFocus = jest.fn();
    const Component = ({
      onFocus,
    }: {
      onFocus?: (e: FocusEvent<HTMLInputElement>) => void;
    }): ReactElement => {
      const ref = React.useRef<HTMLInputElement>(null);
      return (
        <>
          <Input onFocus={onFocus} ref={ref} />
          <Button
            text="Focus it"
            onClick={(): void => {
              if (ref.current !== null) {
                ref.current.focus();
              }
            }}
          />
        </>
      );
    };
    const { getByText } = renderWithTheme(<Component onFocus={mockOnFocus} />);
    expect(mockOnFocus).not.toHaveBeenCalled();
    fireEvent.click(getByText('Focus it'));
    expect(mockOnFocus).toHaveBeenCalledTimes(1);
  });
});
