import React, { useCallback, useEffect, useMemo, useState } from 'react';
import { bem } from 'utils/bem';
import { LinkfireSearchError, LinkfireSearchOption } from './types';
import { AutocompleteInput } from '../autocomplete-input/autocomplete-input';
import { BaseOption } from '../form/select';
import { useDispatch, useSelector } from 'react-redux';
import { searchLinkfireLinks } from 'common/actions';
import { isQueryLinkExists, mapLinkfireSearchToBaseOption } from 'common/transducers';
import { isLinkfireLinksLoadingSelector, linkfireLinksSelector } from 'common/selectors';
import { HTTPS_LINK_PREFIX } from 'common/constants';
import { Icon } from '../icon';
import { BEM_CLASS, SLinkfireSearch } from './s-linkfire-search';
import { MIN_CHARACTER_COUNT } from './constants';

interface Props {
  value?: LinkfireSearchOption[];
  onChange(value: LinkfireSearchOption[]): void;
  onLinkError?(error: LinkfireSearchError): void;
  maxLinksCount?: number;
  maxTooltipText?: string;
  placeholder?: string;
  withInputIcon?: boolean;
  disabled?: boolean;
  error?: string;
  className?: string;
  autoFocus?: boolean;
}

export const classes = bem(BEM_CLASS);

export const LinkfireSearch = React.memo(
  ({
    value,
    onChange,
    maxLinksCount,
    maxTooltipText,
    placeholder,
    withInputIcon,
    className,
    error,
    disabled,
    onLinkError,
    autoFocus = false,
  }: Props) => {
    const [searchError, setSearchError] = useState(error);
    const dispatch = useDispatch();

    const isLinkfireLinksLoading = useSelector(isLinkfireLinksLoadingSelector);
    const linkfireLinks = useSelector(linkfireLinksSelector);

    const linkfireSearchOptions = useMemo(() => mapLinkfireSearchToBaseOption(linkfireLinks), [linkfireLinks]);
    const plainLinkfireSearchOptions = useMemo(() => {
      const selectedIds = Array.isArray(value) ? value.map(({ id }) => id) : [];
      return linkfireSearchOptions.filter(({ id }) => !selectedIds.includes(id));
    }, [linkfireSearchOptions, value]);

    const setErrorMessage = useCallback(
      (type: LinkfireSearchError, message?: string) => {
        setSearchError(message);
        onLinkError && onLinkError(type);
      },
      [onLinkError]
    );
    const resetErrorMessage = useCallback(() => {
      setSearchError(undefined);
      onLinkError && onLinkError(LinkfireSearchError.None);
    }, [onLinkError]);

    const onLinkClick = useCallback((link: BaseOption) => window.open(HTTPS_LINK_PREFIX + link.name, '_blank'), []);
    const onLinkSearchRequest = useCallback(
      (query: string) => {
        if (query && query.length >= MIN_CHARACTER_COUNT) {
          dispatch(searchLinkfireLinks.request(query));
        }

        if (value && isQueryLinkExists(value, query)) {
          setErrorMessage(LinkfireSearchError.DuplicatedLink, error);
          return;
        }

        resetErrorMessage();
      },
      [dispatch, value, resetErrorMessage, setErrorMessage, error]
    );
    const onPressEnter = useCallback(
      (event: React.KeyboardEvent, query: string) => {
        if (event.key !== 'Enter') return;

        const isValueShort = query.length < MIN_CHARACTER_COUNT;
        const isDataUnavailable = !isLinkfireLinksLoading && plainLinkfireSearchOptions.length === 0;

        if (isValueShort) {
          setErrorMessage(
            LinkfireSearchError.InvalidLength,
            `Type at least ${MIN_CHARACTER_COUNT} character to search a link.`
          );
          return;
        }

        if (value && isQueryLinkExists(value, query)) {
          setErrorMessage(LinkfireSearchError.DuplicatedLink, error);
          return;
        }

        if (isDataUnavailable) {
          setErrorMessage(LinkfireSearchError.NotAvailable, 'Data for this link isn’t available.');
          return;
        }

        resetErrorMessage();
      },
      [error, isLinkfireLinksLoading, plainLinkfireSearchOptions.length, resetErrorMessage, setErrorMessage, value]
    );

    useEffect(() => {
      setSearchError(error);
    }, [error]);

    useEffect(() => {
      document.addEventListener('click', resetErrorMessage);

      return () => {
        document.removeEventListener('click', resetErrorMessage);
      };
    }, [resetErrorMessage]);

    return (
      <SLinkfireSearch>
        <AutocompleteInput
          placeholder={placeholder}
          maxItemsCount={maxLinksCount}
          isLoading={isLinkfireLinksLoading}
          options={linkfireSearchOptions}
          onClickItem={onLinkClick}
          value={value}
          onChange={onChange}
          maxTooltipText={maxTooltipText}
          onRequestData={onLinkSearchRequest}
          withInputIcon={withInputIcon}
          className={className}
          hasError={!!searchError}
          onKeyDown={onPressEnter}
          disabled={disabled}
          minInputLength={MIN_CHARACTER_COUNT}
          autoFocus={autoFocus}
        />
        {searchError && (
          <div className={classes('error')}>
            <Icon className={classes('error-icon')} name="warning" size="general" color="inherit" />
            {searchError}
          </div>
        )}
      </SLinkfireSearch>
    );
  }
);
