import React, { useCallback, useEffect, useMemo, useState } from 'react';
import { CreatableSelectType, LinkInputMultiSelectProps, LinkOption, SelectComponents } from '../../types';
import { BEM_CLASS, SLinkInputMultiSelect } from './s-link-input-multi-select';
import { bem } from 'utils/bem';
import { noop } from 'lodash';
import { BaseCreatableSelect } from '../base-creatable-select';
import { DecibelLink } from 'utils/decibel-link';
import { LinkContainer, LinkItem } from './components';
import { getLinksWithLabels } from './transducers';
import { InputActionMeta } from 'react-select/dist/declarations/src/types';

const classes = bem(BEM_CLASS);

const Root = <T extends LinkOption, P extends {}>({
  valueHandler,
  onInvalidLink,
  onAllValid,
  selectComponents,
  selectContainer,
  classNames,
  maxLength,
  rawLinks,
  onEditFinished,
  inputValue: initialInputValue,
  ...selectProps
}: LinkInputMultiSelectProps<T, P>) => {
  const [inputValue, setInputValue] = useState(initialInputValue);

  const handleChange = useCallback(
    (value: (string | number)[]) => {
      valueHandler(value);
    },
    [valueHandler]
  );

  const components: () => SelectComponents<T, true> = useCallback(
    () => ({
      DropdownIndicator: () => null,
      IndicatorsContainer: () => null,
      MultiValueContainer: LinkContainer,
      MultiValueLabel: LinkItem,

      ...selectComponents?.({
        isMenuOpen: false,
        openMenu: noop,
        closeMenu: noop,
      }),
    }),
    [selectComponents]
  );

  const selectClassNames = useMemo(
    () => ({
      ...classNames,
      control: classes('control', undefined, classNames?.control),
    }),
    [classNames]
  );

  const isMaxValues = maxLength === rawLinks?.length;

  const handleInputChange = useCallback(
    (value: string, meta: InputActionMeta) => {
      if (!isMaxValues && meta.action === 'input-change') {
        setInputValue(value);
      }
    },
    [isMaxValues]
  );

  const mappedValues: T[] = useMemo(() => getLinksWithLabels(rawLinks || [], true), [rawLinks]);
  const isAnyInvalid = useMemo(() => mappedValues.some(link => link.isInvalid), [mappedValues]);

  const addLink = useCallback(
    (link: string) => {
      try {
        setInputValue('');

        const trimmedInput = link.trim();
        const decibelLink = new DecibelLink(trimmedInput);

        handleChange([...rawLinks, decibelLink.url.href]);
      } catch (e) {
        setInputValue('');

        if (!rawLinks?.includes(link)) {
          handleChange([...rawLinks, link]);
        }
      }
    },
    [handleChange, rawLinks]
  );

  const handleKeyDown = useCallback<React.KeyboardEventHandler<HTMLDivElement>>(
    event => {
      switch (event.key) {
        case 'Enter':
          if (isMaxValues) break;
          event.preventDefault();
          event.stopPropagation();
          if (inputValue) {
            addLink(inputValue);
            return;
          } else {
            onEditFinished?.();
          }
          break;
        case 'Tab':
        case ',':
        case ' ':
          if (isMaxValues) break;
          if (inputValue) {
            event.preventDefault();
            addLink(inputValue);
          }
          break;
        default:
          break;
      }
      selectProps.onKeyDown?.(event);
    },
    [isMaxValues, inputValue, addLink, onEditFinished, selectProps]
  );

  const getOptionValue = useCallback(({ id }: T) => {
    if (typeof id === 'string') return id;

    return id.toString();
  }, []);

  useEffect(() => {
    if (isAnyInvalid) {
      onInvalidLink?.();
    } else {
      onAllValid?.();
    }
  }, [isAnyInvalid, onAllValid, onInvalidLink]);

  return (
    <SLinkInputMultiSelect>
      {selectContainer?.({ ...selectProps, classNames: selectClassNames }) || (
        <BaseCreatableSelect
          {...selectProps}
          type={CreatableSelectType.Base}
          value={mappedValues}
          dataSelector="link-input-multi-select"
          isSearchable={true}
          inputValue={inputValue}
          onInputChange={handleInputChange}
          valueHandler={handleChange}
          onKeyDown={handleKeyDown}
          selectComponents={components}
          classNames={selectClassNames}
          getOptionLabel={({ label }) => label || ''}
          getOptionValue={getOptionValue}
          valueContainerLabel={`Max ${maxLength} Links`}
        />
      )}
    </SLinkInputMultiSelect>
  );
};

export const LinkInputMultiSelect = React.memo(Root) as typeof Root;
