import React, { useCallback, useImperativeHandle, useLayoutEffect, useMemo, useRef, useState } from 'react';
import { useDidUpdateEffect } from '@devesharp/react-hooks/lib/useDidUpdateEffect';
import useResizeObserver from '@react-hook/resize-observer';
import { IPropsTabs } from './Tabs.interface';
import * as S from './Tabs.style';

export const Tabs = React.forwardRef(function Tabs(
   {
      options,
      onSelect,
      disabled = false,
      skeleton = false,
      defaultTab,
      backgroundActiveColor,
      textActiveColor,
      textColor,
      activeButtonRounded,
   }: IPropsTabs,
   ref: any,
) {
   // Tab selecionada (index)
   const indexDefault = useMemo(() => options.findIndex((i) => i.id === defaultTab), [options]);
   const [selectTabIndex, setSelectTabIndex] = useState<number>(indexDefault === -1 ? 0 : indexDefault);
   // Style do selector (background que fica se movendo atrás das tabs)
   const [selectorStyle, setSelectorStyle] = useState<any>({});
   // Ref de todas as tabs
   const tabRefs = useRef<any>({});
   // Ref do bontainer de tabs
   const tabContainerRef = useRef<any>();

   useImperativeHandle(
      ref,
      () => ({
         setTab: (tab) => {
            const indexTab = options.findIndex((i) => i.id === tab);

            if (indexTab !== -1) {
               setSelectTabIndex(indexTab);
               const currentTab = options.find((i) => i.id === tab);
               setSelectorPosition(currentTab, indexTab);
            }
         },
         getTab: () => {
            return options[selectTabIndex];
         },
      }),
      [options, selectTabIndex],
   );

   useDidUpdateEffect(() => {
      onSelect(options[selectTabIndex]);
   }, [selectTabIndex]);

   /**
    * Selecionar tab
    */
   const setTab = useCallback(function setTab(item, indexTab): void {
      setSelectTabIndex((currentSelect) => {
         if (currentSelect !== indexTab) {
            return indexTab;
         }
         return currentSelect;
      });
   }, []);

   /**
    * Ao selecionar tab
    * @param item
    * @param i
    */
   function onSetTab(item: any, i: number): any {
      setSelectorPosition(item, i);
      return !disabled && !skeleton && setTab(item, i);
   }

   /**
    * Define posição inicial do selector
    */
   useLayoutEffect(() => {
      setSelectorPosition(options[selectTabIndex], selectTabIndex, false);
   }, []);

   /**
    * Mover selecter
    * @param tab
    * @param i
    */
   function setSelectorPosition(tab: any, i: number, animation = true): void {
      const { clientWidth, clientHeight, offsetLeft, offsetTop } = tabRefs.current[i];

      setSelectorStyle({
         width: `${clientWidth}px`,
         height: `${clientHeight}px`,
         left: `${offsetLeft}px`,
         top: `${offsetTop}px`,
         transition: animation ? '200ms all ease-in-out' : null,
      });

      scrollToLeft(tabContainerRef.current, offsetLeft - clientWidth / 2 + clientWidth / 2, 100);
   }

   /**
    * Animação do Scroll
    * Posiciona tab no centro da view, caso haja scroll
    */
   function scrollToLeft(element: any, to, duration): void {
      const start = element.scrollLeft;
      const change = to - start;
      let currentTime = 0;
      const increment = 20;

      const easeInOutQuad = (t, b, c, d): any => {
         t /= d / 2;
         if (t < 1) {
            return (c / 2) * t * t + b;
         }

         t -= 1;

         return (-c / 2) * (t * (t - 2) - 1) + b;
      };

      const animateScroll = function (): void {
         currentTime += increment;
         const val = easeInOutQuad(currentTime, start, change, duration);
         element.scrollLeft = val;
         if (currentTime < duration) {
            setTimeout(animateScroll, increment);
         }
      };
      animateScroll();
   }

   function onUpdateSize(): void {
      setSelectorPosition(options[selectTabIndex], selectTabIndex, false);
   }

   return (
      <S.TabsContainer
         textColor={textColor}
         backgroundActiveColor={backgroundActiveColor}
         textActiveColor={textActiveColor}
         activeButtonRounded={activeButtonRounded}
      >
         <div className="tabs" ref={tabContainerRef}>
            <div className="tab-selector" data-testid="tab-selector" style={selectorStyle} />
            {options &&
               options.map((item, i) => (
                  <Tab
                     key={i}
                     index={i}
                     item={item}
                     onSetTab={onSetTab}
                     selectTabIndex={selectTabIndex}
                     skeleton={skeleton}
                     tabRefs={tabRefs}
                     onUpdateSize={onUpdateSize}
                  />
               ))}
         </div>
      </S.TabsContainer>
   );
});

function Tab({ index, item, onSetTab, selectTabIndex, skeleton, tabRefs, onUpdateSize }): JSX.Element {
   const ref = useRef<any>();
   useResizeObserver(ref, (entry) => {
      onUpdateSize();
   });

   return (
      <div
         key={index}
         data-index={index}
         data-testid={`tab-${item.id}`}
         className={`tab ${index === selectTabIndex && 'active'}`}
         onClick={() => onSetTab(item, index)}
         ref={(_ref) => {
            tabRefs.current[index] = _ref;
            ref.current = _ref;
         }}
      >
         <span className={`${skeleton && 'skeleton'}`}>{item.name}</span>
      </div>
   );
}
