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

export interface ComboBoxProps extends Omit<React.HTMLProps<HTMLInputElement>, 'size' | 'type' | 'style' | 'label' | 'list'> {
  /**
   * 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,
  /**
   * Type of the component value.
   */
  type?: TextControlBase.TextControlInputType,
  /**
   * Style of the component.
   */
  style?: TextControlBase.TextControlStyle,
  /**
   * Is the label hidden?
   */
  hiddenLabel?: boolean,
  /**
   * Options available for the component's values.
   */
  options?: SelectControlBase.SelectOption[],
}

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

const RenderOptions: React.FC<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 const ComboBox = React.forwardRef<HTMLInputElement, ComboBoxProps>((
  {
    label = '',
    hint = '',
    indicator = null,
    size = TextControlBase.TextControlSize.MEDIUM,
    border = false,
    block = false,
    type = TextControlBase.TextControlInputType.TEXT,
    style = TextControlBase.TextControlStyle.DEFAULT,
    hiddenLabel = false,
    className: _className,
    placeholder: _placeholder,
    as: _as,
    options = [],
    ...etcProps
  }: ComboBoxProps,
  ref,
) => {
  const datalistId = useId();
  const textInputBaseArgs: TextControlBase.TextControlBaseArgs = {
    block,
    border,
    size,
    indicator: Boolean(indicator),
    style,
    resizable: false,
    predefinedValues: false,
  };

  return (
    <>
      <datalist id={datalistId}>
        <RenderOptions options={options} />
      </datalist>
      <div
        className={TextControlBase.Root(textInputBaseArgs)}
      >
        <input
          {...etcProps}
          className={TextControlBase.Input(textInputBaseArgs)}
          ref={ref}
          aria-label={label?.toString()}
          type={type}
          data-testid="input"
          list={datalistId}
        />
        {
          border && (
            <span
              data-testid="border"
            />
          )
        }
        {
          label && !hiddenLabel && (
            <div
              data-testid="label"
              className={TextControlBase.LabelWrapper(textInputBaseArgs)}
            >
              {label}
            </div>
          )
        }
        {hint && (
          <div
            className={TextControlBase.HintWrapper(textInputBaseArgs)}
            data-testid="hint"
          >
            <div
              className={TextControlBase.Hint()}
            >
              {hint}
            </div>
          </div>
        )}
        {indicator && (
          <div
            className={TextControlBase.IndicatorWrapper(textInputBaseArgs)}
          >
            {indicator}
          </div>
        )}
      </div>
    </>
  );
});

ComboBox.displayName = 'ComboBox';
