import React, { useCallback, useMemo, useRef, useState } from 'react';
import { components as selectComponents, GroupBase, SelectInstance } from 'react-select';
import CreatableSelectInstance from 'react-select/creatable';
import { SelectComponents } from 'react-select/dist/declarations/src/components';
import { BaseOption } from 'common/components/form/select';
import { BgStyle } from 'common/styles/utils';
import { SCreatableSelect } from './s-creatable-select';
import { MultiSelectItem } from '../multiselect/components/multi-select-item';
import { EmptyState, MenuFooter } from './components';
import { EllipsisItem } from 'common/components/form/creatable-select/components/elipsis-item';
import { getJoinedNames } from 'common/transducers';

interface Props<T> {
  value: T;
  isMulti: boolean;
  onChange(ids: T): void;
  onCreateNew(name: string): void;
  options: BaseOption[];
  components: SelectComponents<unknown, boolean, GroupBase<unknown>>;
  bgStyle?: BgStyle;
  disabled?: boolean;
  className?: string;
  placeholder?: string;
  maxVisibleValue?: number;
  maxInputCharacters?: number;
  isLoading?: boolean;
  dataSelector?: string;
}

export const CreatableSelect = React.memo(
  <T,>({
    value,
    onChange,
    options,
    components,
    bgStyle = 'grey',
    disabled,
    className,
    placeholder,
    maxVisibleValue = 2,
    maxInputCharacters = 20,
    onCreateNew,
    isLoading,
    dataSelector,
  }: Props<T>) => {
    const [inputValue, setInputValue] = useState('');

    const selectInstance = useRef<SelectInstance<unknown, boolean, GroupBase<unknown>>>(null);

    const handleChange = useCallback(
      value => {
        onChange(value);
      },
      [onChange]
    );

    const handleInputChange = useCallback(
      (value: string) => {
        if (value.length >= maxInputCharacters) return;
        setInputValue(value);
      },
      [setInputValue, maxInputCharacters]
    );

    const handleApply = useCallback(() => {
      if (!inputValue) {
        selectInstance.current?.blur();
      }
      onCreateNew && onCreateNew(inputValue);
    }, [inputValue, selectInstance, onCreateNew]);

    const renderValueContainer = useCallback(
      ({ children, ...rest }) => {
        const value = rest.getValue();
        const childrenArray = React.Children.toArray(children);
        const childrenArrayWithoutInput = childrenArray.slice(0, childrenArray.length - 1);
        return (
          <selectComponents.ValueContainer {...rest} data-selector={`${dataSelector}-value-container`}>
            {childrenArrayWithoutInput.slice(0, maxVisibleValue)}
            {value && Array.isArray(value) && value.length > maxVisibleValue && (
              <EllipsisItem
                count={value.length - maxVisibleValue}
                tooltipText={getJoinedNames(value.slice(maxVisibleValue, value.length))}
                dataSelector={`${dataSelector}-ellipsis-item`}
              />
            )}
            {childrenArray[childrenArray.length - 1]}
          </selectComponents.ValueContainer>
        );
      },
      [maxVisibleValue, dataSelector]
    );

    const renderOption = useCallback(
      ({ label, innerProps: { onClick }, isSelected, isDisabled }) => (
        <div onClick={onClick}>
          <MultiSelectItem
            label={label}
            depth={1}
            isChecked={isSelected}
            isDisabled={isDisabled}
            isLeaf
            dataSelector={`${dataSelector}-item-${label}`}
          />
        </div>
      ),
      [dataSelector]
    );

    const renderMenu = useCallback(
      ({ children, ...rest }) => {
        return (
          <selectComponents.Menu {...rest} data-selector={`${dataSelector}-menu`}>
            <>
              {children}
              <MenuFooter onApplyClick={handleApply} onResetClick={rest.clearValue} canCreate={!!inputValue} />
            </>
          </selectComponents.Menu>
        );
      },
      [handleApply, inputValue, dataSelector]
    );

    const renderEmptyState = useCallback(() => <EmptyState dataSelector={`${dataSelector}-empty-state`} />, [
      dataSelector,
    ]);

    const customComponents = useMemo(
      () => ({
        ...components,
        ValueContainer: renderValueContainer,
        Option: renderOption,
        NoOptionsMessage: renderEmptyState,
        Menu: renderMenu,
      }),
      [renderMenu, renderOption, renderValueContainer, renderEmptyState, components]
    );

    const getOptionLabel = useCallback(({ name }) => name || '', []);
    const getOptionValue = useCallback(({ id }) => id, []);

    return (
      <SCreatableSelect<CreatableSelectInstance>
        ref={selectInstance}
        components={customComponents}
        inputValue={inputValue}
        isMulti
        isClearable={false}
        onChange={handleChange}
        onInputChange={handleInputChange}
        options={options}
        closeMenuOnSelect={false}
        hideSelectedOptions={false}
        backspaceRemovesValue={false}
        isLoading={isLoading}
        placeholder={placeholder}
        value={value}
        bgStyle={bgStyle}
        isDisabled={disabled}
        getOptionLabel={getOptionLabel}
        getOptionValue={getOptionValue}
        classNamePrefix="react-select"
        className={`react-select ${className}`}
      />
    );
  }
);
