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

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

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


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

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

  it('renders 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('renders a hint', () => {
    render(
      <DropdownSelect
        hint="foo"
      />
    );
    const hint = screen.getByTestId('hint');
    expect(hint).toBeInTheDocument();
  });

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

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

  it('renders 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('renders 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(Object.values(TextControlBase.TextControlSize))('on %s size', (size) => {
    it('renders input styles', () => {
      render(
        <DropdownSelect
          size={size}
        />
      );

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

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

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

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

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

  it('renders a block input', () => {
    render(
      <DropdownSelect
        block
      />
    );

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

  describe.each(Object.values(TextControlBase.TextControlStyle))('on %s style', (style) => {
    it('renders input styles', () => {
      render(
        <DropdownSelect
          style={style}
        />
      );

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

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

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

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

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

  it('handles change events', () => {
    const onChange = vi.fn().mockImplementationOnce((e) => { e.preventDefault(); })
    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();
  });
});
