import {
  type Dispatch,
  type FC,
  Fragment,
  type SetStateAction,
  useCallback,
  useEffect,
  useMemo,
  useRef,
} from 'react';
import { useInView } from 'react-intersection-observer';
import { useInfiniteQuery } from 'react-query';
import { useLocation } from 'react-router-dom';

import { isSameDay } from 'date-fns';
import { debounce } from 'lodash-es';
import Divider from '@material-hu/mui/Divider';
import Stack from '@material-hu/mui/Stack';
import Typography from '@material-hu/mui/Typography';

import HuAlert from '@material-hu/components/design-system/Alert';
import HuCircularProgress from '@material-hu/components/design-system/ProgressIndicators/Spinner';

import { useAuth } from 'src/contexts/JWTContext';
import useFeatureFlag from 'src/hooks/useFeatureFlag';
import { serviceManagementKeys } from 'src/pages/dashboard/serviceManagement/queries';
import Comment from 'src/pages/dashboard/serviceManagement/servicePortal/requestedDetails/components/Comments/Comment';
import EmptyComments from 'src/pages/dashboard/serviceManagement/servicePortal/requestedDetails/components/Comments/EmptyComments';
import { getComments } from 'src/pages/dashboard/serviceManagement/services';
import {
  ApprovalStateName,
  type Task,
} from 'src/pages/dashboard/serviceManagement/types';
import {
  getApprovalAlertProps,
  getApprovalStateName,
  isApprovalTask,
} from 'src/pages/dashboard/serviceManagement/utils';
import { FeatureFlags } from 'src/types/featureFlags';
import { useLokaliseTranslation } from 'src/utils/i18n';
import { getCurrentLocale } from 'src/utils/locale';

import { useTaskVisit } from '../../../contexts/TaskVisitContext';
import useMarkLastReadComment from '../../../hooks/useMarkLastReadComment';
import useVisitTask from '../../../hooks/useVisitTask';
import useCsatSurvey from '../hooks/useCsatSurvey';

import CommentForm from './Comments/CommentForm';
import { getDateSeparator } from './Comments/utils';
import CsatSurvey from './CsatSurvey/CsatSurvey';

const DISSAPEAR_TIMEOUT_MS = 3000;

export type CommentsProps = {
  task: Task;
  showBubble: boolean;
  setShowBubble: Dispatch<SetStateAction<boolean>>;
  showUnreadMessage: boolean;
  setShowUnreadMessage: Dispatch<SetStateAction<boolean>>;
  isAgentSide: boolean;
};

const Comments: FC<CommentsProps> = ({
  task,
  showBubble,
  setShowBubble,
  showUnreadMessage,
  setShowUnreadMessage,
  isAgentSide,
}) => {
  const { t } = useLokaliseTranslation(['service_management', 'general']);
  const { user } = useAuth();
  const location = useLocation();
  const scrollRef = useRef<HTMLDivElement>(null);

  const { ref: endOfCommentsRef, inView } = useInView({ threshold: 0.1 });

  const {
    isLoading: isLoadingSurvey,
    surveyTemplate,
    hasRespondedSurvey,
    surveyEnabled,
  } = useCsatSurvey({ task });

  const {
    data: commentsData,
    isLoading,
    isFetching,
    fetchNextPage,
    hasNextPage,
    isFetchingNextPage,
  } = useInfiniteQuery(
    serviceManagementKeys.comments.list(task?.id!),
    ({ pageParam = null }) => getComments(task?.id!, pageParam),
    {
      getNextPageParam: lastPage => lastPage.data.cursor || undefined,
      enabled: !!task?.id,
      keepPreviousData: true,
    },
  );

  const loading = isLoading || !commentsData;
  // True while the first page is being (re)fetched (initial load or a
  // socket-triggered invalidation refetch). It is NOT true when paginating
  // with `fetchNextPage`, so it's safe to use as a "wait for fresh data" guard.
  const isFetchingFirstPage = isFetching && !isFetchingNextPage;
  const comments = useMemo(
    () => commentsData?.pages.flatMap(page => page.data.items).reverse() || [],
    [commentsData],
  );

  const markAsReadMutation = useMarkLastReadComment();
  const { mutate: visitTaskMutate } = useVisitTask();
  const bubblesEnabled = useFeatureFlag(
    FeatureFlags.SERVICE_MANAGEMENT_BUBBLES_ENABLED,
  );
  const { visitedTaskIdRef } = useTaskVisit();
  const visitedLastCommentIdRef = useRef<string | null>(null);

  const handleMarkLastCommentRead = useCallback(() => {
    if (!comments || !task) return;
    if (!bubblesEnabled) {
      const lastComment = comments[comments.length - 1];
      if (lastComment && !lastComment.read) {
        markAsReadMutation.mutate({
          taskId: task.id,
          commentId: lastComment.id,
        });
      }
      return;
    }
    // Bubbles path: only act after initial visit, and only if a new comment
    // arrived via socket since the last visit.
    if (visitedTaskIdRef.current !== task.id) return;
    const lastComment = comments[comments.length - 1];
    if (!lastComment || lastComment.id === visitedLastCommentIdRef.current)
      return;
    // Advance the pointer regardless — but skip the network call when the user
    // sent the comment themselves (no need to mark your own message as read).
    visitedLastCommentIdRef.current = lastComment.id;
    if (lastComment.user.id === user?.id) return;
    visitTaskMutate({
      taskId: task.id,
      body: { lastReadCommentId: lastComment.id },
    });
  }, [
    comments,
    task,
    markAsReadMutation,
    bubblesEnabled,
    visitedTaskIdRef,
    visitTaskMutate,
    user,
  ]);

  // Single source of truth for the initial visit when this component is mounted.
  // Since the comments view auto-scrolls to the bottom on mount, the user is
  // implicitly considered to have seen up to the last loaded comment. We wait
  // for the first page to finish (re)fetching to ensure we have the latest
  // comment id before sending the visit body.
  //
  // When the flag is OFF we skip `visit` entirely: the endpoint exists only
  // for the red-bubbles lifecycle, so legacy `markLastReadComment` (still
  // wired in `handleMarkLastCommentRead` below) keeps handling the read state.
  useEffect(() => {
    if (!bubblesEnabled || loading || isFetchingFirstPage || !task) return;
    if (visitedTaskIdRef.current === task.id) return;

    visitedTaskIdRef.current = task.id;

    if (comments.length === 0) {
      visitedLastCommentIdRef.current = null;
      visitTaskMutate({
        taskId: task.id,
        body: { commentsViewed: true },
      });
      return;
    }

    const lastComment = comments[comments.length - 1];
    visitedLastCommentIdRef.current = lastComment.id;
    const hasUnread = comments.some(c => !c.read);
    visitTaskMutate({
      taskId: task.id,
      body: hasUnread
        ? { lastReadCommentId: lastComment.id }
        : { commentsViewed: true },
    });
  }, [
    bubblesEnabled,
    loading,
    isFetchingFirstPage,
    task,
    comments,
    visitTaskMutate,
    visitedTaskIdRef,
  ]);

  const scrollToBottom = () => {
    if (scrollRef.current) {
      scrollRef.current.scrollTo({
        top: scrollRef.current.scrollHeight,
        behavior: 'auto',
      });
      setShowBubble(false);
      handleMarkLastCommentRead();
    }
  };
  const scrollToBottomRef = useRef(scrollToBottom);
  scrollToBottomRef.current = scrollToBottom;

  const handleMarkLastCommentReadRef = useRef(handleMarkLastCommentRead);
  handleMarkLastCommentReadRef.current = handleMarkLastCommentRead;

  useEffect(() => {
    if (inView && task) {
      if (showBubble) setTimeout(() => setShowBubble(false), 500);
      handleMarkLastCommentReadRef.current();
    }
  }, [inView, task, showBubble, setShowBubble]);

  const handleScroll = useCallback(
    debounce(() => {
      if (
        scrollRef.current &&
        scrollRef.current.scrollTop === 0 &&
        hasNextPage
      ) {
        const previousScrollHeight = scrollRef.current.scrollHeight;
        fetchNextPage().finally(() => {
          requestAnimationFrame(() => {
            if (scrollRef.current) {
              const newScrollHeight = scrollRef.current.scrollHeight;
              scrollRef.current.scrollTop =
                newScrollHeight - previousScrollHeight;
            }
          });
        });
      }
    }, 100),
    [fetchNextPage, hasNextPage],
  );

  useEffect(() => {
    if (!loading) scrollToBottomRef.current();
  }, [loading]);

  useEffect(() => {
    let timer: NodeJS.Timeout | null = null;
    if (!showBubble) {
      timer = setTimeout(
        () => setShowUnreadMessage(false),
        DISSAPEAR_TIMEOUT_MS,
      );
    }
    return () => {
      if (timer) clearTimeout(timer);
    };
  }, [showBubble, setShowUnreadMessage]);

  const unreadCommentsCount = comments.reduce(
    (acc, comment) => (comment.read ? acc : acc + 1),
    0,
  );
  const firstUnreadCommentIndex = comments.findIndex(comment => !comment.read);
  const showUnreadCommentsBubble = unreadCommentsCount > 0 && showBubble;

  const isAgentWorkspace = location.pathname.includes('agent-workspace');
  const isApprovalTaskActive = isApprovalTask(task) && !task?.state.terminal;
  const isApprovalTaskTerminal = isApprovalTask(task) && task?.state.terminal;
  const showComments = task?.canCommentTask || isApprovalTaskActive;
  const showSurvey = surveyEnabled && !isAgentSide;
  const showCommentsContainer = comments?.length > 0 || showSurvey;
  const showEmptyComments =
    (comments?.length === 0 && !surveyEnabled && !loading) ||
    (comments?.length === 0 && isAgentSide);

  const userLocale = getCurrentLocale(user!);
  let lastDate: Date | null = null;

  return (
    <Stack sx={{ height: '100%', overflow: 'hidden' }}>
      <Stack
        sx={{
          px: 3,
          py: 2,
          borderBottom: theme =>
            `1px solid ${theme.palette.new.border.neutral.divider}`,
          height: '54px',
        }}
      >
        <Typography
          variant="globalM"
          fontWeight="semiBold"
        >
          {t('comments')}
        </Typography>
      </Stack>
      <Stack
        sx={{
          backgroundColor: theme => theme.palette.new.background.layout.default,
          height: 'calc(100% - 54px)',
          width: '100%',
        }}
      >
        {loading && (
          <HuCircularProgress
            centered
            sx={{ mt: 4 }}
          />
        )}
        {!loading && (
          <>
            {showEmptyComments && <EmptyComments />}
            {showCommentsContainer && (
              <Stack
                ref={scrollRef}
                onScroll={handleScroll}
                id="comment-sidebar"
                sx={{
                  position: 'relative',
                  overflowY: 'auto',
                  flexGrow: 1,
                  py: 3,
                  gap: 1.5,
                  width: '100%',
                  scrollbarWidth: 'thin',
                  scrollbarColor: 'rgba(0, 0, 0, 0.2) transparent',
                }}
              >
                {isFetchingNextPage && <HuCircularProgress centered />}
                {comments.map((comment, index) => {
                  const commentDate = new Date(comment.timestamp);
                  const showDateSeparator = lastDate
                    ? !isSameDay(lastDate, commentDate)
                    : true;
                  lastDate = commentDate;

                  const isFirstUnread =
                    index === firstUnreadCommentIndex &&
                    unreadCommentsCount > 0 &&
                    showUnreadMessage;

                  return (
                    <Fragment key={comment.id}>
                      {showDateSeparator && !isFetchingNextPage && (
                        <Divider
                          sx={{
                            px: 3,
                            my: 1,
                            display: 'flex',
                            alignItems: 'center',
                            '&::after, &::before': {
                              borderTop: theme =>
                                `1px solid ${theme.palette.new.border.neutral.divider}`,
                              flex: 1,
                            },
                          }}
                        >
                          <Typography
                            variant="globalXS"
                            sx={{ px: 2 }}
                          >
                            {getDateSeparator(commentDate, t, userLocale)}
                          </Typography>
                        </Divider>
                      )}
                      {isFirstUnread && (
                        <Stack
                          sx={{
                            width: 1,
                            py: '2px',
                            my: 1,
                            backgroundColor: theme =>
                              theme.palette.new.background.elements.overlay,
                          }}
                        >
                          <Typography
                            variant="overline"
                            sx={{
                              textAlign: 'center',
                              px: 2,
                              marginInline: 'auto',
                              color: theme =>
                                theme.palette.new.text.neutral.default,
                            }}
                          >
                            {t('comments_unread_messages', {
                              count: unreadCommentsCount,
                            })}
                          </Typography>
                        </Stack>
                      )}
                      <Comment
                        comment={comment}
                        isTaskTerminal={!!task?.state?.terminal}
                      />
                    </Fragment>
                  );
                })}
                <Stack
                  ref={endOfCommentsRef}
                  sx={{ height: '1px' }}
                />
                {showSurvey && (
                  <CsatSurvey
                    task={task}
                    isLoading={isLoadingSurvey}
                    surveyTemplate={surveyTemplate}
                    hasRespondedSurvey={hasRespondedSurvey}
                    includeDivider={comments?.length > 0}
                  />
                )}
              </Stack>
            )}
            {showComments && (
              <CommentForm
                task={task}
                isAgentWorkspace={isAgentWorkspace}
                onScrollToBottom={scrollToBottom}
                showUnreadCommentsBubble={showUnreadCommentsBubble}
                unreadCommentsCount={unreadCommentsCount}
              />
            )}
          </>
        )}
        {!loading && isApprovalTaskTerminal && (
          <Stack sx={{ p: 2 }}>
            <HuAlert
              severity={getApprovalAlertProps(task.state.name).severity}
              title={t(getApprovalAlertProps(task.state.name).title)}
              description={
                getApprovalStateName(task.state.name) ===
                ApprovalStateName.CANCELLED
                  ? t('approvals.cancelled_request_description')
                  : task.approvalGroupTask?.message || ''
              }
            />
          </Stack>
        )}
      </Stack>
    </Stack>
  );
};

export default Comments;
