import React, { ChangeEvent, useMemo } from "react";
import classNames from "classnames";
import { Box } from "../Box";
import { Flex } from "../Flex";
import {
  SelectState,
  OptionInterface,
  combineReducers,
  createBaseReducer,
  createSingleValueReducer,
  searchReducer,
  useSelect,
} from "./useSelect";
import {
  Option,
  surfaceClassName,
  selectInputCn,
  NoOptions,
  Container,
  Listbox,
  ClearButton,
  Arrow,
  ComboboxSharedProps,
} from "./ui";

const initialState: SelectState<OptionInterface> = {
  value: {
    value: "",
    label: "",
  },
  isOpen: false,
  searchValue: "",
  hoverIndex: 0,
};

const reducers = [
  createBaseReducer(initialState),
  searchReducer,
  createSingleValueReducer(initialState),
];
const rootReducer = combineReducers(reducers);
function reducer(
  state: SelectState<OptionInterface> = initialState,
  action: any,
) {
  const nextState = rootReducer(state, action);
  return nextState;
}

export const Combobox = ({
  options,
  placeholder = "Select…",
  isCreatable = false,
  isClearable = false,
  isSearchable = true,
  disabled = false,
  className,
  onCreate = () => {},
  onChange = () => {},
}: ComboboxSharedProps) => {
  const {
    state,
    availableOptions,
    ref,
    onKeyPress,
    select,
    close,
    hover,
    clear,
    create,
    open,
    search,
    comboboxRef,
    listboxStyles,
  } = useSelect({ options, reducer, initialState, onChange, onCreate });
  const { isOpen, searchValue, value, hoverIndex } = state;
  const hasOptions = availableOptions.length > 0;
  const allowInput = isSearchable || isCreatable;
  const canCreate = isCreatable && !!searchValue;
  const canClear = isClearable && !!value.value;

  const showOptions = useMemo(() => {
    return availableOptions.map((opt, index) => {
      const isSelected = opt.value === value.value;
      return (
        <Option
          key={opt.value}
          id={opt.value}
          onClick={() => select(opt)}
          hover={() => hover(index)}
          isHover={index === hoverIndex}
          isSelected={isSelected}
        >
          {opt.label ? opt.label : opt.value}
        </Option>
      );
    });
  }, [availableOptions, value.value, hoverIndex, hover, select]);

  const noOptions = (
    <NoOptions
      canCreate={canCreate}
      create={create}
      searchValue={searchValue}
    />
  );

  return (
    <Container
      close={close}
      ref={comboboxRef}
      isOpen={isOpen}
      selectedId={value.value}
      onKeyDown={allowInput ? undefined : onKeyPress}
      disabled={disabled}
      className={className}
    >
      <Flex
        onClick={disabled ? undefined : open}
        tabIndex={disabled ? undefined : 0}
        className={surfaceClassName}
      >
        <Flex className="items-center w-full">
          {allowInput ? (
            <>
              {searchValue ? "" : value.value}
              <input
                onChange={(event: ChangeEvent<HTMLInputElement>): void =>
                  search(event.currentTarget.value)
                }
                value={searchValue}
                className={selectInputCn}
                ref={ref}
                onKeyDown={onKeyPress}
                placeholder={value.value ? "" : placeholder}
                disabled={disabled}
              />
            </>
          ) : (
            <Box as="span" className={classNames(!value && "text-placeholder")}>
              {value.value || placeholder}
            </Box>
          )}
        </Flex>
        <Flex className="items-center">
          <ClearButton clear={clear} canClear={canClear} />
          <Arrow />
        </Flex>
      </Flex>

      {isOpen && (
        <Listbox
          top={listboxStyles.top + 40}
          left={listboxStyles.left}
          width={listboxStyles.width}
        >
          {hasOptions ? showOptions : noOptions}
        </Listbox>
      )}
    </Container>
  );
};
