import Info from '../info/Info';
import { Input } from '../inputs/Input';
import { mockMatchMedia, render, screen, userEvent } from '../test-utils';

import { Field } from './Field';

mockMatchMedia();

describe('Field', () => {
  it('should render label', () => {
    render(
      <Field label="Phone number">
        <Input />
      </Field>,
    );

    expect(screen.getByLabelText(/Phone number/)).toBeInTheDocument();
    expect(screen.getByRole('textbox')).not.toHaveAttribute('aria-describedby');
  });

  it('should render help text if provided', () => {
    render(
      <Field label="Phone number" hint="This is help text">
        <Input />
      </Field>,
    );

    const textbox = screen.getByRole('textbox', { description: /This is help text/ });
    expect(textbox).toBeInTheDocument();
    expect(textbox).not.toBeInvalid();
  });

  it('should render error text if provided', () => {
    render(
      <Field label="Phone number" error="This is error text">
        <Input />
      </Field>,
    );

    const textbox = screen.getByRole('textbox', { description: 'This is error text' });
    expect(textbox).toBeInTheDocument();
    expect(textbox).toBeInvalid();
  });

  it('should shows error text and help text when both are provided', () => {
    render(
      <Field label="Phone number" error="This is error text" hint="This is help text">
        <Input />
      </Field>,
    );

    expect(screen.getByRole('textbox', { description: /This is error text/ })).toBeInTheDocument();
    expect(screen.getByText('This is help text')).toBeInTheDocument();
    expect(screen.getByLabelText(/Phone number/)).toHaveAttribute('aria-describedby');
  });

  it('should shows message and description when both are provided', () => {
    render(
      <Field
        label="Phone number"
        description="This is help text"
        sentiment="negative"
        message="This is error text"
      >
        <Input />
      </Field>,
    );

    expect(screen.getByRole('textbox', { description: /This is error text/ })).toBeInTheDocument();
    expect(screen.getByText('This is help text')).toBeInTheDocument();
    expect(screen.getByLabelText(/Phone number/)).toHaveAttribute('aria-describedby');
  });

  it("should allow for InlinePrompt's StatusIcon override via `messageIconLabel` prop", () => {
    const customIconLabel = 'My custom icon label';

    render(
      <Field
        label="Phone number"
        description="This is help text"
        sentiment="negative"
        message="This is error text"
        messageIconLabel={customIconLabel}
      >
        <Input />
      </Field>,
    );

    expect(screen.queryByLabelText('Error:')).not.toBeInTheDocument();
    expect(screen.getByLabelText(customIconLabel)).toBeInTheDocument();
  });

  it('should show or hide (Optional) suffix depending on required prop', () => {
    render(
      <Field label="Phone number" required={false} description="This is help text">
        <Input />
      </Field>,
    );

    expect(screen.getByText('(Optional)')).toBeInTheDocument();

    render(
      <Field label="Phone number" description="This is help text" required>
        <Input />
      </Field>,
    );

    expect(screen.getByLabelText('Phone number')).toBeInTheDocument();
  });

  it('avoids triggering button within label inadvertently', async () => {
    const handleClick = jest.fn();

    render(
      <Field
        label={
          <span>
            Phone number{' '}
            <Info content="Further information explained in detail." onClick={handleClick} />
          </span>
        }
      >
        <Input />
      </Field>,
    );

    const button = screen.getByRole('button');
    button.addEventListener('click', handleClick);

    const label = screen.getByText('Phone number');
    await userEvent.click(label);

    expect(handleClick).not.toHaveBeenCalled();
  });

  it('allows nesting-based label association', async () => {
    const handleClick = jest.fn();

    render(
      <Field id={null} label="Phone number">
        <Input onClick={handleClick} />
      </Field>,
    );

    expect(screen.getByRole('textbox')).not.toHaveAttribute('id');

    const label = screen.getByText('Phone number');
    await userEvent.click(label);
  });

  it('should show loading spinner when messageLoading is true', () => {
    render(
      <Field
        label="Phone number"
        message="Processing your request"
        sentiment="neutral"
        messageLoading
      >
        <Input />
      </Field>,
    );

    expect(screen.getByTestId('InlinePrompt_ProcessIndicator')).toBeInTheDocument();
    expect(screen.getByText('Processing your request')).toBeInTheDocument();
  });
});
