import React, {
  useEffect,
  FC,
  RefObject,
  ReactType,
  useRef,
  useState,
} from 'react';
import classNames from 'classnames';

import css from './index.module.css';
import InputSearch, { InputSearchProps } from '../InputSearch';

import Link, { LinkProps } from '../../blocks/Link';
import Loading from '../../blocks/Loading';
import Select, { SelectProps } from '../Select';
import Button, { ButtonProps } from '../../blocks/Button';

export interface SearchItemProps {
  Link: ReactType;
  LinkProps: LinkProps;
  name: string;
  currency: string;
  price: string;
  id: string;
}

export interface SearchProps {
  isActive?: boolean;
  results: SearchItemProps[];
  refs?: RefObject<HTMLLIElement>[];
  error?: boolean;
  loading?: boolean;
  closeList?: boolean;
  emptyError?: string;
  SelectProps?: SelectProps;
  InputSearchProps?: InputSearchProps;
  ButtonProps?: ButtonProps;
  mobileNavOpen?: boolean;
  stickyHeader?: boolean;
  variant: 'normal' | 'header';
}

const Search: FC<SearchProps> = ({
  isActive = true,
  results,
  refs,
  error,
  loading,
  closeList,
  emptyError,
  InputSearchProps,
  SelectProps,
  mobileNavOpen,
  stickyHeader,
  ButtonProps,
  variant = 'header',
}) => {
  const [closeSearch, setCloseSearch] = useState(closeList);
  useEffect(() => {
    setCloseSearch(closeList);
  }, [closeList]);

  const searchListElementRef = useRef(null);
  const searchClassNames = classNames(css.search, {
    [css.searchLoading]: loading,
    [css.searchIsDisabled]: !isActive,
  });

  const searchListClassNames = classNames(css.search__list, {
    [css.search__listError]: error,
  });

  const handleClickOutside = (e): void => {
    if (
      searchListElementRef.current &&
      InputSearchProps.inputRef.current &&
      (searchListElementRef.current.contains(e.target) ||
        InputSearchProps.inputRef.current.contains(e.target))
    ) {
      searchListElementRef.current &&
        searchListElementRef.current.setAttribute('style', 'display: block;');
      return;
    }
    setCloseSearch(true);
    searchListElementRef.current &&
      searchListElementRef.current.setAttribute('style', 'display: none;');
  };
  useEffect(() => {
    document.addEventListener('mousedown', handleClickOutside);

    return (): void => {
      document.removeEventListener('mousedown', handleClickOutside);
    };
  });

  const focusNextResultItem = (index: number, moveBy: number): void => {
    const nextIndex =
      index + moveBy < 0
        ? refs.length - 1
        : index + moveBy > refs.length - 1
        ? 0
        : index === undefined
        ? 0
        : index + moveBy;

    // @ts-ignore
    refs[nextIndex] && refs[nextIndex].current.firstChild.focus();
  };

  const handleInternalKeyPress = (
    e: React.KeyboardEvent<HTMLInputElement>,
    index?: number,
  ): void => {
    const arrowIndexMap = {
      ArrowDown: 1,
      ArrowUp: -1,
    };

    if (arrowIndexMap[e.key]) {
      e.preventDefault();
    }

    arrowIndexMap[e.key] && focusNextResultItem(index, arrowIndexMap[e.key]);
  };

  const [resultListIsOpen, setResultListIsOpen] = useState(false);

  useEffect(() => {
    setResultListIsOpen(
      !loading && !closeSearch && results && results.length > 0,
    );
  }, [loading, results]);

  return (
    <div className={searchClassNames}>
      <div className={css.search__searchWrap}>
        <div className={css.search__input}>
          <InputSearch
            {...InputSearchProps}
            onInvalid={(e): void =>
              emptyError && e.target.setCustomValidity(emptyError)
            }
            onInput={(e): void => e.target.setCustomValidity('')}
            hasResults={resultListIsOpen}
            onKeyDown={handleInternalKeyPress}
            onSearchToggle={handleClickOutside}
            stickyHeader={stickyHeader}
            mobileNavOpen={mobileNavOpen}
            closeSearch={!closeSearch}
            variant={variant}
            setCloseSearch={setCloseSearch}
          />

          {loading && (
            <div className={css.search__loading}>
              <Loading />
            </div>
          )}
        </div>

        {resultListIsOpen && (
          <div className={css.search__listWrap} ref={searchListElementRef}>
            <ul className={searchListClassNames}>
              {results.map((item, i) => (
                <li
                  ref={refs[i]}
                  key={item.id}
                  className={css.search__listItem}
                >
                  <Link
                    className={css.search__listItemLink}
                    onKeyDown={(e): void => handleInternalKeyPress(e, i)}
                    {...item.LinkProps}
                  >
                    <span className={css.search__resultTitle}>{item.name}</span>
                    <span className={css.search__resultDescription}>
                      {item.currency} {item.price}
                    </span>
                  </Link>
                </li>
              ))}
            </ul>
          </div>
        )}

        {SelectProps && (
          <div className={css.search__select}>
            <Select {...SelectProps} />
          </div>
        )}

        {ButtonProps && variant === 'normal' && (
          <div className={css.search__cta}>
            <Button fullwidth type="submit" {...ButtonProps} />
          </div>
        )}
      </div>
    </div>
  );
};

export default Search;
