import React, { InputHTMLAttributes, 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;
   placeholder?: string;
   clearable?: boolean;
   select?: (i: any) => void;
   title?: any;
   valueSelected: string;
   error?: boolean;
   cleanAfterSelect?: boolean;
}

export const Autocomplete = React.forwardRef<any, IPropsAutoComplete>(function Autocomplete(
   {
      renderInput,
      title,
      name,
      select,
      fetch,
      prefix,
      placeholder = 'Selecionar...',
      clearable,
      valueSelected,
      error,
      value,
      setError,
      cleanAfterSelect = false,
      NotFoundComponent = NotFoundAutoCompleteComponent,
      ...rest
   },
   ref,
) {
   const textInput = useRef<HTMLInputElement>();
   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);
   const [haveText, setHaveText] = useState(false);

   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);
            },
         );
   };

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

   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);
      setHaveText(false);
      if (cleanAfterSelect) textInput.current.value = '';
      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);
         setHaveText(false);
         // if (cleanAfterSelect) textInput.current.value = '';
         textInput.current.blur();
      }
   }

   function onInput(ev): void {
      setHaveText(ev.currentTarget.value.length !== 0);
   }

   function onClear(): void {
      selectItem(null);
   }

   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>}

            <div className="input-container--inner">
               <input
                  {...rest}
                  placeholder={valueSelected ? '' : placeholder}
                  ref={textInput}
                  onChange={handleInputChange}
                  onFocus={openAutoComplete}
                  onBlur={closeAutoComplete}
                  onInput={onInput}
                  data-testid="autocomplete--input"
               />

               {!haveText && (
                  <div className="text-placeholder" data-testid="autocomplete--text-placeholder">
                     {valueSelected}
                  </div>
               )}
            </div>

            <div className="suffix">{searching && <LoaderIcon size={24} />}</div>
            {clearable && !!valueSelected && (
               <div className="suffix clearable" onClick={onClear}>
                  <svg
                     height="20"
                     width="20"
                     viewBox="0 0 20 20"
                     aria-hidden="true"
                     focusable="false"
                     className="css-6q0nyr-Svg"
                  >
                     <path d="M14.348 14.849c-0.469 0.469-1.229 0.469-1.697 0l-2.651-3.030-2.651 3.029c-0.469 0.469-1.229 0.469-1.697 0-0.469-0.469-0.469-1.229 0-1.697l2.758-3.15-2.759-3.152c-0.469-0.469-0.469-1.228 0-1.697s1.228-0.469 1.697 0l2.652 3.031 2.651-3.031c0.469-0.469 1.228-0.469 1.697 0s0.469 1.229 0 1.697l-2.758 3.152 2.758 3.15c0.469 0.469 0.469 1.229 0 1.698z" />
                  </svg>
               </div>
            )}

            <span className="line" />

            <div className="suffix down-icon">
               <svg
                  height="20"
                  width="20"
                  viewBox="0 0 20 20"
                  aria-hidden="true"
                  focusable="false"
                  className="css-6q0nyr-Svg"
               >
                  <path d="M4.516 7.548c0.436-0.446 1.043-0.481 1.576 0l3.908 3.747 3.908-3.747c0.533-0.481 1.141-0.446 1.574 0 0.436 0.445 0.408 1.197 0 1.615-0.406 0.418-4.695 4.502-4.695 4.502-0.217 0.223-0.502 0.335-0.787 0.335s-0.57-0.112-0.789-0.335c0 0-4.287-4.084-4.695-4.502s-0.436-1.17 0-1.615z" />
               </svg>
            </div>
         </div>

         {open && (
            <ul className="autocomplete-list" data-testid="autocomplete-list">
               {result.map((e, i) => (
                  <li
                     key={e.id}
                     onMouseDown={() => 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</>;
};
