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

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

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

export const ComboboxMulti = ({
  options,
  placeholder = "Select…",
  isCreatable = false,
  isClearable = false,
  isSearchable = true,
  disabled = false,
  className,
  onCreate = () => {},
  onChange = () => {},
}: ComboboxSharedProps) => {
  const {
    state,
    availableOptions,
    ref,
    onKeyPress,
    add,
    open,
    remove,
    search,
    close,
    hover,
    clear,
    create,
    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.length > 0;

  const showOptions = availableOptions.map((opt, index) => {
    const isSelected = value.find((v) => v.value === opt.value);
    return (
      <Option
        key={opt.value}
        id={opt.value}
        onClick={() => add(opt)}
        hover={() => hover(index)}
        isHover={index === hoverIndex}
        isSelected={!!isSelected}
      >
        {opt.label ? opt.label : opt.value}
      </Option>
    );
  });

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

  return (
    <Container
      close={close}
      ref={comboboxRef}
      isOpen={isOpen}
      selectedId={value.join(",")}
      onKeyDown={allowInput ? undefined : onKeyPress}
      disabled={disabled}
      className={className}
    >
      <Flex
        onClick={disabled ? undefined : open}
        tabIndex={disabled ? undefined : 0}
        className={classNames([surfaceClassName, surfaceMultiClassName])}
      >
        <Stack className="w-full">
          {value.map((val) => {
            return (
              <Tag key={val.value} onRemove={() => remove(val)}>
                {val.value}
              </Tag>
            );
          })}
          {allowInput && (
            <input
              onChange={(event: ChangeEvent<HTMLInputElement>): void =>
                search(event.currentTarget.value)
              }
              value={searchValue}
              className={selectInputCn}
              ref={ref}
              onKeyDown={onKeyPress}
              placeholder={value.length === 0 ? placeholder : ""}
              disabled={disabled}
            />
          )}
          {!allowInput && value.length === 0 && (
            <Box as="span" className="text-placeholder">
              {placeholder}
            </Box>
          )}
        </Stack>
        <Flex className="items-center">
          <ClearButton clear={clear} canClear={canClear} />
          <Arrow />
        </Flex>
      </Flex>

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