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

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

describe('DropdownSelect', () => {
  it('should render a combobox', () => {
    render(<DropdownSelect />);
    const combobox = screen.getByRole('combobox');
    expect(combobox).toBeInTheDocument();
  });
  
  
  it('should render a border', () => {
    render(
      <DropdownSelect
        border
      />
    );
    const border = screen.getByTestId('border');
    expect(border).toBeInTheDocument();
  });

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

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

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

  it('should not render invalid options', () => {
    render(
      <DropdownSelect
        options={[
          {
            label: 'foo',
          },
          {
            label: 'bar',
          }
        ]}
      />
    );
    const combobox = screen.getByRole('combobox');
    expect(combobox.children).toHaveLength(0);
  });

  it('should render valid options', () => {
    render(
      <DropdownSelect
        options={[
          {
            label: 'foo',
            value: 'foo',
          },
          {
            label: 'bar',
            value: 'bar',
          }
        ]}
      />
    );
    const combobox = screen.getByRole('combobox');
    expect(combobox.children).toHaveLength(2);
  });

  it('should render shallow option groups', () => {
    render(
      <DropdownSelect
        options={[
          {
            label: 'foo',
            children: [
              {
                label: 'baz',
                value: 'baz',
              },
            ],
          },
          {
            label: 'bar',
            children: [
              {
                label: 'quux',
                value: 'quux',
              },
              {
                label: 'quuux',
                value: 'quuux',
              },
            ],
          }
        ]}
      />
    );
    const combobox = screen.getByRole('combobox');
    expect(combobox.children).toHaveLength(2);
    expect(combobox.children[0].children).toHaveLength(1);
    expect(combobox.children[1].children).toHaveLength(2);
  });

  it('should render deep option groups', () => {
    render(
      <DropdownSelect
        options={[
          {
            label: 'foo',
            children: [
              {
                label: 'baz',
                children: [
                  {
                    label: 'quuuux',
                    value: 'quuuux',
                  },
                  {
                    label: 'quuuuux',
                    value: 'quuuuux',
                  },
                  {
                    label: 'quuuuuux',
                    value: 'quuuuuux',
                  },
                ],
              },
            ],
          },
          {
            label: 'bar',
            children: [
              {
                label: 'quux',
                value: 'quux',
              },
              {
                label: 'quuux',
                value: 'quuux',
              },
            ],
          }
        ]}
      />
    );
    const combobox = screen.getByRole('combobox');
    expect(combobox.children).toHaveLength(2);
    expect(combobox.children[0].children).toHaveLength(4);
    expect(combobox.children[1].children).toHaveLength(2);
  });

  describe.each([
    TextControlBase.TextControlSize.SMALL,
    TextControlBase.TextControlSize.MEDIUM,
    TextControlBase.TextControlSize.LARGE,
  ])('on %s size', (size) => {
    it('should render input styles', () => {
      render(
        <DropdownSelect
          size={size}
        />
      );

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

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

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

    it('should render indicator styles', () => {
      render(
        <DropdownSelect
          size={size}
        />
      );

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

  it('should render a block textbox', () => {
    render(
      <DropdownSelect
        block
      />
    );

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

  describe.each([
    TextControlBase.TextControlStyle.DEFAULT,
    TextControlBase.TextControlStyle.ALTERNATE,
  ])('on %s style', (style) => {
    it('should render input styles', () => {
      render(
        <DropdownSelect
          style={style}
        />
      );

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

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

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

    it('should render indicator styles', () => {
      render(
        <DropdownSelect
          style={style}
        />
      );

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

  it('should handle change events', () => {
    const onChange = jest.fn();
    render(
      <DropdownSelect
        onChange={onChange}
        options={[
          {
            label: 'foo',
            value: 'foo',
          },
          {
            label: 'bar',
            value: 'bar',
          }
        ]}
      />
    );
    const combobox: HTMLSelectElement = screen.getByRole('combobox');
    const [, secondOption] = screen.getAllByRole('option');
    userEvent.selectOptions(combobox, secondOption);
    expect(onChange).toBeCalled();
  });
});
