import { act, render, screen } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import React from 'react';
import { ARROW_DOWN, ENTER, ESCAPE } from '../../jest/keys';
import { SilkeAutocompleteField } from './silke-autocomplete-field';

type Item = {
  label: string;
  value: string;
};

const makeItem = (value: string): Item => ({ label: value, value });

const items: Item[] = [
  makeItem('cure'),
  makeItem('cat'),
  makeItem('lion'),
  makeItem('bear'),
  makeItem('deer'),
];

describe('silke-autocomplete-field', () => {
  beforeAll(() => {
    window.HTMLElement.prototype.scrollIntoView = function () {};
  });

  it('Displays all options when clicking input field', async () => {
    const user = userEvent.setup();

    render(<SilkeAutocompleteField items={items} label="Animal" />);
    const input = screen.getByLabelText('Animal');

    await act(async () => await user.click(input));

    const list = screen.getByRole('listbox');

    items.forEach((item) => {
      expect(list.textContent).toContain(item.label);
    });
  });

  it('Hides all options when pressing ESC key', async () => {
    const user = userEvent.setup();

    render(<SilkeAutocompleteField items={items} label="Animal" />);
    const input = screen.getByLabelText('Animal');
    await act(async () => await user.click(input));

    const list = screen.getByRole('listbox');

    items.forEach((item) => {
      expect(list.textContent).toContain(item.label);
    });
    await act(async () => await user.keyboard(ESCAPE));

    const list2 = await screen.queryAllByRole('listbox');
    expect(list2).toHaveLength(0);
  });

  it('It is possible to search for item', async () => {
    const user = userEvent.setup();

    render(<SilkeAutocompleteField items={items} label="Animal" />);

    const input = screen.getByLabelText('Animal');
    act(() => input.focus());

    await act(async () => await user.keyboard(ARROW_DOWN));

    await act(async () => await user.keyboard('C'));

    const listbox = screen.getByRole('listbox');

    ['cat', 'cure'].forEach((label) => {
      expect(listbox.textContent).toContain(label);
    });

    ['lion', 'beer', 'deer'].forEach((label) => {
      expect(listbox.textContent).not.toContain(label);
    });

    await act(async () => await user.keyboard('A'));

    ['cat'].forEach((label) => {
      expect(listbox.textContent).toContain(label);
    });

    ['lion', 'beer', 'deer', 'cure'].forEach((label) => {
      expect(listbox.textContent).not.toContain(label);
    });
  });

  it('Is possible to add new items when clicking add button', async () => {
    const user = userEvent.setup();
    const onChange = jest.fn();
    const onAdd = jest.fn();

    render(
      <SilkeAutocompleteField
        label="Animal"
        multiple
        onChange={onChange}
        value={['beer']}
        items={items}
        onAdd={onAdd}
      />,
    );

    const input = screen.getByLabelText('Animal');
    act(() => input.focus());

    await act(async () => await user.keyboard('banana'));

    const addButton = screen.getByText('+ Add item "banana"');
    await act(async () => await user.click(addButton));

    expect(onAdd).toHaveBeenCalledWith('banana');
  });

  it('Is possible to add new items by typing the item and then press ENTER', async () => {
    const user = userEvent.setup();
    const onChange = jest.fn();
    const onAdd = jest.fn();

    render(
      <SilkeAutocompleteField
        label="Animal"
        multiple
        onChange={onChange}
        value={['beer']}
        items={items}
        onAdd={onAdd}
      />,
    );

    const input = screen.getByLabelText('Animal');
    act(() => input.focus());

    await act(async () => await user.keyboard('banana'));

    await act(async () => await user.keyboard(ENTER));

    expect(onAdd).toHaveBeenCalledWith('banana');
  });

  it('Is possible to select item by clicking on it', async () => {
    const user = userEvent.setup();
    const onChange = jest.fn();

    render(<SilkeAutocompleteField label="Animal" onChange={onChange} items={items} />);

    const input = screen.getByLabelText('Animal');

    await act(async () => await user.click(input));

    const bearItem = screen.getByText('bear');
    await act(async () => await user.click(bearItem));

    expect(onChange).toHaveBeenCalledWith('bear');
  });

  it('Is possible to select item by using keyboard only', async () => {
    const user = userEvent.setup();
    const onChange = jest.fn();

    render(<SilkeAutocompleteField label="Animal" onChange={onChange} items={items} />);

    const input = screen.getByLabelText('Animal');
    act(() => input.focus());

    await act(async () => await user.keyboard(ARROW_DOWN));
    await act(async () => await user.keyboard(ARROW_DOWN));
    await act(async () => await user.keyboard(ARROW_DOWN));
    await act(async () => await user.keyboard(ENTER));

    expect(onChange).toHaveBeenCalledWith('bear');
  });
});
