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

export interface SelectOption {
  label: string,
  value?: string | number | readonly string[]
  children?: SelectOption[]
}

type RenderOptionsProps = {
  options: 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 DropdownSelectProps = Omit<React.HTMLProps<HTMLSelectElement>, 'size' | 'style' | 'children'> & {
  /**
   * 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,
  /**
   * 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?: SelectOption[],
}

/**
 * Component for inputting textual values.
 *
 * This component supports multiline input and adjusts its layout accordingly.
 */
export const DropdownSelect = React.forwardRef<HTMLSelectElement, DropdownSelectProps>(
  (
    {
      label = '',
      hint = '',
      size = TextControlBase.TextControlSize.MEDIUM,
      border = false,
      block = false,
      style = TextControlBase.TextControlStyle.DEFAULT,
      hiddenLabel = false,
      multiple: _multiple,
      className: _className,
      placeholder: _placeholder,
      as: _as,
      options = [],
      ...etcProps
    }: DropdownSelectProps,
    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}
        >
          <RenderOptions
            options={options}
          />
        </select>
        {border && (
          <span
            data-testid="border"
          />
        )}
        {label && !hiddenLabel && (
          <div
            className={TextControlBase.LabelWrapper(styleArgs)}
            data-testid="label"
          >
            {label}
          </div>
        )}
        {hint && (
          <div
            className={TextControlBase.HintWrapper(styleArgs)}
            data-testid="hint"
          >
            <div
              className={TextControlBase.Hint()}
            >
              {hint}
            </div>
          </div>
        )}
        <div
          className={TextControlBase.IndicatorWrapper(styleArgs)}
        >
          <svg
            className={TextControlBase.Indicator()}
            viewBox="0 0 24 24"
            role="presentation"
          >
            <polyline
              points="6 9 12 15 18 9"
            />
          </svg>
        </div>
      </div>
    );
  }
);

DropdownSelect.displayName = 'DropdownSelect';
