import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
import { useInfiniteQuery, useMutation, useQueryClient } from 'react-query';

import isNil from 'lodash-es/isNil';

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

import { logEvent } from 'src/config/logging';
import { EVENTS_SOCKETS } from 'src/constants/sockets';
import { useSocket } from 'src/contexts/SocketContext';
import {
  getNotifications,
  markAsRead,
  readAllNotifications,
} from '../services';
import { type NotificationItem } from '../types';
import { EventName } from 'src/types/amplitude';
import { useLokaliseTranslation } from 'src/utils/i18n';

import NotificationSidePanelContent from './components/NotificationSidePanelContent';
import NotificationSidePanelHeader from './components/NotificationSidePanelHeader';
import { NOTIFICATIONS_PAGE_SIZE } from '../constants';
import { notificationCenterKeys } from '../queries';

type UseNotificationSidePanelControllerArgs = {
  onClose: () => void;
};

export const useNotificationSidePanelController = ({
  onClose,
}: UseNotificationSidePanelControllerArgs) => {
  const { t } = useLokaliseTranslation('notifications_center');
  const queryClient = useQueryClient();
  const { enqueueSnackbar } = useHuSnackbar();
  const socket = useSocket();

  const [notifications, setNotifications] = useState<NotificationItem[] | null>(
    null,
  );
  const [showUnread, setShowUnread] = useState(false);
  const [isFetchingRead, setIsFetchingRead] = useState(false);
  const showUnreadRef = useRef(showUnread);

  const { isLoading, fetchNextPage, hasNextPage, isFetchingNextPage, isError } =
    useInfiniteQuery(
      notificationCenterKeys.list(showUnread),
      ({ pageParam = '' }) =>
        getNotifications(showUnread, pageParam, NOTIFICATIONS_PAGE_SIZE),
      {
        getNextPageParam: lastPage => {
          return lastPage?.data?.cursor;
        },
        onSuccess: response => {
          setNotifications(response.pages.flatMap(page => page.data.items));
          setIsFetchingRead(false);
        },
        refetchOnWindowFocus: false,
      },
    );

  const resetViewState = useCallback(() => {
    setShowUnread(false);
    showUnreadRef.current = false;
  }, []);

  const handleOpen = useCallback(() => {
    logEvent(EventName.NC_LIST_OPEN);
  }, []);

  const handleClose = useCallback((): void => {
    onClose();
  }, [onClose]);

  const readMutation = useMutation(
    ({ id, read }: { id: string; read: boolean }) => markAsRead(id, read),
    {
      onSuccess: () => {
        queryClient.invalidateQueries(notificationCenterKeys.stats());
      },
      onError: () => {
        enqueueSnackbar({
          title: t('mark_read_error'),
          description: t('error_message_description'),
          variant: 'error',
        });
      },
    },
  );

  const handleRead = useCallback(
    (id: string, read: boolean) => {
      if (readMutation.isLoading) return;
      setNotifications(prevNotifications =>
        !isNil(prevNotifications)
          ? prevNotifications.map(notification =>
              notification.id === id
                ? { ...notification, isRead: !notification.isRead }
                : notification,
            )
          : prevNotifications,
      );
      readMutation.mutate({ id, read });
    },
    [readMutation],
  );

  const readAllMutation = useMutation(() => readAllNotifications(), {
    onSuccess: () => {
      queryClient.invalidateQueries(notificationCenterKeys.stats());

      setNotifications(prevNotifications =>
        !isNil(prevNotifications)
          ? prevNotifications.map(notification => ({
              ...notification,
              isRead: true,
            }))
          : prevNotifications,
      );

      enqueueSnackbar({
        title: t('all_read_success'),
        variant: 'success',
      });
    },
    onError: () => {
      enqueueSnackbar({
        title: t('mark_all_read_error'),
        variant: 'error',
      });
    },
  });

  const handleMarkAllRead = useCallback(() => {
    readAllMutation.mutate();
  }, [readAllMutation]);

  const handleDelete = useCallback(
    (id: string) => {
      // Temporal fake deletion
      setNotifications(prevNotifications =>
        !isNil(prevNotifications)
          ? prevNotifications.filter(notification => notification.id !== id)
          : prevNotifications,
      );
      // Eventually replaced by deleteMutation.mutate({ id });
      enqueueSnackbar({
        title: t('delete_notification_success'),
        variant: 'success',
      });
    },
    [enqueueSnackbar, t],
  );

  const handleTabChange = useCallback(() => {
    setIsFetchingRead(true);
    setShowUnread(prev => {
      const next = !prev;
      showUnreadRef.current = next;
      return next;
    });
  }, []);

  useEffect(() => {
    if (!socket) {
      return;
    }

    const handleNotificationCreated = () => {
      queryClient.invalidateQueries(
        notificationCenterKeys.list(showUnreadRef.current),
      );
      queryClient.invalidateQueries(notificationCenterKeys.stats());
    };

    socket.listenEvent(
      EVENTS_SOCKETS.NEW_NOTIFICATION_CREATED,
      handleNotificationCreated,
    );

    return () => {
      socket.closeEvent(
        EVENTS_SOCKETS.NEW_NOTIFICATION_CREATED,
        handleNotificationCreated,
      );
    };
  }, [socket, queryClient]);

  const hasNotifications = !isNil(notifications);
  const notificationsCount = hasNotifications ? notifications.length : 0;
  const loading = isLoading || isFetchingRead || !hasNotifications;
  const showError = !loading && notificationsCount === 0 && isError;
  const showEmpty = !loading && notificationsCount === 0 && !isError;
  const hasUnread = hasNotifications
    ? notifications.some(n => !n.isRead)
    : false;

  const disableHeaderActions = loading;
  const disableMarkAllRead = !hasUnread || loading || readAllMutation.isLoading;

  const headerNode = useMemo(
    () => (
      <NotificationSidePanelHeader
        disableHeaderActions={disableHeaderActions}
        disableMarkAllRead={disableMarkAllRead}
        onTabChange={handleTabChange}
        onMarkAllRead={handleMarkAllRead}
      />
    ),
    [
      disableHeaderActions,
      disableMarkAllRead,
      handleTabChange,
      handleMarkAllRead,
    ],
  );

  const bodyNode = useMemo(
    () => (
      <NotificationSidePanelContent
        notifications={hasNotifications ? notifications : []}
        loading={loading}
        hasNextPage={hasNextPage}
        isFetchingNextPage={isFetchingNextPage}
        isMarkingRead={readMutation.isLoading}
        onOpen={handleClose}
        onRead={handleRead}
        onDelete={handleDelete}
        onFetchNextPage={fetchNextPage}
        isEmpty={showEmpty}
        showError={showError}
      />
    ),
    [
      hasNotifications,
      notifications,
      loading,
      hasNextPage,
      isFetchingNextPage,
      readMutation.isLoading,
      handleClose,
      handleRead,
      handleDelete,
      fetchNextPage,
      showEmpty,
      showError,
    ],
  );

  return {
    headerNode,
    bodyNode,
    handleClose,
    onPanelOpen: handleOpen,
    onPanelAfterClose: resetViewState,
  };
};
