import React, { useEffect, useImperativeHandle, useRef, useState } from 'react';
import { useEventListener } from '@devesharp/react-hooks';
import { NormalizeString } from '@devesharp/react-utils';
import { faCheck } from '@fortawesome/free-solid-svg-icons';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import * as _ from 'lodash';
import { lighten } from 'polished';
import { Subject } from 'rxjs';
import { takeUntil } from 'rxjs/operators';
import { CloseBtn } from '../CloseBtn';
import { Input } from '../Input';
import { FormStyle } from '../styles';
import { IPropsTagInput } from './InputTags.interface';
import Style from './InputTags.style';
import { InputTagsCore } from './InputTagsCore';

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

export const InputTagsPure = React.forwardRef<any, IPropsTagInput>(function AutocompletePure(
   {
      renderInput = (item: any) => <div>{item}</div>,
      name,
      onSelect,
      fetch,
      setError,
      cleanAfterSelect = false,
      NotFoundComponent: NotFoundComponentCustom,
      //
      title,
      prefix,
      suffix,
      textRight,
      textCenter,
      loading,
      mask,
      withoutDotNumber,
      decimals,
      error,
      value,
      onInput,
      onEnter,
      tags = [],
      removeTag,
      defaultValue,
      options,
      ...rest
   },
   ref,
) {
   const inputProps = {
      title,
      prefix,
      suffix,
      textRight,
      textCenter,
      loading,
      mask,
      withoutDotNumber,
      decimals,
      error,
      value,
      onInput,
      onEnter,
      defaultValue,
   };

   const textInput = useRef(null);
   const scrollTags = 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, []);

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

   // component not found
   const NotFoundComponent = (NotFoundComponentCustom ?? NotFoundAutoCompleteComponent) as any;

   const onChangeDebounced = async (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);
            },
         );
   };

   const handleInputChange = (text: string): void => {
      if (debounce.current) debounce.current.cancel();
      setSearching(true);
      debounce.current = _.debounce(onChangeDebounced, 300);
      debounce.current(text);
      if (onInput) onInput(text);
   };

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

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

   function selectItem(e): void {
      if (!e.disabled) {
         if (onSelect) onSelect(e.name, e);

         textInput.current.value = e.name;
         if (onInput) onInput(e.name);

         textInput.current.focus();
         // setResults([]);
         // setOpen(false);
         if (cleanAfterSelect) {
            textInput.current.value = '';
            if (onInput) onInput('');
         }
      }
   }

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

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

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

         // Movimentar scroll junto com o item selecionado
         const itemSize = (Array.from(scrollTags.current.getElementsByTagName('li'))[0] as any).getBoundingClientRect().height;
         const { height: heightScroll } = scrollTags.current.getBoundingClientRect();
         const heightScrollTop = scrollTags.current.scrollTop;

         if (!(heightScrollTop < itemSize * nextPos && heightScrollTop + heightScroll > itemSize * nextPos)) {
            scrollTags.current.scrollTop = itemSize * (nextPos + 1) - heightScroll;
         }

         setPosition(nextPos);
      }
   });

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

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

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

         // Movimentar scroll junto com o item selecionado
         const itemSize = (Array.from(scrollTags.current.getElementsByTagName('li'))[0] as any).getBoundingClientRect().height;
         const { height: heightScroll } = scrollTags.current.getBoundingClientRect();
         const heightScrollTop = scrollTags.current.scrollTop;

         if (!(heightScrollTop < itemSize * nextPos && heightScrollTop + heightScroll > itemSize * nextPos)) {
            scrollTags.current.scrollTop = itemSize * nextPos;
         }

         setPosition(nextPos);
      }
   });

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

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

            <div className="input-border autocomplete-border">
               {!!tags.length && (
                  <span className="tags">
                     {tags.map((tag, i) => (
                        <div className="tag" onClick={() => removeTag(tag)} key={`tag-${i}-${tag.name}`}>
                           <div className="name">{tag.name}</div>
                           <div className="close-btn">
                              <CloseBtn size={10} color="white" />
                           </div>
                        </div>
                     ))}
                  </span>
               )}
               <Input
                  {...inputProps}
                  ref={textInput}
                  title={null}
                  onInput={handleInputChange}
                  onFocus={openAutoComplete}
                  onBlur={closeAutoComplete}
                  loading={searching}
                  data-testid="autocomplete--input"
               />
            </div>

            {open && (
               <ul className="autocomplete-list" data-testid="autocomplete-list" ref={scrollTags}>
                  {result.map((e, i) => (
                     <li
                        key={e.id}
                        onClick={() => selectItem(e)}
                        onMouseOver={() => !e.disabled && setPosition(e.id)}
                        data-testid={`autocomplete-list--row-${i}`}
                        id={`autocomplete-list--row-${i}`}
                        className={`${position === e.id && 'active'} ${!!e.disabled && 'disabled'} ${
                           tags.find((tag) => (tag as any).id === (e.value as any).id) && 'selected'
                        }`}
                     >
                        <span
                           className={`checkbox ${
                              tags.find((tag) => (tag as any).id === (e.value as any).id) && 'active'
                           }`}
                        >
                           <FontAwesomeIcon icon={faCheck} />
                        </span>
                        {renderInput(e.name, e.value)}
                     </li>
                  ))}
                  {notFound && <NotFoundComponent />}
               </ul>
            )}
         </Style>
      </FormStyle>
   );
});

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

export const InputTags = React.forwardRef<any, IPropsTagInput>(function Autocomplete(props, ref) {
   return <InputTagsCore {...props} as={InputTagsPure} ref={ref} />;
});
