// noinspection JSUnusedGlobalSymbols

import { useMenuContext } from '@/components/Layout/MenuProvider';
import { useThemeContext } from '@/components/Layout/ThemeProvider';
import { ChangePasswordForm } from '@/components/ra-forms';
import { ApplicaAuthProvider } from '@applica-software-guru/iam-client';
import { Dialog, useMediaQuery, useTheme } from '@mui/material';
import _ from 'lodash';
import { UseGetIdentityResult, useAuthProvider, useGetIdentity } from 'ra-core';
import React, {
  createContext,
  forwardRef,
  useCallback,
  useContext,
  useEffect,
  useMemo,
  useReducer,
  useState
} from 'react';
import { useLoading } from 'react-admin';

enum LayoutActionType {
  UPDATE_MEDIA = 'updateMedia',
  UPDATE_DRAWER = 'updateDrawer',
  UPDATE_LOGO = 'updateLogo',
  UPDATE_NOTIFICATIONS = 'updateNotifications',
  UPDATE_THEME = 'updateTheme'
}

type ILayoutProviderProps = React.PropsWithChildren<{
  logoMain?: React.ReactElement;
  logoIcon?: React.ReactElement;
  enableNotification: boolean;
  notification?: string;
}>;
type ILayoutMediaState = {
  downMd: boolean;
  downLg: boolean;
  downXl: boolean;
  horizontal: boolean;
};
type ILayoutDrawerState = {
  open: boolean;
  width: number;
  handleDrawerToggle: () => void;
};
type ILayoutLogoState = {
  icon?: React.ReactElement;
  main?: React.ReactElement;
};
type ILayoutNotificationState = {
  enable: boolean;
  resource: string;
};
type ILayoutThemeState = {
  iconColorOpen: string;
  iconColor: string;
};
type ILayoutState = {
  drawer: ILayoutDrawerState;
  media: ILayoutMediaState;
  logo: ILayoutLogoState;
  notifications: ILayoutNotificationState;
  theme: ILayoutThemeState;
};
type ILayoutAction =
  | {
      type: LayoutActionType.UPDATE_MEDIA;
      payload: Partial<ILayoutMediaState>;
    }
  | {
      type: LayoutActionType.UPDATE_DRAWER;
      payload: Partial<ILayoutDrawerState>;
    }
  | {
      type: LayoutActionType.UPDATE_LOGO;
      payload: Partial<ILayoutLogoState>;
    }
  | {
      type: LayoutActionType.UPDATE_NOTIFICATIONS;
      payload: Partial<ILayoutNotificationState>;
    }
  | {
      type: LayoutActionType.UPDATE_THEME;
      payload: Partial<ILayoutThemeState>;
    };
type ILayoutContext = {
  state: ILayoutState;
  dispatch: React.Dispatch<ILayoutAction>;
};

function reducer(state: ILayoutState, action: ILayoutAction) {
  const newState = _.clone(state);
  const { type, payload } = action;
  switch (type) {
    case LayoutActionType.UPDATE_MEDIA: {
      const media = _.chain(newState.media).clone().extend(payload).value();
      return _.extend(newState, { media: media });
    }
    case LayoutActionType.UPDATE_DRAWER: {
      const drawer = _.chain(newState.drawer).clone().extend(payload).value();
      return _.extend(newState, { drawer: drawer });
    }
    case LayoutActionType.UPDATE_LOGO: {
      const logo = _.chain(newState.logo).clone().extend(payload).value();
      return _.extend(newState, { logo: logo });
    }
    case LayoutActionType.UPDATE_NOTIFICATIONS: {
      const notifications = _.chain(newState.notifications).clone().extend(payload).value();
      return _.extend(newState, { notifications: notifications });
    }
    case LayoutActionType.UPDATE_THEME: {
      const theme = _.chain(newState.theme).clone().extend(payload).value();
      return _.extend(newState, { theme: theme });
    }
    default:
      return newState;
  }
}

const DefaultState: ILayoutState = {
  drawer: {
    open: true,
    width: 260,
    handleDrawerToggle: () => {}
  },
  media: {
    downMd: false,
    downLg: false,
    downXl: false,
    horizontal: false
  },
  logo: {},
  notifications: {
    enable: true,
    resource: 'entities/notification'
  },
  theme: {
    iconColor: 'grey.300',
    iconColorOpen: 'grey.100'
  }
};
const LayoutContext = createContext<ILayoutContext | undefined>(undefined);

function LayoutProvider(props: ILayoutProviderProps) {
  const authProvider = useAuthProvider<ApplicaAuthProvider>();
  const identity = useGetIdentity() as UseGetIdentityResult;
  const theme = useTheme();
  const downMd = useMediaQuery(theme.breakpoints.down('md'));
  const downLg = useMediaQuery(theme.breakpoints.down('lg'));
  const downXl = useMediaQuery(theme.breakpoints.down('xl'));
  const { miniDrawer, isHorizontalLayout, themeOrientation, drawerWidth } = useThemeContext();
  const isHorizontal = isHorizontalLayout(themeOrientation) && !downLg;
  const { drawerOpen, openDrawer } = useMenuContext();
  const handleDrawerToggle = useCallback(() => {
    openDrawer(!drawerOpen);
  }, [drawerOpen, openDrawer]);
  const [needToChangePassword, setNeedToChangePassword] = useState(false);
  const [state, dispatch] = useReducer(
    reducer,
    _.chain(DefaultState)
      .clone()
      .merge({ drawer: { open: !miniDrawer || drawerOpen } })
      .value()
  );
  const handlePasswordChange = useCallback(() => setNeedToChangePassword(false), []);
  const value = useMemo(() => ({ state, dispatch }), [state, dispatch]);
  const { logoIcon, logoMain, enableNotification, notification } = props;
  const isLayoutLoading = useLoading();

  useEffect(() => {
    dispatch({
      type: LayoutActionType.UPDATE_MEDIA,
      payload: {
        downMd: downMd,
        downLg: downLg,
        downXl: downXl,
        horizontal: isHorizontal
      }
    });
  }, [downMd, downLg, downXl, isHorizontal]);

  useEffect(() => {
    dispatch({
      type: LayoutActionType.UPDATE_DRAWER,
      payload: {
        open: drawerOpen,
        width: drawerWidth,
        handleDrawerToggle: handleDrawerToggle
      }
    });
  }, [drawerOpen, drawerWidth, handleDrawerToggle]);

  useEffect(() => {
    if (!miniDrawer) {
      openDrawer(!downXl);
    }
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [downXl, miniDrawer]);

  useEffect(() => {
    dispatch({
      type: LayoutActionType.UPDATE_LOGO,
      payload: {
        main: logoMain,
        icon: logoIcon
      }
    });
  }, [logoIcon, logoMain]);

  useEffect(() => {
    dispatch({
      type: LayoutActionType.UPDATE_NOTIFICATIONS,
      payload: {
        enable: enableNotification,
        resource: notification ?? 'entities/notification'
      }
    });
  }, [enableNotification, notification]);

  useEffect(() => {
    dispatch({
      type: LayoutActionType.UPDATE_THEME,
      payload: {
        iconColorOpen: theme.palette.mode === 'dark' ? 'grey.200' : 'grey.300',
        iconColor: theme.palette.mode === 'dark' ? 'background.default' : 'grey.100'
      }
    });
  }, [theme.palette.mode]);

  useEffect(() => {
    if (isLayoutLoading) return;
    // Check if authProvider contains a method called isImpersonating
    if (!authProvider.isImpersonating) {
      // eslint-disable-next-line no-console
      console.warn('authProvider does not contain a method called isImpersonating', authProvider);
      return;
    }
    authProvider?.isImpersonating().then((isImpersonating: boolean) => {
      setNeedToChangePassword(!isImpersonating && identity?.data?.needToChangePassword === true);
    });
  }, [identity?.data?.needToChangePassword, authProvider, isLayoutLoading]);

  return (
    <LayoutContext.Provider value={value}>
      <Dialog open={needToChangePassword} maxWidth="xs" fullWidth>
        <ChangePasswordForm onSuccess={handlePasswordChange} firstAccess />
      </Dialog>
      {props.children}
    </LayoutContext.Provider>
  );
}

function withLayoutProvider<P, T>(Component: React.ComponentType<P>) {
  return forwardRef((props: P & ILayoutProviderProps & any, ref: React.ForwardedRef<T>) => {
    const { logoIcon, logoMain, enableNotification, notification } = props;
    return (
      <LayoutProvider
        logoIcon={logoIcon}
        logoMain={logoMain}
        enableNotification={enableNotification}
        notification={notification}
      >
        <Component ref={ref} {...props} />
      </LayoutProvider>
    );
  });
}

function useLayoutContext(): ILayoutContext {
  const context = useContext(LayoutContext);
  if (context === undefined) {
    throw new Error(
      '[Layout] useLayoutContext: context undefined. Please provide a valid LayoutContext using LayoutProvider'
    );
  }
  return context;
}

function useLayoutDispatch(): React.Dispatch<ILayoutAction> {
  return useLayoutContext().dispatch;
}

function useLayoutState(): ILayoutState {
  return useLayoutContext().state;
}

function useLayoutMediaState(): ILayoutMediaState {
  return useLayoutState().media;
}

function useLayoutDrawerState(): ILayoutDrawerState {
  return useLayoutState().drawer;
}

function useLayoutLogoState(): ILayoutLogoState {
  return useLayoutState().logo;
}

function useLayoutNotificationsState(): ILayoutNotificationState {
  return useLayoutState().notifications;
}

function useLayoutThemeState(): ILayoutThemeState {
  return useLayoutState().theme;
}

export type { ILayoutProviderProps };

export {
  LayoutProvider,
  useLayoutContext,
  useLayoutDispatch,
  useLayoutDrawerState,
  useLayoutLogoState,
  useLayoutMediaState,
  useLayoutNotificationsState,
  useLayoutState,
  useLayoutThemeState,
  withLayoutProvider
};
