import React, { useCallback, useEffect, useLayoutEffect, useRef } from 'react';
import { Event } from '@devesharp/react-utils/dist/Event';
import uniqueId from 'lodash/uniqueId';
import { useImmer } from 'use-immer';
import { IItemsToast, IItemToastListStore, INewToast } from './Toast.interface';
import Toast from './Toast/Toast';
import Style from './ToastList.style';

const ToastObs = new Event<INewToast>();

export const ToastList: React.FunctionComponent<any> & {
   show: (options: IItemsToast) => void;
} = function ToastList({ align = 'left' }: any) {
   const [items, setItems] = useImmer<IItemToastListStore[]>([]);
   const itemsRef = useRef<IItemToastListStore[]>([]);

   useLayoutEffect(() => {
      itemsRef.current = items;
   }, [items]);

   function addToast(toast: IItemToastListStore): void {
      const currentId = uniqueId();
      const newToast: any = { ...toast };
      newToast.opened = true;
      newToast.id = toast.id ?? currentId;

      newToast.timeout = setTimeout(() => {
         setItems((v) => {
            if (v.findIndex((i) => i.id === newToast.id) !== -1) {
               v[v.findIndex((i) => i.id === newToast.id)].opened = false;
            }
         });
      }, newToast.duration ?? 2000);

      setItems((v) => {
         v.push(newToast);
      });
   }

   useEffect(() => {
      const obs = ToastObs.observable.subscribe((toast) => {
         if (toast.id) {
            if (itemsRef.current.find((i) => i.id === toast.id)) {
               setItems((v) => {
                  const index = itemsRef.current.findIndex((i) => i.id === toast.id);

                  clearTimeout(v[index].timeout);

                  v[index].timeout = setTimeout(() => {
                     setItems((v) => {
                        if (v.findIndex((i) => i.id === toast.id) !== -1) {
                           v[v.findIndex((i) => i.id === toast.id)].opened = false;
                        }
                     });
                  }, toast.duration ?? 2000);
               });
            } else {
               addToast(toast as any);
            }
         } else {
            addToast(toast as any);
         }
      });

      return () => {
         obs.unsubscribe();
         // clear all timeouts
         setItems((v) => {
            return v.map((i) => {
               clearTimeout(i.timeout);
               return i;
            });
         });
      };
   }, []);

   const onDelete = useCallback(function onDelete(currentId: any): void {
      setItems((v) => {
         clearTimeout(v.find((i) => i.id === currentId)?.timeout);
         v.splice(
            v.findIndex((i) => i.id === currentId),
            1,
         );
      });
   }, []);

   return (
      <Style align={align}>
         {items.map((item) => (
            <Toast key={`toast-${item.id}`} item={item} onDelete={onDelete} />
         ))}
      </Style>
   );
} as any;

ToastList.show = (options: INewToast) => {
   ToastObs.set(options);
};
