import cx from "classnames";
import React from "react";

import generateId from "../../../utils/generateId";

const CLASS_ROOT = "select";

export type SelectOption = { text: string; value: string } | string;

export type SelectSize = "default" | "large";
export type SelectWidth = "default" | "12ch" | "shrink" | "fullwidth";

export interface SelectProps
  extends Omit<React.SelectHTMLAttributes<HTMLSelectElement>, "size"> {
  /** Forwarded DOM element ref. */
  elemRef?: React.Ref<HTMLSelectElement>;
  /** Html id attribute. */
  id?: string;
  /** Disabled state. */
  isDisabled?: boolean;
  /** Invalid state. */
  isInvalid?: boolean;
  /** Html name attribute. */
  name?: string;
  /** Select option can be specified via children or as options array */
  options?: SelectOption[];
  /** Size of element. */
  size?: SelectSize;
  /** Select value. */
  value?: string | string[];
  /** Element width */
  width?: SelectWidth;
  /** Additional CSS classes */
  className?: string;
  /** Child elements */
  children?: React.ReactNode;
}

const defaultProps = {
  size: "default" as SelectSize,
  width: "default" as SelectWidth,
  options: [] as SelectOption[],
};

const Select: React.FC<SelectProps> = ({
  id,
  isDisabled,
  isInvalid,
  name,
  options = defaultProps.options,
  size = defaultProps.size,
  className,
  children,
  elemRef,
  width = defaultProps.width,
  ...other
}) => {
  const classes = cx(CLASS_ROOT, className, {
    "is-invalid": isInvalid,
    [`${CLASS_ROOT}--${size}`]: size !== defaultProps.size,
    [`${CLASS_ROOT}--${width}`]: width !== defaultProps.width,
  });

  const elementId = id || generateId();
  const elementName = name || elementId;

  return (
    <select
      id={elementId}
      name={elementName}
      className={classes}
      disabled={isDisabled}
      ref={elemRef}
      {...other}
    >
      {options.map((option) => {
        const {
          value: optionValue,
          text,
          ...otherOptionProps
        } = typeof option === "string"
          ? { value: option, text: option }
          : option;
        return (
          <option value={optionValue} key={text} {...otherOptionProps}>
            {text}
          </option>
        );
      })}
      {children}
    </select>
  );
};

Select.displayName = "Select";

export { Select };
