import * as React from 'react';
import {
  render,
  screen
} from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import * as TextControlBase from '@tesseract-design/web-base-textcontrol';
import { vi } from 'vitest';
import {
  TextInput, TextInputType,
} from '.';

vi.mock('@tesseract-design/web-base-textcontrol');

describe('TextInput', () => {
  it('renders a textbox', () => {
    render(
      <TextInput />
    );
    const textbox = screen.getByRole('textbox');
    expect(textbox).toBeInTheDocument();
    expect(textbox).toHaveProperty('type', 'text');
  });

  it('renders a border', () => {
    render(
      <TextInput
        border
      />
    );
    const border = screen.getByTestId('border');
    expect(border).toBeInTheDocument();
  });

  it('renders a label', () => {
    render(
      <TextInput
        label="foo"
      />
    );
    const textbox = screen.getByLabelText('foo');
    expect(textbox).toBeInTheDocument();
    const label = screen.getByTestId('label');
    expect(label).toHaveTextContent('foo');
  });

  it('renders a hidden label', () => {
    render(
      <TextInput
        label="foo"
        hiddenLabel
      />
    );
    const textbox = screen.getByLabelText('foo');
    expect(textbox).toBeInTheDocument();
    const label = screen.queryByTestId('label');
    expect(label).toBeNull();
  });

  it('renders a hint', () => {
    render(
      <TextInput
        hint="foo"
      />
    );
    const hint = screen.getByTestId('hint');
    expect(hint).toBeInTheDocument();
  });

  it('renders an indicator', () => {
    render(
      <TextInput
        indicator={
          <div data-testid="indicator" />
        }
      />
    );
    const indicator = screen.getByTestId('indicator');
    expect(indicator).toBeInTheDocument();
  });

  describe.each(Object.values(TextControlBase.TextControlSize))('on %s size', (size) => {
    it('renders input styles', () => {
      render(
        <TextInput
          size={size}
        />
      );

      expect(TextControlBase.Input).toBeCalledWith(expect.objectContaining({
        size,
      }));
    });

    it('renders hint styles', () => {
      render(
        <TextInput
          size={size}
          hint="hint"
        />
      );

      expect(TextControlBase.HintWrapper).toBeCalledWith(expect.objectContaining({
        size,
      }));
    });

    it('renders indicator styles', () => {
      render(
        <TextInput
          size={size}
          indicator={
            <div data-testid="indicator" />
          }
        />
      );

      expect(TextControlBase.IndicatorWrapper).toBeCalledWith(expect.objectContaining({
        size,
      }));
    });
  });

  it('renders a block textbox', () => {
    render(
      <TextInput
        block
      />
    );

    expect(TextControlBase.Root).toBeCalledWith(expect.objectContaining({
      block: true,
    }));
  });

  it.each(Object.values(TextInputType))('renders a textbox with type %s', (buttonType) => {
    render(
      <TextInput
        type={buttonType}
      />
    );
    const textbox: HTMLButtonElement = screen.getByTestId('input');
    expect(textbox).toHaveProperty('type', buttonType);
  });

  describe.each(Object.values(TextControlBase.TextControlStyle))('on %s style', (style) => {
    it('renders input styles', () => {
      render(
        <TextInput
          style={style}
        />
      );

      expect(TextControlBase.Input).toBeCalledWith(expect.objectContaining({
        style,
      }));
    });

    it('renders hint styles', () => {
      render(
        <TextInput
          style={style}
          hint="hint"
        />
      );

      expect(TextControlBase.HintWrapper).toBeCalledWith(expect.objectContaining({
        style,
      }));
    });

    it('renders indicator styles', () => {
      render(
        <TextInput
          style={style}
          indicator={
            <div
              data-testid="indicator"
            />
          }
        />
      );

      expect(TextControlBase.IndicatorWrapper).toBeCalledWith(expect.objectContaining({
        style,
      }));
    });
  });

  it('handles change events', () => {
    const onChange = vi.fn().mockImplementationOnce((e) => { e.preventDefault() });
    render(
      <TextInput
        onChange={onChange}
      />
    );
    const textbox: HTMLInputElement = screen.getByRole('textbox');
    userEvent.type(textbox, 'foobar');
    expect(onChange).toBeCalled();
  });

  it('handles input events', () => {
    const onInput = vi.fn().mockImplementationOnce((e) => { e.preventDefault() });
    render(
      <TextInput
        onInput={onInput}
      />
    );
    const textbox: HTMLInputElement = screen.getByTestId('input');
    userEvent.type(textbox, 'foobar');
    expect(onInput).toBeCalled();
  });
});
