import React, {useCallback, useEffect, useRef} from 'react';
import {FlatList, Keyboard, ListRenderItem, View} from 'react-native';
import {useTranslation} from 'react-i18next';
import {StackActions, useNavigation} from '@react-navigation/native';
import {useDispatch} from 'react-redux';
import {QueryFunctionContext, useMutation} from 'react-query';
import {
  CommentSkeleton,
  MarginKeyboardAwareView,
  Spinner,
  Typography,
  RefreshControl,
  AddComment,
  AddCommentRefType,
} from '@components';
import {PublishCommentData} from '@components/AddComment/interfaces';
import {MAX_ATTACHMENTS_SIZE} from '@components/AddComment/constants';
import {Order} from '@config/api/interfaces';
import {useGoBack} from '@hooks/useGoBack';
import {
  Comment,
  CommentableType,
  Comment as CommentType,
} from '@interfaces/comments';
import {UserPermissions} from '@interfaces/user';
import {Attachment} from '@interfaces/attachments';
import {Reaction} from '@interfaces/reaction';
import {getPostCommentInStore} from '@modules/post/utils';
import {openReactionPicker} from '@modules/app/redux';
import {
  createArticleComment,
  getArticleComments,
} from '@modules/article/services';
import useReactToComments from '@modules/article/hooks/useReactToComments';
import {getCommonFormData} from '@modules/post/screens/PublishPost/utils';
import {useGetArticleComments} from '@modules/article/hooks';
import {handleAddCommentEvent} from '@modules/article/utils';
import {ArticleCommentSocket} from '@modules/article/interfaces';
import CommentItem from '@modules/article/components/ArticleComment';
import {useGetComments} from '@modules/post/hooks/useGetComments';
import {ARTICLE_QUERY_KEYS} from '@modules/article/constants';
import {usePermission, useUserId} from '@redux/selectors';
import {showSnackbar} from '@redux/dispatchers';
import {setCommentToEdit} from '@redux/comment/comment.actions';
import {updateComment} from '@services/comment';
import {REDUCED_LIMIT} from '@services/constants';
import {Screens} from '@shared/constants';
import {getCompleteName, isIos, wait} from '@shared/utils';
import {useTheme} from '@shared/theme';

import {styles} from './styles';

interface Props {
  commentId: number;
  articleId: number;
  isReplying?: boolean;
  comesFromPush?: boolean;
}

const keyExtractor = (item: CommentType) => `${item.id}`;
function ArticleCommentDetail({
  articleId,
  commentId,
  isReplying = false,
  comesFromPush = false,
}: Props) {
  const {t} = useTranslation();
  const {theme} = useTheme();
  const navigation = useNavigation();
  const addCommentRef = useRef<AddCommentRefType>(null);
  const flatListRef = useRef<FlatList>(null);
  const userId = useUserId();
  const {goBack} = useGoBack();
  const dispatch = useDispatch();
  const canCommentGeneral = usePermission(UserPermissions.CREATE_COMMENT);
  const canCommentInArticles = usePermission(
    UserPermissions.CREATE_COMMENT_IN_ARTICLES,
  );
  const canComment = canCommentGeneral || canCommentInArticles;
  const {comments: articleComments = []} = useGetArticleComments({
    articleId,
    enabled: false,
  });

  const {
    data: comments = [],
    isLoading: isLoadingComments,
    isFetchingNextPage: isFetchingNextPageComments,
    fetchNextPage,
    refetch: refetchComments,
  } = useGetComments({
    enabled: canComment,
    queryKey: ARTICLE_QUERY_KEYS.commentDetails(commentId),
    queryFn: ({pageParam}: QueryFunctionContext) =>
      getArticleComments({
        parentId: commentId,
        articleId,
        page: pageParam,
        limit: REDUCED_LIMIT,
        order: Order.ASC,
      }),
    getNextPageParam: (lastPage, pages) =>
      lastPage.length === REDUCED_LIMIT ? pages.length + 1 : undefined,
  });
  const postComment = getPostCommentInStore(commentId, articleComments);
  const {toggleCommentReaction} = useReactToComments({
    articleId,
  });

  const onTagPress = useCallback(
    (targetId: number) => {
      navigation.dispatch(
        StackActions.push(Screens.PROFILE, {userId: targetId}),
      );
    },
    [navigation],
  );
  const onReplyPress = useCallback(
    (index: number) => (item: CommentType) => {
      flatListRef.current?.scrollToIndex({index, animated: true});
      index
        ? addCommentRef.current?.initiateTag(
            item.user.id,
            getCompleteName(item.user),
          )
        : addCommentRef.current?.focus();
    },
    [addCommentRef],
  );
  const scrollToBottom = useCallback(async () => {
    await wait(300);
    flatListRef.current?.scrollToEnd({animated: true});
  }, []);
  const createPostCommentMutation = useMutation({
    mutationFn: createArticleComment,
    onMutate: scrollToBottom,
    onSuccess: response =>
      handleAddCommentEvent({
        tags: [],
        data: response as ArticleCommentSocket,
        shouldUpdateArticleList: false,
      }),
  });
  const updateCommentMutation = useMutation({
    mutationFn: updateComment,
    onSettled: () => {
      dispatch(setCommentToEdit(null));
    },
  });
  useEffect(() => {
    if (!postComment && !comesFromPush) {
      goBack();
    }
  }, [goBack, postComment, comesFromPush]);

  const onToggleSelectNewReaction = useCallback(
    ({comment}: {comment: Comment}) =>
      dispatch(
        openReactionPicker({
          commentId: comment.id,
          commentableType: CommentableType.POST,
          postId: articleId,
          parentId: comment?.id === commentId ? undefined : commentId,
          reactions: comment?.reactions || [],
          onReactionSelected: reaction =>
            toggleCommentReaction({
              commentId: comment.id,
              parentId: comment?.id === commentId ? undefined : commentId,
              reaction,
              reactions: comment?.reactions || [],
            }),
        }),
      ),
    [dispatch, articleId, commentId, toggleCommentReaction],
  );
  const onReactionPress = useCallback(
    ({
      reaction,
      commentId: commentIdToReact,
      reactions,
    }: {
      reaction: string;
      commentId: number;
      reactions: Reaction[];
    }) => {
      toggleCommentReaction({
        commentId: commentIdToReact,
        parentId: commentIdToReact === commentId ? undefined : commentId,
        reaction,
        reactions: reactions || postComment?.reactions || [],
      });
    },
    [toggleCommentReaction, commentId, postComment?.reactions],
  );

  const renderComment: ListRenderItem<CommentType> = useCallback(
    ({item, index}: {item: CommentType; index: number}) => (
      <CommentItem
        postId={articleId}
        item={item}
        onTagPress={onTagPress}
        onReactionPress={onReactionPress}
        onToggleSelectNewReaction={onToggleSelectNewReaction}
        onReply={onReplyPress(index + 1)}
        userId={userId}
        isReply
      />
    ),
    [
      articleId,
      onTagPress,
      onReactionPress,
      onToggleSelectNewReaction,
      onReplyPress,
      userId,
    ],
  );
  const handlePublishComment = ({
    attachmentsSize,
    commentToEdit,
    messageBody,
    images,
    videos,
    taggedUsers,
    gif,
  }: PublishCommentData) => {
    Keyboard.dismiss();
    if (attachmentsSize > MAX_ATTACHMENTS_SIZE) {
      showSnackbar({
        title: t('post.max_size_error'),
        variant: 'info',
      });
      return;
    }

    const formData = {
      ...getCommonFormData({
        body: messageBody,
        images: images as Attachment[],
        videos: videos as Attachment[],
        taggedUsers,
        gif,
      }),
      id: articleId,
    };

    return commentToEdit
      ? updateCommentMutation.mutateAsync({
          ...formData,
          commentableId: commentToEdit.id,
          parentId: commentId,
        })
      : createPostCommentMutation.mutateAsync({
          ...formData,
          parentId: commentId,
        });
  };

  if (!postComment) return null;

  return (
    <View
      style={[
        styles.commentDetailContainer,
        {backgroundColor: theme.background.elements.grey},
      ]}>
      <View style={styles.container}>
        <Typography weight="semiBold" color={theme.text.neutral.default}>
          {t('post.responses_to')}{' '}
          <Typography weight="semiBold" color={theme.text.neutral.default}>
            {getCompleteName(postComment.user)}
          </Typography>
        </Typography>
      </View>
      <MarginKeyboardAwareView>
        <FlatList
          keyboardDismissMode={isIos ? 'interactive' : 'on-drag'}
          style={[
            styles.listContainer,
            {backgroundColor: theme.background.elements.default},
          ]}
          data={comments}
          ListHeaderComponent={
            <CommentItem
              postId={articleId}
              item={postComment}
              onTagPress={onTagPress}
              onReactionPress={onReactionPress}
              onToggleSelectNewReaction={onToggleSelectNewReaction}
              onReply={onReplyPress(0)}
              userId={userId}
            />
          }
          keyExtractor={keyExtractor}
          ref={flatListRef}
          showsVerticalScrollIndicator={false}
          refreshControl={
            <RefreshControl
              refreshing={isLoadingComments}
              onRefresh={refetchComments}
            />
          }
          renderItem={renderComment}
          onEndReached={fetchNextPage}
          ListFooterComponent={
            <View style={styles.footerContainer}>
              {createPostCommentMutation.isLoading && (
                <View style={styles.skeletonContainer}>
                  <CommentSkeleton />
                </View>
              )}
              {isFetchingNextPageComments && <Spinner />}
            </View>
          }
        />
      </MarginKeyboardAwareView>
      {canComment && (
        <AddComment
          keyProp="comment-screen-add-comment"
          autoFocus={isReplying}
          onPublishComment={handlePublishComment}
          ref={addCommentRef}
        />
      )}
    </View>
  );
}

export default ArticleCommentDetail;
