import { Field } from '../field/Field';
import { mockMatchMedia, render, screen, fireEvent } from '../test-utils';
import { wait } from '../test-utils/wait';
import Typeahead from './Typeahead';

mockMatchMedia();

describe('Typeahead', () => {
  it('supports `Field` for labeling', () => {
    render(
      <Field id="test" label="Tags">
        <Typeahead id="test" name="test" options={[{ label: 'Test' }]} onChange={() => {}} />
      </Field>,
    );
    expect(screen.getAllByRole('group')[0]).toHaveAccessibleName(/^Tags/);
  });

  describe('when no options are provided', () => {
    it('does not render a dropdown when no options and no footer are provided', () => {
      render(
        <Field id="test" label="Tags">
          <Typeahead id="test" name="test" options={[]} onChange={() => {}} />
        </Field>,
      );
      expect(screen.queryByRole('menu')).not.toBeInTheDocument();
    });
    it('does render a dropdown when only a footer is provided', () => {
      render(
        <Field id="test" label="Tags">
          <Typeahead id="test" name="test" options={[]} footer={<p>hello</p>} onChange={() => {}} />
        </Field>,
      );
      expect(screen.getByRole('menu')).toBeInTheDocument();
    });
  });

  it('renders input with placeholder', () => {
    render(
      <Typeahead id="test" name="test" options={[]} placeholder="Type here" onChange={() => {}} />,
    );
    expect(screen.getByPlaceholderText('Type here')).toBeInTheDocument();
  });

  it('renders chips when multiple is true and selected has items', () => {
    const initialValue = [{ label: 'Chip 1' }, { label: 'Chip 2' }];
    render(
      <Typeahead
        id="test"
        name="test"
        options={[]}
        multiple
        initialValue={initialValue}
        onChange={() => {}}
      />,
    );
    expect(screen.getByText('Chip 1')).toBeInTheDocument();
    expect(screen.getByText('Chip 2')).toBeInTheDocument();
  });

  it('calls onChange when selecting an option', () => {
    const onChange = jest.fn();
    render(
      <Typeahead
        id="test"
        name="test"
        options={[{ label: 'Option 1' }, { label: 'Option 2' }]}
        minQueryLength={0}
        onChange={onChange}
      />,
    );
    const input = screen.getByRole('combobox');
    fireEvent.change(input, { target: { value: 'Option 1' } });
    fireEvent.click(screen.getByText('Option 1'));
    expect(onChange).toHaveBeenCalled();
  });

  it('shows clear button when there is a value', () => {
    render(
      <Typeahead
        id="test"
        name="test"
        options={[]}
        initialValue={[{ label: 'Chip' }]}
        onChange={() => {}}
      />,
    );
    expect(screen.getByRole('button', { name: /clear/i })).toBeInTheDocument();
  });

  it('clears value when clear button is clicked', () => {
    const onChange = jest.fn();
    render(
      <Typeahead
        id="test"
        name="test"
        options={[]}
        initialValue={[{ label: 'Chip' }]}
        onChange={onChange}
      />,
    );
    fireEvent.click(screen.getByRole('button', { name: /clear/i }));
    expect(onChange).toHaveBeenCalledWith([]);
  });

  it('shows InlinePrompt when alert prop is provided', () => {
    render(
      <Typeahead
        id="test"
        name="test"
        options={[]}
        alert={{ message: 'Something went wrong', type: 'error' }}
        onChange={() => {}}
      />,
    );
    expect(screen.getByText('Something went wrong')).toBeInTheDocument();
  });

  it('calls onFocus when input is focused', () => {
    const onFocus = jest.fn();
    render(<Typeahead id="test" name="test" options={[]} onChange={() => {}} onFocus={onFocus} />);
    fireEvent.focus(screen.getByRole('combobox'));
    expect(onFocus).toHaveBeenCalled();
  });

  it('calls onInputChange when input value changes', () => {
    const onInputChange = jest.fn();
    render(
      <Typeahead
        id="test"
        name="test"
        options={[]}
        minQueryLength={0}
        onChange={() => {}}
        onInputChange={onInputChange}
      />,
    );
    fireEvent.change(screen.getByRole('combobox'), { target: { value: 'abc' } });
    expect(onInputChange).toHaveBeenCalledWith('abc');
  });

  it('calls onSearch when input value changes', async () => {
    const onSearch = jest.fn();
    render(
      <Typeahead
        id="test"
        name="test"
        options={[]}
        minQueryLength={0}
        onChange={() => {}}
        onSearch={onSearch}
      />,
    );
    fireEvent.change(screen.getByRole('combobox'), { target: { value: 'abc' } });
    // wait for debounce
    await wait(500);
    expect(onSearch).toHaveBeenCalledWith('abc');
  });

  it('adds a new chip when allowNew and multiple are true and separator is pasted', () => {
    const onChange = jest.fn();
    render(
      <Typeahead
        id="test"
        name="test"
        options={[]}
        allowNew
        multiple
        chipSeparators={[',']}
        onChange={onChange}
      />,
    );
    const input = screen.getByRole('combobox');
    fireEvent.paste(input, {
      clipboardData: {
        getData: () => 'foo,bar',
      },
    });
    expect(onChange).toHaveBeenCalledWith(
      expect.arrayContaining([{ label: 'foo' }, { label: 'bar' }]),
    );
  });
});
