import * as React from 'react';
import { useCallback } from 'react';
import type { ContextType, SidebarEventListener, SidebarEventType } from './types';
import { useTheme } from '@mui/styles';
import useMediaQuery from '@mui/material/useMediaQuery';

const RightSideBarContext = React.createContext<ContextType>(undefined);

const RightSideBarProvider: React.FC = ({ children }) => {
  const [mainComponent, setMainComponent] = React.useState<React.ReactNode>(null);
  const [open, setOpen] = React.useState<boolean>(false);
  const [openSecondary, setOpenSecondary] = React.useState<boolean>(false);
  const [componentsArray, setComponentsArray] = React.useState<React.ReactNode[]>([]);
  const [drawerLocked, setDrawerLocked] = React.useState<boolean>(false);
  const [showDrawer, setShowDrawer] = React.useState<boolean>(false);

  const eventListeners = React.useRef<Record<string, SidebarEventListener[]>>({});

  const [defaultMod, setDefaultMod] = React.useState(true);

  const durationTime = { enter: 500, exit: 400 };

  const theme = useTheme();
  const isMdUp = useMediaQuery(theme.breakpoints.up(1439));

  const removeEventListener = useCallback(
    (type: SidebarEventType, listener: SidebarEventListener) => {
      eventListeners.current[type] = eventListeners.current[type]?.filter(l => l !== listener);
    },
    []
  );

  const addEventListener = useCallback(
    (type: SidebarEventType, listener: SidebarEventListener) => {
      (eventListeners.current[type] ??= []).push(listener);
      return () => {
        removeEventListener(type, listener);
      };
    },
    [removeEventListener]
  );

  const dispatchEvent = useCallback((type: SidebarEventType, event?: Event) => {
    eventListeners.current[type]?.forEach(listener => {
      listener?.(event);
    });
  }, []);

  const displayDifComponent = useCallback((Components: React.ReactNode) => {
    setMainComponent(Components);
  }, []);

  const setDrawerBehavior = useCallback(
    (event?: Event) => {
      if (isMdUp) {
        setDrawerLocked(true);
        dispatchEvent('open', event);
        setOpen(true);
      }
    },
    [dispatchEvent, isMdUp]
  );

  const setComponentSecondaryDrawer = useCallback((Component: React.ReactNode) => {
    setComponentsArray(oldArray => [Component, ...oldArray]);
  }, []);

  const clearComponentsArray = () => {
    setTimeout(() => {
      setComponentsArray([]);
    }, 500);
  };

  const handleComponentsArray = useCallback(() => {
    if (componentsArray.length > 1) {
      return setComponentsArray(oldArray => oldArray.slice(1));
    }
    if (componentsArray.length === 1) {
      clearComponentsArray();
      setOpenSecondary(false);
    }
  }, [componentsArray.length]);

  const handleMainDrawer = useCallback(
    (event?: Event) => {
      setDefaultMod(!defaultMod);
      if (open && drawerLocked) {
        setDrawerLocked(false);
        open && dispatchEvent('closed', event);
        return setOpen(false);
      }
      if (open && isMdUp) {
        dispatchEvent('locked', event);
        return setDrawerLocked(true);
      }
      if (!open) {
        if (isMdUp) {
          dispatchEvent('locked', event);
          setDrawerLocked(true);
        }
        dispatchEvent('open', event);
        return setOpen(true);
      }
      open && dispatchEvent('closed', event);
      return setOpen(false);
    },
    [defaultMod, dispatchEvent, drawerLocked, isMdUp, open]
  );

  const value: ContextType = {
    mainComponent,
    displayDifComponent,
    open,
    setOpen,
    setDrawerBehavior,
    openSecondary,
    setOpenSecondary,
    componentsArray,
    setComponentSecondaryDrawer,
    handleComponentsArray,
    clearComponentsArray,
    handleMainDrawer,
    drawerLocked,
    setDrawerLocked,
    setShowDrawer,
    showDrawer,
    durationTime,
    addEventListener,
    removeEventListener,
    dispatchEvent,
    defaultMod,
    setDefaultMod
  };

  return <RightSideBarContext.Provider value={value}>{children}</RightSideBarContext.Provider>;
};

function useRightSideBar() {
  const context = React.useContext(RightSideBarContext);
  if (context === undefined) {
    throw new Error('useRightSideBar must be used within a RightSideBarProvider');
  }
  return context;
}

export { RightSideBarProvider, useRightSideBar };
