import React, { ClipboardEvent, useCallback, useMemo, useRef, useState } from 'react';
import { LinkContainer } from './components';
import { LinkSelectOption } from './types';
import { getLinksWithLabels } from 'common/transducers';
import { Tooltip } from '../../tooltip';
import { DecibelLink } from 'utils/decibel-link';
import { useClickAway } from 'react-use';
import { dropRight } from 'lodash';
import { BgStyle } from 'common/styles/utils';
import { SCreatableSelect } from './s-creatable-select';

type Props = {
  [x: string]: any;
  [x: number]: any;
};

type CreatablePropsWithoutOnChange = Omit<Props, 'onChange'>;

export interface LinksProps extends CreatablePropsWithoutOnChange {
  value?: string[];
  onChange(values: string[]): void;
  bgStyle: BgStyle;
  placeholder?: string;
  maxLength?: number;
  onError?: (error: string) => void;
  onResetError?: () => void;
  limitText?: string;
  disabled?: boolean;
  onLinkClick?(): void;
  uneditable?: boolean;
  allowInvalid?: boolean;
  isTooltipDisabled?: boolean;
}

export const LinksInput = React.memo(
  ({
    maxLength,
    placeholder = '',
    bgStyle,
    onBlur,
    className,
    components,
    onChange,
    value,
    onError,
    onResetError,
    limitText,
    disabled,
    onLinkClick,
    uneditable = false,
    allowInvalid = false,
    isTooltipDisabled = false,
  }: LinksProps) => {
    const [inputValue, setInputValue] = useState('');
    const [isTooltipVisible, setIsTooltipVisible] = useState(false);

    const inputContainerRef = useRef<HTMLDivElement>(null);

    useClickAway(inputContainerRef, () => {
      if (!inputValue) return;
      addLink(inputValue);
    });

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

    const handleInputChange = useCallback(
      (value: string) => {
        if (!isMaxValues) {
          setInputValue(value);
        }
      },
      [isMaxValues]
    );

    const handleError = useCallback(
      (error: string) => {
        if (onError) {
          onError(error);
        }
      },
      [onError]
    );

    const mappedValues = useMemo(() => {
      try {
        return value ? getLinksWithLabels(value, allowInvalid) : [];
      } catch (e) {
        onChange([]);
        handleError((e as Error).message);
        return [];
      }
    }, [value, allowInvalid, onChange, handleError]);

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

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

          if (!value?.includes(decibelLink.url.href)) {
            onChange([...(value || []), decibelLink.url.href]);

            if (onResetError) {
              onResetError();
            }
          }
        } catch (e) {
          if (allowInvalid) {
            setInputValue('');

            if (!value?.includes(link)) {
              onChange([...(value || []), link]);

              if (onResetError) {
                onResetError();
              }
            }
          } else {
            handleError((e as Error).message);
          }
        }
      },
      [value, onChange, onResetError, allowInvalid, handleError]
    );

    const handleKeyDown = useCallback<React.KeyboardEventHandler<HTMLDivElement>>(
      event => {
        if (isMaxValues) return;

        if (!inputValue) {
          if (event.key === 'Backspace' && value) {
            event.preventDefault();
            onChange(dropRight(value));
          }

          return;
        }

        switch (event.key) {
          case 'Enter':
          case 'Tab':
            event.preventDefault();
            addLink(inputValue);
            break;
          default:
            break;
        }
      },
      [addLink, inputValue, isMaxValues, onChange, value]
    );

    const onPasteCapture = useCallback(
      (event: ClipboardEvent<HTMLDivElement>) => {
        addLink(event.clipboardData.getData('text'));
        event.preventDefault();
      },
      [addLink]
    );

    const onRemoveValue = useCallback(
      (removedValue: LinkSelectOption) => {
        onChange(value?.filter(iteratedValue => iteratedValue !== removedValue.link) || []);
      },
      [onChange, value]
    );

    const handleBlur = useCallback(() => {
      if (onBlur) {
        onBlur(value);
      }
    }, [onBlur, value]);

    const showTooltip = useCallback(() => setIsTooltipVisible(true), []);
    const hideTooltip = useCallback(() => setIsTooltipVisible(false), []);

    const linkComponents = useMemo(
      () => ({
        ...components,
        MultiValueContainer: ({ data }) => (
          <LinkContainer
            data={data}
            onRemove={onRemoveValue}
            disabled={disabled}
            uneditable={uneditable}
            onLinkClick={onLinkClick}
            onMouseEnter={hideTooltip}
            onMouseLeave={showTooltip}
          />
        ),
        DropdownIndicator: null,
      }),
      [components, onRemoveValue, disabled, uneditable, onLinkClick, hideTooltip, showTooltip]
    );

    const getOptionLabel = useCallback(({ value }) => value, []);
    const getOptionValue = useCallback(({ id }) => id, []);

    return (
      <Tooltip content={limitText} visible={isTooltipVisible} enabled={isMaxValues && !isTooltipDisabled}>
        <div
          ref={inputContainerRef}
          onPasteCapture={onPasteCapture}
          onMouseEnter={showTooltip}
          onMouseLeave={hideTooltip}
        >
          <SCreatableSelect
            className={className}
            components={linkComponents}
            inputValue={inputValue}
            isMulti
            bgStyle={bgStyle}
            onBlur={handleBlur}
            classNamePrefix="react-select"
            getOptionLabel={getOptionLabel}
            getOptionValue={getOptionValue}
            menuIsOpen={false}
            isClearable={false}
            isDisabled={disabled}
            onKeyDown={handleKeyDown}
            onInputChange={handleInputChange}
            value={mappedValues}
            placeholder={placeholder}
          />
        </div>
      </Tooltip>
    );
  }
);
