import * as React from 'react';
import * as TextControlBase from '@tesseract-design/web-base-textcontrol';
import * as SelectControlBase from '@tesseract-design/web-base-selectcontrol';

type RenderOptionsProps = {
  options: SelectControlBase.SelectOption[],
  optionComponent?: React.ElementType,
  optgroupComponent?: React.ElementType,
  level?: number,
}

const RenderOptions: React.VFC<RenderOptionsProps> = ({
  options,
  optionComponent: Option = 'option',
  optgroupComponent: Optgroup = 'optgroup',
  level = 0,
}: RenderOptionsProps) => (
  <>
    {
      options.map((o) => {
        if (typeof o.value !== 'undefined') {
          return (
            <Option
              key={`${o.label}:${o.value.toString()}`}
              value={o.value}
            >
              {o.label}
            </Option>
          );
        }

        if (typeof o.children !== 'undefined') {
          if (level === 0) {
            return (
              <Optgroup
                key={o.label}
                label={o.label}
              >
                <RenderOptions
                  options={o.children}
                  optionComponent={Option}
                  optgroupComponent={Optgroup}
                  level={level + 1}
                />
              </Optgroup>
            );
          }
          return (
            <React.Fragment
              key={o.label}
            >
              <Option
                disabled
              >
                {o.label}
              </Option>
              <RenderOptions
                options={o.children}
                optionComponent={Option}
                optgroupComponent={Optgroup}
                level={level + 1}
              />
            </React.Fragment>
          );
        }

        return null;
      })
    }
  </>
);

export type MenuSelectProps = Omit<React.HTMLProps<HTMLSelectElement>, 'size' | 'style'> & {
  /**
   * Short textual description indicating the nature of the component's value.
   */
  label?: React.ReactNode,
  /**
   * Short textual description as guidelines for valid input values.
   */
  hint?: React.ReactNode,
  /**
   * Size of the component.
   */
  size?: TextControlBase.TextControlSize,
  /**
   * Additional description, usually graphical, indicating the nature of the component's value.
   */
  indicator?: React.ReactNode,
  /**
   * Should the component display a border?
   */
  border?: boolean,
  /**
   * Should the component occupy the whole width of its parent?
   */
  block?: boolean,
  /**
   * Style of the component.
   */
  style?: TextControlBase.TextControlStyle,
  /**
   * Is the label hidden?
   */
  hiddenLabel?: boolean,
  /**
   * Options available for the component's values.
   */
  options?: SelectControlBase.SelectOption[],
}

export const MenuSelect = React.forwardRef<HTMLSelectElement, MenuSelectProps>(({
  label = '',
  hint = '',
  indicator = null,
  size = TextControlBase.TextControlSize.MEDIUM,
  border = false,
  block = false,
  style = TextControlBase.TextControlStyle.DEFAULT,
  hiddenLabel = false,
  options = [],
  className: _className,
  placeholder: _placeholder,
  as: _as,
  ...etcProps
}: MenuSelectProps, ref) => {
  const styleArgs = React.useMemo<TextControlBase.TextControlBaseArgs>(() => ({
    block,
    border,
    size,
    indicator: true,
    style,
    resizable: true,
    predefinedValues: true,
  }), [block, border, size, style]);

  return (
    <div
      className={TextControlBase.Root(styleArgs)}
    >
        <select
          {...etcProps}
          className={TextControlBase.Input(styleArgs)}
          ref={ref}
          aria-label={label}
          style={{
            height: TextControlBase.MIN_HEIGHTS[size],
          }}
          size={2}
          data-testid="input"
        >
          <RenderOptions
            options={options}
          />
        </select>
      {
        border && (
          <span
            data-testid="border"
          />
        )
      }
      {
        label && !hiddenLabel && (
          <div
            data-testid="label"
            className={TextControlBase.LabelWrapper(styleArgs)}
          >
            {label}
          </div>
        )
      }
      {hint && (
        <div
          className={TextControlBase.HintWrapper(styleArgs)}
          data-testid="hint"
        >
          <div
            className={TextControlBase.Hint()}
          >
            {hint}
          </div>
        </div>
      )}
      {indicator && (
        <div
          className={TextControlBase.IndicatorWrapper(styleArgs)}
        >
          {indicator}
        </div>
      )}
    </div>
  );
});

MenuSelect.displayName = 'MenuSelect';
