import React, {useCallback, useEffect, useMemo, useRef, useState} from 'react';
import {View, ViewToken, ListRenderItem} from 'react-native';
import {useTranslation} from 'react-i18next';
import {TFunction} from 'i18next';
import {useMutation, useQueryClient} from '@tanstack/react-query';
import {FlatList} from 'react-native-gesture-handler';
import {useHeaderHeight} from '@react-navigation/elements';
import Animated, {
  useAnimatedStyle,
  useSharedValue,
} from 'react-native-reanimated';
import {IconPlus, IconSend} from '@tabler/icons-react-native';
import {
  KeyboardAvoidingView,
  KeyboardController,
} from 'react-native-keyboard-controller';
import {
  Typography,
  Button,
  IconButton,
  InputClassic,
  Pill,
  Dialog,
  Alert,
} from '@components';
// TODO: migrate to custom List component
import {FlatListLayout} from '@components/InfinityScrollFlatList/components/FlatListLayout';
import {EventsSocketsType} from '@config/constants';
import {SocketConnection} from '@config/socket/socket';
import {ApiErrors, HumandAPIError} from '@config/api';
import {useSafeAreaBottomPadding} from '@hooks/useSafeAreaBottomPadding';
import {useModalHandler} from '@hooks/useModalHandler';
import {ServiceManagementErrorMessage} from '@modules/serviceManagement/components/ServiceManagementErrorMessage';
import {
  markCommentAsRead,
  sendTaskComment,
} from '@modules/serviceManagement/services';
import {
  NEW_MESSAGE_ALERT_DELAY,
  serviceManagementQueryKeys,
} from '@modules/serviceManagement/constants';
import {
  AmplitudeServiceManagementRole,
  ApprovalGroupStatus,
  ApprovalGroupStatusDictionary,
  AttachmentUploadStatus,
  CurrentServiceManagementComment,
  FormattedServiceManagementComment,
  ServiceManagementComment,
  ServiceManagementCommentInfiniteQueryData,
  ServiceManagementTask,
  ServiceManagementTaskType,
} from '@modules/serviceManagement/interfaces';
import MultipleFileAttachmentsInput from '@modules/serviceManagement/components/MultipleFileAttachmentsInput';
import {useMultipleAttachments} from '@modules/serviceManagement/hooks/useMultipleAttachments';
import {useGetCsatSurvey} from '@modules/serviceManagement/hooks/useGetCsatSurvey';
import {useGetComments} from '@modules/serviceManagement/hooks/useGetComments';
import {useAreBubblesEnabled} from '@modules/serviceManagement/hooks/useAreBubblesEnabled';
import {useVisitTask} from '@modules/serviceManagement/hooks/useVisitTask';
import {
  getTaskId,
  invalidateBubbleQueries,
} from '@modules/serviceManagement/utils';
import {useAppSelector} from '@redux/utils';
import {showSnackbar} from '@redux/dispatchers';
import {commonStyles} from '@shared/styles';
import {
  capitalize,
  getHumanizedDate,
  logAmplitudeEvent,
  openSettings,
} from '@shared/utils';
import {SPACING, useTheme} from '@shared/theme';
import {AMPLITUDE_EVENTS} from '@shared/constants';

import ServiceManagementTaskComment from './components/ServiceManagementTaskComment';
import {styles} from './styles';
import CustomerSatisfaction from './components/CustomerSatisfaction';
import AttachmentsPreview from './components/AttachmentsPreview';
import MessageOptions from './components/MessageOptions';
import {ServiceManagementFABButton} from '../ServiceManagementFABButton';

const KEYBOARD_VERTICAL_OFFSET = 103;
const VISIBLE_PERCENT_THRESHOLD = 0;
const VIEWABILITY_CONFIG = {
  itemVisiblePercentThreshold: VISIBLE_PERCENT_THRESHOLD,
};
const socket = new SocketConnection();

interface Props {
  task: ServiceManagementTask;
  isAgent: boolean;
  isActive?: boolean;
}

const getApprovalGroupAlertConfig = (
  task: ServiceManagementTask,
  t: TFunction,
): {
  variant: 'success' | 'error' | 'info';
  title: string;
  description: string;
} | null => {
  const status = ApprovalGroupStatusDictionary[task.state.name];

  if (
    task.type !== ServiceManagementTaskType.Approval ||
    !task.state.terminal ||
    !status
  ) {
    return null;
  }

  switch (status) {
    case ApprovalGroupStatus.Pending:
      return null;
    case ApprovalGroupStatus.Approved:
      return {
        variant: 'success',
        title: t('service_management.approvals.approved_request'),
        description: task.approvalGroupTask.message || '',
      };
    case ApprovalGroupStatus.Rejected:
      return {
        variant: 'error',
        title: t('service_management.approvals.rejected_request'),
        description: task.approvalGroupTask.message || '',
      };
    case ApprovalGroupStatus.Cancelled:
      return {
        variant: 'info',
        title: t('service_management.approvals.cancelled_request'),
        description: t(
          'service_management.approvals.cancelled_request_description',
        ),
      };
    default:
      return null;
  }
};

function ServiceManagementTaskCommentsTab({
  task,
  isAgent,
  isActive = false,
}: Props) {
  const taskId = getTaskId(task);
  const {t} = useTranslation();
  const headerHeight = useHeaderHeight();
  const addSharedValue = useSharedValue(0);
  const {theme} = useTheme();
  const queryClient = useQueryClient();
  const currentUserId = useAppSelector(({user}) => user.id);
  const paddingBottom = useSafeAreaBottomPadding({bottom: 0}) - SPACING.x1;
  const commentEnabled =
    task.type === ServiceManagementTaskType.Incident
      ? !!task.canCommentTask
      : !task.state.terminal;
  const [currentComment, setCurrentComment] =
    useState<Nullable<CurrentServiceManagementComment>>(null);
  const [message, setMessage] = useState('');
  const [isLastUnreadMessageVisible, setIsLastUnreadMessageVisible] =
    useState(true);
  const readCommentIdsRef = useRef<Set<string>>(new Set());
  const isBubblesEnabled = useAreBubblesEnabled();
  const {visit, visitOnce} = useVisitTask();

  const {mutate: markCommentAsReadMutation, isError: isMarkCommentAsReadError} =
    useMutation({
      mutationFn: markCommentAsRead,
      onSuccess: (_, {commentId}) => {
        queryClient.setQueryData<ServiceManagementCommentInfiniteQueryData>(
          queryKey,
          oldData => {
            if (!oldData) return oldData;

            return {
              ...oldData,
              pages: oldData.pages.map(page => ({
                ...page,
                items: page.items.map(comment =>
                  comment.id === commentId ? {...comment, read: true} : comment,
                ),
              })),
            };
          },
        );
      },
      onError: (error: HumandAPIError) => {
        error.code !== ApiErrors.RESOURCE_NOT_FOUND &&
          showSnackbar({
            title: t('errors.error'),
            variant: 'error',
          });
      },
    });

  const markCommentRead = useCallback(
    (commentId: string) => {
      if (isBubblesEnabled) {
        visit(
          taskId,
          {lastReadCommentId: commentId},
          {onSuccess: () => markCommentAsReadMutation({taskId, commentId})},
        );
      } else {
        markCommentAsReadMutation({taskId, commentId});
      }
    },
    [isBubblesEnabled, visit, taskId, markCommentAsReadMutation],
  );

  const onSuccessMessages = useCallback(
    (data: ServiceManagementComment[]) => {
      const lastMessage = data[0];
      if (
        lastMessage &&
        !lastMessage.read &&
        isLastUnreadMessageVisible &&
        !isMarkCommentAsReadError
      ) {
        markCommentRead(lastMessage.id);
      }
    },
    [isLastUnreadMessageVisible, isMarkCommentAsReadError, markCommentRead],
  );
  const {
    data: messages,
    formatedData,
    isError,
    isLoading,
    isFetchingNextPage,
    getNextPage,
    refresh,
  } = useGetComments({
    onSuccess: onSuccessMessages,
    taskId,
  });
  const {data: csatData, showSurvey} = useGetCsatSurvey({isAgent, task});
  const flatlistRef = useRef<Nullable<FlatList>>(null);
  const {
    isVisible: isNewMessageButtonVisible,
    onCloseModal: onCloseNewMessageButton,
    onOpenModal: onOpenNewMessageButton,
  } = useModalHandler();
  const queryKey = useMemo(
    () => serviceManagementQueryKeys.taskComments(taskId),
    [taskId],
  );
  const unreadMessages = useMemo(
    () => (messages ? messages.filter(item => !item.read) : []),
    [messages],
  );
  const newMessagesCount = useMemo(
    () => unreadMessages.length,
    [unreadMessages],
  );

  const onViewableItemsChanged = useRef(
    ({
      viewableItems,
    }: {
      viewableItems: ViewToken<FormattedServiceManagementComment>[];
    }) => {
      if (!viewableItems.length) {
        return;
      }

      const {hasLastUnreadVisible} = viewableItems.reduce(
        (acc, {item}) => {
          const alreadyMarked = readCommentIdsRef.current.has(item.id);

          if (!item.read && !alreadyMarked) {
            readCommentIdsRef.current.add(item.id);

            markCommentRead(item.id);
            if (
              !acc.hasLastUnreadVisible &&
              item.id === unreadMessages?.[0]?.id
            ) {
              acc.hasLastUnreadVisible = true;
            }
          }

          return acc;
        },
        {hasLastUnreadVisible: false},
      );

      setIsLastUnreadMessageVisible(hasLastUnreadVisible);
    },
  );

  const itemRefs = useRef<Record<string, View>>(Object.create(null));

  useEffect(() => {
    if (!isBubblesEnabled || !isActive || isLoading) {
      return;
    }
    if (unreadMessages.length === 0) {
      visitOnce(taskId, {commentsViewed: true});
    }
  }, [
    isBubblesEnabled,
    isActive,
    isLoading,
    unreadMessages.length,
    visitOnce,
    taskId,
  ]);

  useEffect(() => {
    if (newMessagesCount > 0 && !isLastUnreadMessageVisible) {
      onOpenNewMessageButton();
    } else {
      const timeout = setTimeout(() => {
        onCloseNewMessageButton();
      }, NEW_MESSAGE_ALERT_DELAY);

      return () => clearTimeout(timeout);
    }
  }, [
    newMessagesCount,
    isLastUnreadMessageVisible,
    onOpenNewMessageButton,
    onCloseNewMessageButton,
  ]);

  const {
    isVisible: showAttachmentsModal,
    onCloseModal: onCloseAttachmentsModal,
    onOpenModal: onOpenAttachmentsModal,
  } = useModalHandler();

  const handleNewComment = useCallback(() => {
    queryClient.invalidateQueries({queryKey});
    invalidateBubbleQueries(queryClient);
    if (!isLastUnreadMessageVisible) {
      onOpenNewMessageButton();
    }
  }, [
    isLastUnreadMessageVisible,
    onOpenNewMessageButton,
    queryClient,
    queryKey,
  ]);

  useEffect(() => {
    readCommentIdsRef.current.clear();
    socket.listenEvent(
      EventsSocketsType.SERVICE_MANAGER_TASK_NEW_COMMENT,
      handleNewComment,
    );
    return () => {
      socket.unListenEvent(EventsSocketsType.SERVICE_MANAGER_TASK_NEW_COMMENT);
      queryClient.invalidateQueries({queryKey});
    };
  }, [handleNewComment, queryClient, queryKey]);

  const handlePressAddIcon = useCallback(() => {
    KeyboardController.dismiss();
    onOpenAttachmentsModal();
  }, [onOpenAttachmentsModal]);

  const {
    attachments,
    handleDeleteAttachment,
    onSelectDocument,
    onSelectPhotos,
    onTakePhoto,
    uploadStatuses,
    isPending: isAttachmentsPending,
    uploadDisabled,
    handleClearAttachments,
    isAttachmentsPermissionBlocked,
  } = useMultipleAttachments({onFinishSelection: onCloseAttachmentsModal});

  const onCloseDeleteCommentModal = useCallback(
    () => setCurrentComment(null),
    [],
  );

  const {mutate: sendComment, isPending: isSendingComment} = useMutation({
    mutationFn: sendTaskComment,
    onSuccess: () => {
      logAmplitudeEvent(AMPLITUDE_EVENTS.SERVICE_MGMT_COMMENT_SENT, {
        role: isAgent
          ? AmplitudeServiceManagementRole.AGENT
          : task.type === ServiceManagementTaskType.Approval
          ? AmplitudeServiceManagementRole.APPROVER
          : AmplitudeServiceManagementRole.INITIATOR,
        serviceId: task?.catalogItem?.id,
        serviceName: task?.catalogItem?.name,
        requestId: task?.id,
      });
      queryClient.invalidateQueries({
        queryKey: serviceManagementQueryKeys.taskComments(taskId),
      });
      invalidateBubbleQueries(queryClient);
    },
  });

  const disableSendButton = useMemo(
    () =>
      Object.values(uploadStatuses || {}).some(
        item => item !== AttachmentUploadStatus.Success,
      ) ||
      (!message && (!attachments || attachments.length === 0)) ||
      isSendingComment,
    [attachments, isSendingComment, message, uploadStatuses],
  );

  const sendMessage = useCallback(() => {
    sendComment({message, taskId, attachments: attachments});
    handleClearAttachments();
    setMessage('');
  }, [attachments, handleClearAttachments, message, sendComment, taskId]);

  const onLongPressMessage = (comment: FormattedServiceManagementComment) => {
    const ref = itemRefs.current[comment.id];
    // eslint-disable-next-line max-params
    ref?.measureInWindow((x, y, _, height) => {
      setCurrentComment({...comment, x, y, height});
    });
  };

  const renderItem: ListRenderItem<FormattedServiceManagementComment> =
    useCallback(
      ({item, index}) => {
        const showUnreadMessagesCount =
          isNewMessageButtonVisible &&
          newMessagesCount - 1 === index &&
          newMessagesCount > 0 &&
          !isMarkCommentAsReadError;

        return (
          <View>
            {showUnreadMessagesCount ? (
              <View style={styles.newMessage}>
                <Pill
                  text={t('service_management.comments_unread_messages', {
                    count: newMessagesCount,
                  })}
                  variant="info"
                />
              </View>
            ) : null}
            {item.showDate && (
              <View style={styles.date}>
                <Pill
                  text={
                    capitalize(getHumanizedDate(item.timestamp)) ?? undefined
                  }
                  variant="neutral"
                />
              </View>
            )}
            <View
              ref={ref => {
                if (ref) itemRefs.current[item.id] = ref;
              }}
              collapsable={false}>
              <ServiceManagementTaskComment
                key={item.id}
                comment={item}
                isCurrentUserComment={item.user?.id === currentUserId}
                onLongPress={() => onLongPressMessage(item)}
              />
            </View>
          </View>
        );
      },

      [
        currentUserId,
        isMarkCommentAsReadError,
        isNewMessageButtonVisible,
        newMessagesCount,
        t,
      ],
    );

  const onPressNewAlertButton = () => {
    onCloseNewMessageButton();
    flatlistRef.current?.scrollToOffset({offset: 0});
  };

  const onPressCollapse = () => {
    flatlistRef.current?.scrollToOffset({offset: 0});
  };

  const addAnimatedStyle = useAnimatedStyle(() => {
    return {
      transform: [{rotate: `${addSharedValue.value}deg`}],
    };
  });

  const alertConfig = useMemo(
    () => getApprovalGroupAlertConfig(task, t),
    [task, t],
  );

  return (
    <View
      style={[
        commonStyles.flex,
        !commentEnabled && {backgroundColor: theme.background.layout.tertiary},
        {paddingBottom},
      ]}>
      <ServiceManagementFABButton
        isVisible={isNewMessageButtonVisible && newMessagesCount > 0}
        text={t('service_management.comments_new_messages', {
          count: newMessagesCount,
        })}
        onPress={onPressNewAlertButton}
        extraPadding={SPACING.x4}
      />
      {currentComment && (
        <MessageOptions
          comment={currentComment}
          onClose={onCloseDeleteCommentModal}
          taskId={taskId}
          isTerminal={!!task.state.terminal}
        />
      )}
      <KeyboardAvoidingView
        behavior="padding"
        keyboardVerticalOffset={KEYBOARD_VERTICAL_OFFSET + headerHeight}
        style={commonStyles.flex}>
        <FlatListLayout
          data={formatedData}
          ref={flatlistRef}
          inverted
          onEndReached={getNextPage}
          isLoading={isLoading}
          isFetchingNextPage={isFetchingNextPage}
          onRefresh={refresh}
          isError={isError}
          extraData={currentUserId + newMessagesCount}
          renderItem={renderItem}
          contentContainerStyle={styles.contentContainerStyle}
          containerStyle={{backgroundColor: theme.background.layout.default}}
          onViewableItemsChanged={onViewableItemsChanged.current}
          viewabilityConfig={VIEWABILITY_CONFIG}
          ListEmptyComponent={
            !showSurvey ? (
              <ServiceManagementErrorMessage
                title={t('service_management.empty_comments')}
                body={t('service_management.empty_comments_body')}
              />
            ) : (
              <View />
            )
          }
          ListErrorComponent={
            <ServiceManagementErrorMessage
              title={t('service_management.tasks_error_title')}
              body={t('service_management.tasks_error_body')}
            />
          }
          ListHeaderComponent={
            csatData && showSurvey ? (
              <CustomerSatisfaction
                task={task}
                survey={csatData}
                onPressCollapse={onPressCollapse}
              />
            ) : null
          }
          ListHeaderComponentStyle={styles.csat}
        />
        {commentEnabled && (
          <View style={styles.footerContainer}>
            <View
              style={[
                styles.container,
                {backgroundColor: theme.background.layout.tertiary},
              ]}>
              <Animated.View style={addAnimatedStyle}>
                <IconButton
                  Icon={IconPlus}
                  variant="flat"
                  onPress={handlePressAddIcon}
                />
              </Animated.View>
              <View style={styles.inputContainer}>
                <InputClassic
                  value={message}
                  onChangeText={setMessage}
                  scrollEnabled
                  returnKeyType="default"
                  multiline
                  autoComplete="off"
                  style={styles.input}
                  withClearButton={false}
                  placeholder={t('chat.messages.write_message')}
                />
              </View>
              <IconButton
                Icon={IconSend}
                variant="secondary"
                onPress={sendMessage}
                disabled={disableSendButton}
              />
            </View>
            <AttachmentsPreview
              attachments={attachments}
              onPressDelete={handleDeleteAttachment}
              uploadStatuses={uploadStatuses}
            />
          </View>
        )}
        {showAttachmentsModal && (
          <Dialog
            isVisible={showAttachmentsModal && isActive}
            onClose={onCloseAttachmentsModal}
            withCloseButton={false}
            {...(isAttachmentsPermissionBlocked && {
              title: t('general.missing_permissions_title'),
            })}>
            {isAttachmentsPermissionBlocked ? (
              <View style={styles.permissionBlockedContainer}>
                <Typography align="center" color={theme.text.feedback.info}>
                  {t('post.no_camera_storage_permissions_error')}
                </Typography>
                <Button text={t('general.settings')} onPress={openSettings} />
                <Button
                  text={t('general.cancel')}
                  variant="flat"
                  onPress={onCloseAttachmentsModal}
                  style={styles.closeModalButton}
                />
              </View>
            ) : uploadDisabled ? (
              <Typography color={theme.text.feedback.error}>
                {t('general.max_size_error')}
              </Typography>
            ) : (
              <MultipleFileAttachmentsInput
                onPressDocument={onSelectDocument}
                onPressCamera={onTakePhoto}
                onPressImage={onSelectPhotos}
                isLoading={isAttachmentsPending}
              />
            )}
          </Dialog>
        )}
      </KeyboardAvoidingView>
      {alertConfig ? (
        <View style={styles.alertContainer}>
          <Alert
            variant={alertConfig.variant}
            withClose={false}
            title={alertConfig.title}
            description={alertConfig.description}
          />
        </View>
      ) : null}
    </View>
  );
}

export default ServiceManagementTaskCommentsTab;
