import { Field } from '../field/Field';
import { mockMatchMedia, mockResizeObserver, render, screen, userEvent } from '../test-utils';

import MoneyInput from './MoneyInput';
import messages from './MoneyInput.messages';

mockMatchMedia();
mockResizeObserver();

describe('MoneyInput', () => {
  const currencies = [
    {
      header: 'Popular currencies',
    },
    {
      value: 'EUR',
      label: 'EUR',
      note: 'Euro',
      currency: 'eur',
      searchable: 'Spain, Germany, France, Austria, Estonia',
    },
    {
      value: 'USD',
      label: 'USD',
      note: 'United States dollar',
      currency: 'usd',
      searchable: 'Hong Kong, Saudi Arabia',
    },
    {
      value: 'GBP',
      label: 'GBP',
      note: 'British pound',
      currency: 'gbp',
      searchable: 'England, Scotland, Wales',
    },
    {
      header: 'Some other currencies',
    },
    {
      value: 'CAD',
      label: 'CAD',
      note: 'Canadian dollar',
      currency: 'cad',
    },
    {
      value: 'AUD',
      label: 'AUD',
      note: 'Australian dollar',
      currency: 'aud',
    },
  ] as const;
  const props = {
    currencies,
    selectedCurrency: currencies[1],
    searchPlaceholder: 'Type a currency / country',
    amount: 1000,
    onAmountChange: jest.fn(),
    onCurrencyChange: jest.fn(),
  };
  it.each([
    ['asd', ''],
    ['1a2s3d', '123'],
    ['±!@#$^*_+?><', ''],
    ['1±!@#$^*,_+?><2', '1,2'],
    ['12,3', '12,3'],
    ['12.3', '12.3'],
  ])("ignores the letters when typed '%s' and shows '%s'", async (testValue, expectedValue) => {
    render(<MoneyInput {...props} amount={null} />);

    const input = screen.getByRole('textbox');
    await userEvent.type(input, testValue);
    expect(input).toHaveValue(expectedValue);
  });

  it('supports custom `aria-labelledby` attribute', () => {
    render(
      <>
        {/* eslint-disable-next-line jsx-a11y/label-has-associated-control */}
        <label id="prioritized-label">Prioritized label</label>
        <MoneyInput {...props} aria-labelledby="prioritized-label" />
      </>,
    );

    expect(screen.getAllByLabelText('Prioritized label')[0]).toHaveClass('input-group');
  });

  it('supports `Field` for labeling', () => {
    render(
      <Field label="Recipient gets">
        <MoneyInput {...props} />
      </Field>,
    );
    expect(screen.getAllByRole('group')[0]).toHaveAccessibleName(/^Recipient gets/);
  });

  describe('ids', () => {
    it('should guarantee id and connect the input with the selected currency via withId HoC', () => {
      render(<MoneyInput {...props} />);
      const input = screen.getByRole('textbox');
      const button = screen.getByRole('combobox');
      expect(input.getAttribute('id')).toBeTruthy();
      expect(input).toHaveAttribute('aria-describedby', button.getAttribute('id'));
    });

    it('should have unique id for the select filter with predefined id', async () => {
      const fieldId = 'myFieldId';
      render(
        <Field label="Multiple currencies" id={fieldId}>
          <MoneyInput {...props} />
        </Field>,
      );
      await userEvent.click(screen.getByRole('combobox'));
      expect(screen.getByLabelText(props.searchPlaceholder)).toHaveAttribute(
        'id',
        `${fieldId}SelectedCurrencySearch`,
      );
    });

    it('should have unique id for the select filter without predefined id', async () => {
      render(
        <Field label="Multiple currencies">
          <MoneyInput {...props} />
        </Field>,
      );
      await userEvent.click(screen.getByRole('combobox'));
      expect(screen.getByLabelText(props.searchPlaceholder)).toHaveAttribute(
        'id',
        expect.stringMatching(/^:.*?:SelectedCurrencySearch$/),
      );
    });
  });

  it('should have AT label for the currency dropdown', () => {
    render(<MoneyInput {...props} />);
    expect(screen.getByRole('combobox')).toHaveAttribute(
      'aria-label',
      messages.selectCurrencyLabel.defaultMessage,
    );
  });

  it('should have a listbox label', async () => {
    render(<MoneyInput {...props} />);
    const trigger = screen.getByRole('combobox');
    await userEvent.click(trigger);
    const triggerLabel = trigger.getAttribute('aria-label');
    expect(triggerLabel).toBeTruthy();
    expect(screen.getByRole('listbox', { name: triggerLabel ?? '' })).toBeInTheDocument();
  });
});
