import React, { useCallback, useContext, useEffect, useImperativeHandle, useRef, useState } from 'react';
import { Animated, BackHandler, FlatList, Modal, View } from 'react-native';
import { faArrowLeft, faCheck } from '@fortawesome/free-solid-svg-icons';
import { FontAwesomeIcon } from '@fortawesome/react-native-fontawesome';
import * as _ from 'lodash';
import { Subject } from 'rxjs';
import { takeUntil } from 'rxjs/operators';
import { useTheme } from 'styled-components';
import { useImmer } from 'use-immer';
import { useEventListener } from '../../hooks/useEventListener';
import AppContext from '../AppProvider/AppContext';
import { Flex } from '../Flex/native';
import { LoaderIcon } from '../LoaderIcon/native';
import { Pressable } from '../Pressable';
import { RSValue } from '../RFValue';
import { TextNative } from '../TextNative';
import {
   BtnBack,
   Container,
   IconSearch,
   Input,
   InputContainer,
   ErrorContainer,
   ErrorContainerText,
   BtnCheck,
} from './AutocompleteFullscreen.style';

interface IPropsAutoComplete extends React.ComponentProps<any> {
   renderInput: (params: any) => any;
   renderItemSelected?: (params: any) => any;
   name: any;
   fetch: any;
   multiSelection: boolean;
   show?: boolean;
   onSelect?: (i: any) => void;
   cleanAfterSelect?: boolean;
   initialSelect?: any [];
   initialValue?: any[];
}

function treatmentResults(results: any[]): any[] {
   let i = -1;

   return results.map((el) => {
      if (el.disabled) {
         return { id: JSON.stringify(el), disabled: true, data: el };
      }

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

export const AutocompleteFullscreen = React.forwardRef<any, IPropsAutoComplete>(function Autocomplete(
   {
      renderInput,
      renderItemSelected,
      title,
      name,
      onSelect,
      fetch,
      show,
      onClose,
      value,
      initialSelect = [],
      multiSelection = false,
      cleanAfterSelect = false,
      initialValue,
      NotFoundComponent = NotFoundAutoCompleteComponent,
      ...rest
   },
   ref,
) {
   const theme: any = useTheme();
   const textInputRef = useRef(null);
   const [results, setResults] = useState(treatmentResults(initialValue || []));
   const [searching, setSearching] = useState(false);
   const [notFound, setNotFound] = useState(false);
   const [error, setError] = useState(false);

   const fadeAnim = useRef(new Animated.Value(0)).current;
   const [selected, setSelected] = useImmer(initialSelect);

   useImperativeHandle(ref, () => textInputRef.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(textInputRef.current.value)
         .pipe(takeUntil($destroy.current))
         .subscribe(
            (response) => {
               setError(false);

               const resultsP = treatmentResults(response);

               setResults(resultsP);
               setNotFound(!resultsP.length);

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

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

   const selectItem = useCallback(function selectItem(e): void {
      if (!e.disabled) {
         if (multiSelection) {
            setSelected((draft) => {
               if (draft.findIndex((v) => _.isEqual(v, e.data)) === -1) {
                  return [...draft, e.data];
               }

               draft.splice(
                  draft.findIndex((v) => _.isEqual(v, e.data)),
                  1,
               );

               return draft;
            });
         } else {
            onSelect(e.data);
            setResults(treatmentResults(initialValue || []));
            onClose();
            if (cleanAfterSelect) textInputRef.current.value = '';
         }
      }
   }, []);

   function applySelect(): void {
      onSelect(selected);
      onClose();
   }

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

   const renderRow = useCallback(
      (item) => {
         return (
            <AutoCompleteItem
               item={item.item}
               checked={selected.findIndex((v) => _.isEqual(v, item.item.data)) !== -1}
               index={`${item.item.id}`}
               selectItem={selectItem}
               renderInput={renderInput}
            />
         );
      },
      [renderInput, selected],
   );

   const renderItemSelect = useCallback(
      (item) => {
         return (
            <AutoCompleteSelectItem
               item={item.item}
               index={`${item.item.id}`}
               renderInput={renderItemSelected}
               selectItem={selectItem}
            />
         );
      },
      [renderItemSelected, selectItem],
   );

   function onRequestClose(): void {
      onClose();
   }

   return (
      <Modal visible={show} animated animationType="slide" onRequestClose={onRequestClose}>
         <Flex flex={1}>
            <InputContainer>
               <BtnBack onPress={() => onClose()} testID="back-button">
                  <Flex flex={1} alignItems="center" horizontal>
                     <IconSearch icon={faArrowLeft} color="white" size={24} />
                  </Flex>
               </BtnBack>
               <Flex flex={1}>
                  <Input
                     testID="autocomplete#input"
                     ref={textInputRef}
                     placeholder="Pesquisar"
                     onChangeText={handleInputChange}
                  />
               </Flex>
            </InputContainer>

            {!!selected.length && (
               <Flex paddingVertical={7}>
                  <FlatList
                     horizontal
                     keyExtractor={keyExtractor}
                     keyboardShouldPersistTaps="always"
                     showsHorizontalScrollIndicator={false}
                     data={selected}
                     renderItem={renderItemSelect}
                  />
               </Flex>
            )}

            {searching && (
               <Flex flex={1} justifyContent="center" testID="autocomplete#loading">
                  <LoaderIcon size={90} color={theme?.primary || 'black'} />
               </Flex>
            )}

            {!searching && error && (
               <ErrorContainer testID="autocomplete#error">
                  <ErrorContainerText>Erro ao conectar com servidor, verifique sua conexão!</ErrorContainerText>
               </ErrorContainer>
            )}

            {multiSelection && (
               <BtnCheck testID="autocomplete#apply" onPress={applySelect}>
                  <FontAwesomeIcon icon={faCheck} color="white" size={RSValue(20)} />
               </BtnCheck>
            )}

            {notFound && !searching && <NotFoundComponent />}

            {!searching && !error && (
               <FlatList
                  showsVerticalScrollIndicator={false}
                  extraData={results}
                  data={results}
                  keyboardShouldPersistTaps="always"
                  keyExtractor={keyExtractor}
                  renderItem={renderRow}
               />
            )}
         </Flex>
      </Modal>
   );
});

const NotFoundAutoCompleteComponent = (): any => {
   return <TextNative style={{ padding: 10 }}>Nenhum resultado encontrado</TextNative>;
};

function keyExtractor(item, index): string {
   return `item-${index}`;
}

const AutoCompleteItem = React.memo(
   function AutoCompleteItem({ item, index, selectItem, renderInput, checked }: any) {
      const onPress = (): any => {
         selectItem(item);
      };

      return item.disabled ? (
         <View key={`item-${index}`} testID={`item-${index}`}>
            {renderInput(item.data, checked)}
         </View>
      ) : (
         <Pressable onPress={onPress} key={`item-${index}`} testID={`item-${index}`} ripple>
            {renderInput(item.data, checked)}
         </Pressable>
      );
   },
   (old, current) => {
      return _.isEqual(old, current);
   },
);

const AutoCompleteSelectItem = React.memo(
   function AutoCompleteSelectItem({ item, index, renderInput, selectItem }: any) {
      const onPress = (): any => {
         selectItem(item);
      };

      return (
         <Pressable key={`item-${index}`} testID={`item-select-${index}`} onPress={onPress}>
            {renderInput(item)}
         </Pressable>
      );
   },
   (old, current) => {
      return _.isEqual(old, current);
   },
);
