import {
  createContext,
  type ReactNode,
  useContext,
  useEffect,
  useState,
} from 'react';

import useHuSnackbar from '@material-hu/components/design-system/Snackbar';

import { encryptStorage } from 'src/config/encrypt-storage';
import { queryClient } from 'src/config/react-query';
import { type ExtendedCallback, SocketConnection } from 'src/config/socket';
import { EVENTS_SOCKETS } from 'src/constants/sockets';
import useAuth from 'src/contexts/JWTContext';
import useFeatureFlag from 'src/hooks/useFeatureFlag';
import { GRANTS_INVALIDATION_QUERY_KEYS } from 'src/pages/dashboard/Users/grants';
import { FeatureFlags } from 'src/types/featureFlags';
import { type UserDeletedSocket, type UserLogoutSocket } from 'src/types/user';
import { useLokaliseTranslation } from 'src/utils/i18n';

import CircularProgress from 'src/components/CircularProgress';

type SocketProviderProps = {
  children?: ReactNode;
};

type SocketContextValue = {
  startSocket: (environment: string, token: string, cb: () => void) => void;
  listenEvent: (event: string, cb: ExtendedCallback) => void;
  emitEvent: (event: string, data: any) => void;
  closeEvent: (event: string, cb: ExtendedCallback) => void;
};

const SocketContext = createContext<SocketContextValue>(new SocketConnection());

const socket = new SocketConnection();

export const SocketProvider = (props: SocketProviderProps) => {
  const { children } = props;

  const [connected, setConnected] = useState(false);

  const { t } = useLokaliseTranslation('users');
  const { user, logout, updateUserRolesAndPermissions } = useAuth();
  const { enqueueSnackbar } = useHuSnackbar();
  const isCerberusEnabled = useFeatureFlag(FeatureFlags.CERBERUS_ENABLED);

  useEffect(() => {
    socket.startSocket(import.meta.env.VITE_SOCKET_URL, () =>
      setConnected(true),
    );
    setTimeout(() => {
      setConnected(true);
    }, 5000);
    return () =>
      socket && connected && socket.stopSocket() && setConnected(false);
  }, []);

  useEffect(() => {
    const deleteUser = (data: UserDeletedSocket) => {
      if (data.id === user?.id) {
        logout?.(true);
        enqueueSnackbar({
          title: t('WARNING_USER_DELETED'),
          variant: 'warning',
        });
      }
    };

    const deactivateUser = (data: UserDeletedSocket) => {
      if (data.id === user?.id) {
        logout?.(true);
        enqueueSnackbar({
          title: t('WARNING_USER_DEACTIVATED'),
          variant: 'warning',
        });
      }
    };

    const handleValidSessions = ({ sessionIds }: { sessionIds: string[] }) => {
      const currentSession = encryptStorage.getItem('sessionId');
      if (!sessionIds.includes(currentSession)) {
        enqueueSnackbar({
          title: t('WARNING_SESSION_KICKED'),
          variant: 'warning',
        });
        logout?.(true);
      }
    };

    const handleLogoutOnAnotherDevice = (data: UserLogoutSocket) => {
      const { sessionId, reason } = data;
      const currentSession = encryptStorage.getItem('sessionId');
      if (sessionId === currentSession) {
        enqueueSnackbar({
          title: t(
            reason === 'MISSING_PERMISSIONS'
              ? 'WARNING_MISSING_PERMISSIONS'
              : 'WARNING_SESSION_KICKED',
          ),
          variant: 'warning',
        });
        logout?.(reason !== 'MISSING_PERMISSIONS');
      }
    };

    socket.listenEvent(EVENTS_SOCKETS.USER_DELETED, deleteUser);

    socket.listenEvent(EVENTS_SOCKETS.USER_DEACTIVATED, deactivateUser);

    socket.listenEvent(
      EVENTS_SOCKETS.ADMIN_CURRENT_VALID_SESSIONS,
      handleValidSessions,
    );

    socket.listenEvent(EVENTS_SOCKETS.LOGOUT, handleLogoutOnAnotherDevice);

    const handleUserPermissionsChanged = () => {
      updateUserRolesAndPermissions?.();
      for (const getKey of GRANTS_INVALIDATION_QUERY_KEYS) {
        queryClient.invalidateQueries(getKey());
      }
    };

    if (isCerberusEnabled && updateUserRolesAndPermissions) {
      socket.listenEvent(
        EVENTS_SOCKETS.USER_PERMISSIONS_CHANGED,
        handleUserPermissionsChanged,
      );
    }

    return () => {
      socket.closeEvent(EVENTS_SOCKETS.USER_DELETED, deleteUser);
      socket.closeEvent(EVENTS_SOCKETS.USER_DEACTIVATED, deactivateUser);
      socket.closeEvent(
        EVENTS_SOCKETS.ADMIN_CURRENT_VALID_SESSIONS,
        handleValidSessions,
      );
      socket.closeEvent(EVENTS_SOCKETS.LOGOUT, handleLogoutOnAnotherDevice);

      if (isCerberusEnabled && updateUserRolesAndPermissions) {
        socket.closeEvent(
          EVENTS_SOCKETS.USER_PERMISSIONS_CHANGED,
          handleUserPermissionsChanged,
        );
      }
    };
  }, []);

  return (
    <SocketContext.Provider value={socket}>
      {!connected && <CircularProgress centered />}
      {connected && children}
    </SocketContext.Provider>
  );
};

const useSocket = () => useContext(SocketContext);

export default useSocket;
