import React, { useEffect, useImperativeHandle, useRef, useState } from 'react';
import { useEventListener } from '@devesharp/react-hooks';
import { NormalizeString } from '@devesharp/react-utils';
import { faAngleDown } from '@fortawesome/free-solid-svg-icons';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import * as _ from 'lodash';
import { Subject } from 'rxjs';
import { takeUntil } from 'rxjs/operators';
import useDeepCompareEffect from 'use-deep-compare-effect';
import { CloseBtn } from '../CloseBtn';
import { Input } from '../Input';
import { FormStyle } from '../styles';
import { IPropsSelectAutoComplete } from './SelectAutocomplete.interface';
import * as S from './SelectAutocomplete.style';
import { SelectAutocompleteCore } from './SelectAutocompleteCore';

function convertArrayFetchToOptions(results): any {
   let i = -1;
   return results.map((el) => {
      if (el.disabled) {
         return {
            id: el.id ?? JSON.stringify(el),
            disabled: true,
            value: el.value ?? el.name,
            // trata objeto com getAsyncLabel ou resgata el (que deve ser uma string)
            name: el.name,
         };
      }

      i += 1;
      return { id: i, value: el.value, name: el.name };
   });
}
/**
 * todo: adicioanar testes para o autocomplete
 */
export const SelectAutocompletePure = React.forwardRef<any, IPropsSelectAutoComplete>(function SelectAutocompletePure(
   {
      renderInput = (item: any) => <div>{item}</div>,
      name,
      onSelect,
      getAsyncLabel,
      fetch,
      placeHolder,
      setError,
      initialFetch = [],
      options,
      isClearable = false,
      NotFoundComponent: NotFoundComponentCustom,
      //
      title,
      prefix,
      suffix,
      textRight,
      textCenter,
      loading,
      mask,
      withoutDotNumber,
      decimals,
      error,
      value,
      onInput,
      onEnter,
      defaultValue,
      ...rest
   },
   ref,
) {
   const inputProps = {
      title,
      prefix,
      suffix,
      textRight,
      textCenter,
      loading,
      mask,
      withoutDotNumber,
      decimals,
      error,
      onInput,
      onEnter,
      defaultValue,
   };

   const textInput = useRef(null);
   const [result, setResults] = useState(
      !options ? convertArrayFetchToOptions(initialFetch) : options.map((el, i) => ({ ...el, id: i })),
   );
   const [open, setOpen] = useState(false);

   useDeepCompareEffect(() => {
      setResults(!options ? convertArrayFetchToOptions(initialFetch) : options.map((el, i) => ({ ...el, id: i })));
   }, [initialFetch]);

   /**
    * Buscando na request
    */
   const [searching, setSearching] = useState(false);

   /**
    * Nenhum resultado encontrado
    */
   const [notFound, setNotFound] = useState(false);

   /**
    * Posição atual do autocomplete
    * pode ser movido com setas para cima e para baixo
    */
   const [position, setPosition] = useState(0);

   /**
    * Quantidade de items
    */
   const [lengthItems, setLengthItems] = useState(0);

   /**
    * String da input selecionada
    */
   const [selectedLabel, setSelectedLabel] = useState<any>('');

   /**
    * Quando input não for um string vazia, irá mostrar valor de selected
    */
   const [typing, setTyping] = useState(false);

   /**
    * Passar referência de input para ref
    */
   useImperativeHandle(ref, () => textInput.current, []);

   /**
    * Debounce para não sobrecarregar servidor
    */
   const debounce = useRef<any>();
   const $destroy = useRef(new Subject());
   const closeSelectAutocomplete = useRef<any>();

   useEffect(() => () => debounce.current && debounce.current.cancel(), []);

   /**
    * Component NotFound custom
    */
   const NotFoundComponent = (NotFoundComponentCustom ?? NotFoundAutoCompleteComponent) as any;

   function onChangeDebounced(text: string): Promise<void> {
      if (options) {
         setPosition(0);
         setLengthItems(options.length);
         setResults(
            options
               .filter((i) => NormalizeString(i.name).includes(NormalizeString(text)))
               .map((el, i) => ({ ...el, id: i })),
         );
         setOpen(true);
         setSearching(false);

         return;
      }

      fetch(text)
         .pipe(takeUntil($destroy.current))
         .subscribe(
            (results) => {
               setPosition(0);
               const items = convertArrayFetchToOptions(results);
               setLengthItems(items.filter((i) => !i.disabled).length);
               setResults(items);
               setOpen(true);
               setNotFound(items.filter((i) => !i.disabled).length === 0);

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

   function handleInputChange(text: string): void {
      setTyping(textInput.current.value != '');

      if (debounce.current) debounce.current.cancel();
      setSearching(true);
      debounce.current = _.debounce(onChangeDebounced, 300);
      debounce.current(text);
      if (onInput) onInput(text);
   }

   function closeAutoComplete(): void {
      closeSelectAutocomplete.current = setInterval(() => setOpen(false), 200);
      textInput.current.value = '';
      setTyping(false);
      if (rest.onBlur) rest.onBlur();
   }

   function openAutoComplete(): void {
      textInput.current.focus();
      clearInterval(closeSelectAutocomplete.current);
      setOpen(true);
      if (rest.onFocus) rest.onFocus();
   }

   function clearInput(): void {
      if (onSelect) onSelect(null);
   }

   function selectItem(e): void {
      textInput.current.blur();

      // reset auto complete
      if (options) setResults(options.map((el, i) => ({ ...el, id: i })));

      if (!e.disabled) {
         if (onSelect) onSelect(e.value);
         // setSelectedLabel(e.name);
         setOpen(false);
      }

      textInput.current.value = '';
      setTyping(false);
   }

   // ClearInternal
   useEffect(
      () => () => {
         clearInterval(closeSelectAutocomplete.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);
   });

   const label = value ? (options ?? result).find((i) => _.isEqual(i.value, value))?.name ?? '' : null;

   return (
      <FormStyle data-cy={`autocomplete-${name}`} {...rest}>
         <S.Container>
            <Input
               {...inputProps}
               ref={textInput}
               onInput={handleInputChange}
               onFocus={openAutoComplete}
               onBlur={closeAutoComplete}
               loading={searching}
               data-testid="select-autocomplete--input"
               prefix={
                  <>
                     {!typing && (
                        <div className="text-selected" data-testid="text-selected-label">
                           {label}
                        </div>
                     )}
                     {!typing && !label && placeHolder && <div className="place-holder">{placeHolder}</div>}
                  </>
               }
               suffix={
                  <>
                     {label && isClearable && (
                        <CloseBtn
                           size={13}
                           color="#444"
                           onClick={clearInput}
                           data-testid="select-autocomplete--clear"
                        />
                     )}

                     <span className="line" />

                     <span onClick={openAutoComplete}>
                        <div className="select-icon">
                           <FontAwesomeIcon icon={faAngleDown} />
                        </div>
                     </span>
                  </>
               }
            />

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

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

export const SelectAutocomplete = React.forwardRef<any, IPropsSelectAutoComplete>(function SelectAutocomplete(
   props,
   ref,
) {
   return <SelectAutocompleteCore {...props} as={SelectAutocompletePure} ref={ref} />;
});
