import React from "react";
import PropTypes from "prop-types";
import { useClickAway } from "use-click-away";
import styling from "@devcake/styling";
import { Transition } from "@headlessui/react";
import "../init";
import { FormElement } from "../formElement";
import { Label } from "../Label";
import { DefaultRenderer, ItemRenderer } from "./item";
import { Icon } from "../../Icon";

// Icon from HeroIcons, MIT licensed
styling.link("components.select.icons.open", "components.icons.chevron-down");
styling.link("components.select.icons.close", "components.select.icons.open");
styling.setDefault("components.select.enabled", "bg-white cursor-pointer focus:ring-1 focus:ring-indigo-500 focus:border-indigo-500");
styling.link("components.select.disabled", "components.form.disabled");
styling.setDefault("components.select.highlight", "hover:bg-blue-100");
styling.setDefault("components.select.list", "max-h-60 rounded-md py-1 text-base ring-1 ring-black ring-opacity-5 overflow-auto focus:outline-none sm:text-sm")
styling.setDefault("components.select.list_item", "text-gray-900 cursor-pointer select-none relative py-2 pl-3 pr-9")
styling.setDefault("components.select.dropdown" , "rounded-md bg-white shadow-lg")

export type SelectProps<T = string> = FormElement & {
  options: { [key: string]: T };
  renderItem?: ItemRenderer<T>;
  placeholder?: string
  placeholderClassName: string
}

export const Select = ({ className, disabled, label, nullable, onChange, placeholder, placeholderClassName,options, renderItem, value }: SelectProps) => {
  const ref = React.useRef();
  const [open, setOpen] = React.useState(false);
  const [innerValue, setInnerValue] = React.useState(value);

  // Update inner value when outer value changes
  React.useEffect(() => setInnerValue(value), [value]);

  useClickAway(ref, () => setOpen(false));

  const highlight = styling.get("components.select.highlight");
  const select = key => {
    if (nullable && key === innerValue) key = null;
    setInnerValue(key);
    onChange(key);
    setOpen(false);
  };

  return <>
    {label && <Label className="mb-1">{label}</Label>}
    <div className="relative" ref={ref}>
      <button
        type="button"
        className={[
          className,
          styling.get(disabled ? "components.select.disabled" : "components.select.enabled"),
          "relative w-full border border-gray-300 rounded-md shadow-sm pl-3 pr-10 py-2 text-left focus:outline-none sm:text-sm"
        ].join(" ")}
        onClick={disabled ? null : () => setOpen(!open)}
      >
        <div className="block truncate">
          {/* Explicit weak comparison to also catch undefined */}
          {innerValue != null ? renderItem(options[innerValue], false) : <i className={`${placeholderClassName}`}>{placeholder ? placeholder : "Nothing selected"}</i>}
        </div>
        <div className="absolute inset-y-0 right-0 flex items-center pr-2 pointer-events-none">
          <Icon icon={styling.get(`components.select.icons.${open ? "close" : "open"}`)} className='h-6 w-6 text-gray-400'/>
        </div>
      </button>
      <Transition
        show={open}
        leave="transition ease-in duration-100"
        leaveFrom="opacity-100"
        leaveTo="opacity-0"
      >
        <div className={`absolute mt-1 w-full z-10 ${styling.get("components.select.dropdown")}`}>
          <ul
            className={styling.get("components.select.list")}
          >
            {Object.keys(options).map(key => (
              <li
                key={key}
                className={`${styling.get("components.select.list_item")} ${highlight}`}
                onClick={() => select(key)}
              >
                {renderItem(options[key], key === innerValue)}
              </li>
            ))}
          </ul>
        </div>
      </Transition>
    </div>
  </>;
};

Select.propTypes = {
  className: PropTypes.string,
  disabled: PropTypes.bool,
  label: PropTypes.node,
  nullable: PropTypes.bool,
  onChange: PropTypes.func,
  options: PropTypes.object.isRequired,
  renderItem: PropTypes.func,
  value: PropTypes.string,
  placeholder: PropTypes.string,
  placeholderClassName: PropTypes.string
};
Select.defaultProps = {
  disabled: false,
  nullable: false,
  onChange: () => null,
  renderItem: DefaultRenderer,
};
