import React, { useEffect, useImperativeHandle, useRef, useState } from 'react';
import * as _ from 'lodash';
import { Subject } from 'rxjs';
import { takeUntil } from 'rxjs/operators';
import { useEventListener } from '../../hooks/useEventListener';
import { LoaderIcon } from '../LoaderIcon/web';
import Style from './Autocomplete.style';

interface IPropsAutoComplete extends React.ComponentProps<any> {
   renderInput: (params: any) => any;
   name: any;
   fetch: any;
   prefix?: any;
   value?: string;
   select?: (i: any) => void;
   title?: any;
   error?: boolean;
   cleanAfterSelect?: boolean;
}

export const Autocomplete = React.forwardRef<any, IPropsAutoComplete>(function Autocomplete(
   {
      renderInput,
      title,
      name,
      select,
      fetch,
      prefix,
      error,
      value,
      setError,
      cleanAfterSelect = false,
      NotFoundComponent = NotFoundAutoCompleteComponent,
      ...rest
   },
   ref,
) {
   const textInput = useRef(null);
   const [result, setResults] = useState([]);
   const [open, setOpen] = useState(false);
   const [searching, setSearching] = useState(false);
   const [notFound, setNotFound] = useState(false);
   const [position, setPosition] = useState(0);
   const [lengthItems, setLengthItems] = useState(0);

   useImperativeHandle(ref, () => textInput.current, []);

   // Se Input está em focus
   const [focus, setFocus] = useState(false);

   const debounce = useRef<any>();
   const $destroy = useRef(new Subject());
   const closeAutocomplete = useRef<any>();

   const onChangeDebounced = async (): Promise<void> => {
      $destroy.current.next();
      fetch(textInput.current.value)
         .pipe(takeUntil($destroy.current))
         .subscribe(
            (results) => {
               let i = -1;
               const resultsP = results.map((el) => {
                  if (el.disabled) {
                     return { id: JSON.stringify(el), disabled: true, data: el };
                  }

                  i += 1;
                  return { id: i, data: el };
               });

               setPosition(0);
               setLengthItems(i + 1);
               setResults(resultsP);
               setOpen(true);
               setNotFound(i + 1 === 0);

               setSearching(false);
            },
            (e) => {
               setSearching(false);
               if (setError) setError(e);
            },
         );
   };

   const handleInputChange = (e: any): void => {
      if (debounce.current) debounce.current.cancel();
      setSearching(true);
      debounce.current = _.debounce(onChangeDebounced, 300);
      debounce.current(e.target.value);
   };

   function closeAutoComplete(): void {
      setFocus(false);
      closeAutocomplete.current = setInterval(() => setOpen(false), 200);
      if (rest.onBlur) rest.onBlur();
   }

   function openAutoComplete(): void {
      setFocus(true);
      clearInterval(closeAutocomplete.current);
      setOpen(true);
      if (rest.onFocus) rest.onFocus();
   }

   function selectItem(e): void {
      if (!e.disabled) {
         select(e);
         setResults([]);
         setOpen(false);
         if (cleanAfterSelect) textInput.current.value = '';
      }
   }

   // ClearInternal
   useEffect(
      () => () => {
         clearInterval(closeAutocomplete.current);
      },
      [],
   );

   useEventListener('keydown.arrowdown', (event) => {
      if (open) {
         let nextPos = position + 1;

         if (nextPos >= lengthItems) {
            nextPos = 0;
         }

         setPosition(nextPos);
      }
   });

   useEventListener('keydown.arrowup', (event) => {
      if (open) {
         let nextPos = position - 1;

         if (nextPos < 0) {
            nextPos = lengthItems - 1;

            if (lengthItems - 1 < 0) {
               nextPos = 0;
            }
         }

         setPosition(nextPos);
      }
   });

   useEventListener('keydown.enter', (event) => {
      const item = result.find((e) => e.id === position);
      if (item) selectItem(item.data);
   });

   return (
      <Style data-cy={`autocomplete-${name}`}>
         <div className={`input-title ${focus && 'focus'}`}>{title}</div>

         <div className={`input-border ${error && 'has-error'} ${focus && 'focus'}`}>
            {prefix && <div className="prefix">{prefix}</div>}

            <input
               {...rest}
               ref={textInput}
               onChange={handleInputChange}
               onFocus={openAutoComplete}
               onBlur={closeAutoComplete}
               data-testid="autocomplete--input"
            />

            <div className="suffix">{searching && <LoaderIcon size={24} />}</div>
         </div>

         {open && (
            <ul className="autocomplete-list" data-testid="autocomplete-list">
               {result.map((e, i) => (
                  <li
                     key={e.id}
                     onClick={() => selectItem(e.data)}
                     data-testid={`autocomplete-list--row-${i}`}
                     className={`${position === e.id && 'active'} ${!!e.disabled && 'disabled'}`}
                  >
                     {renderInput(e.data)}
                  </li>
               ))}
               {notFound && <NotFoundComponent />}
            </ul>
         )}
      </Style>
   );
});

const NotFoundAutoCompleteComponent = (): any => {
   return <>Não encontrado</>;
};
