import React, { createContext, useContext, useEffect, useImperativeHandle, useRef, useState } from 'react';
import { ScrollView, View } from 'react-native';
import dot from 'dot-object';
import useDeepCompareEffect, { useDeepCompareEffectNoCheck } from 'use-deep-compare-effect';
import { useImmer } from 'use-immer';
import {
   IScrollViewSectionContainerProps,
   IScrollViewSectionContainerRef,
   IScrollViewSectionProps,
} from './ScrollViewSection.interface';

const ContextScroll = createContext<any>({} as any);

function useUpdateKey(name): any {
   const { registerItem, unRegisterItem } = useContext(ContextScroll);

   useRef(() => () => unRegisterItem(name));
   return {
      registerItem,
   };
}

export function ScrollViewSection({ testID = null, name, children }: IScrollViewSectionProps): JSX.Element {
   const viewRef = useRef<any>();

   const { registerItem } = useUpdateKey(name);

   function onLayout(): void {
      viewRef.current.measure((fx, fy, width, height, px, py) => {
         registerItem({
            name,
            pos: py,
         });
      });
   }

   return (
      <View testID={testID} ref={viewRef} onLayout={onLayout}>
         {children}
      </View>
   );
}

export const ScrollViewSectionContainer = React.forwardRef<
   IScrollViewSectionContainerRef,
   IScrollViewSectionContainerProps
>(function ScrollViewSectionContainer({ testID = null, onChange, children }, ref) {
   const [items, setItems] = useImmer({});
   const [currentSection, setCurrentSection] = useState<any>();
   const [heightScroll, setHeightScroll] = useState(0);
   const scrollPosY = useRef(0);
   const scrollOffset = useRef(0);
   const itemsArray = useRef([]);
   const ScrollViewRef = useRef<ScrollView>();

   useImperativeHandle(
      ref,
      () => ({
         setSection(name) {
            if (items[name]) {
               ScrollViewRef.current.scrollTo({
                  x: 0,
                  y: items[name].pos,
                  animated: true,
               });

               // Para não atualizar componente dentro de outro componente
               setTimeout(() => {
                  setCurrentSection(items[name]);
               }, 1);
            }
         },
      }),
      [items],
   );

   useEffect(() => {
      scrollPosY.current = 0;
   }, []);

   useDeepCompareEffectNoCheck(() => {
      if (onChange && currentSection) {
         onChange(currentSection.name);
      }
   }, [currentSection, onChange]);

   function registerItem({ name, pos }): void {
      setItems((draft) => {
         dot.set(
            name,
            {
               name,
               pos: pos + scrollPosY.current - scrollOffset.current,
            },
            draft,
         );
      });
   }

   function unRegisterItem(name: string): void {
      setItems((draft) => {
         dot.remove(name, draft);
      });
   }

   useDeepCompareEffect(() => {
      itemsArray.current = Object.values(items);
   }, [items]);

   return (
      <ContextScroll.Provider
         value={{
            registerItem,
            unRegisterItem,
            items: [],
         }}
      >
         <ScrollView
            ref={ScrollViewRef}
            testID={testID}
            onLayout={(ev) => {
               scrollOffset.current = ev.nativeEvent.layout.y;
               setHeightScroll(ev.nativeEvent.layout.height);
            }}
            onScroll={(event) => {
               scrollPosY.current = event.nativeEvent.contentOffset.y;
            }}
            onMomentumScrollEnd={() => {
               let currentOffset = 0;
               let selectSection: any;

               itemsArray.current.forEach((item) => {
                  if (scrollPosY.current >= item.pos - heightScroll * 0.2 && item.pos >= currentOffset) {
                     currentOffset = item.pos;
                     selectSection = item;
                  }
               });

               setCurrentSection(() => selectSection);
            }}
         >
            {children}
         </ScrollView>
      </ContextScroll.Provider>
   );
});
