import React, {useCallback, useEffect, useRef, useState} from 'react';
import {FlatList, Keyboard, View} from 'react-native';
import {useTranslation} from 'react-i18next';
import {useDispatch} from 'react-redux';
import {useMutation} from '@tanstack/react-query';
import {useNavigation} from '@react-navigation/native';
import {
  AddComment,
  AddCommentRefType,
  CommentSkeleton,
  MarginKeyboardAwareView,
  Spinner,
  Typography,
  RefreshControl,
} from '@components';
import {PublishCommentData} from '@components/AddComment/interfaces';
import {MAX_ATTACHMENTS_SIZE} from '@components/AddComment/constants';
import {Comment as CommentType} from '@interfaces/comments';
import {UserPermissions} from '@interfaces/user';
import {Attachment} from '@interfaces/attachments';
import {Navigation} from '@interfaces/navigation';
import {getCommonFormData as getPublishFormData} from '@modules/post/screens/PublishPost/utils';
import {styles} from '@modules/post/screens/CommentDetailRefactored/styles';
import {createEventPostComment} from '@modules/events/services';
import {useEventPostsStore} from '@modules/events/store/useEventPostsStore';
import {buildEventPostCommentBody} from '@modules/events/utils';
import EventCommentItem from '@modules/events/components/EventCommentItem';
import {
  useEventCommentsActions,
  useEventCommentById,
  useEventPostComments,
} from '@modules/events/store/useEventCommentsStore';
import {usePermission, useUserId} from '@redux/selectors';
import {showSnackbar} from '@redux/dispatchers';
import {setCommentToEdit} from '@redux/comment/comment.actions';
import {updateComment} from '@services/comment';
import {Screens} from '@shared/constants';
import {getCompleteName, isIos, wait} from '@shared/utils';
import {useTheme} from '@shared/theme';

interface Props {
  eventId: number;
  postId: number;
  commentId: number;
  isReplying?: boolean;
}

const keyExtractor = (item: number) => `${item}`;

function EventCommentDetailScreen({
  eventId,
  postId,
  commentId,
  isReplying = false,
}: Props) {
  const navigation =
    useNavigation<Navigation<Screens.EVENT_COMMENT_DETAIL>['navigation']>();
  const dispatch = useDispatch();
  const {t} = useTranslation();
  const {theme} = useTheme();
  const userId = useUserId();
  const [showAddComment, setShowAddComment] = useState(false);
  const addCommentRef = useRef<AddCommentRefType>(null);
  const flatListRef = useRef<FlatList>(null);

  const canCommentGeneral = usePermission(UserPermissions.CREATE_COMMENT);
  const canCommentInPosts = usePermission(
    UserPermissions.CREATE_COMMENT_IN_POSTS,
  );
  const canComment = canCommentGeneral || canCommentInPosts;

  const parentComment = useEventCommentById({postId, commentId});
  const repliesData = useEventPostComments({postId, parentId: commentId});
  const commentIds = repliesData.commentIds ?? [];
  const isLoadingComments = repliesData.isLoading;
  const isFetchingNextPageComments = repliesData.isFetchingNextPage;

  const eventCommentsActions = useEventCommentsActions();
  const {refetch: refetchComments, getNextPage} = eventCommentsActions;

  useEffect(() => {
    if (!parentComment) {
      if (navigation.canGoBack()) {
        navigation.goBack();
      } else {
        navigation.replace(Screens.TABS);
      }
    }
  }, [navigation, parentComment]);

  useEffect(() => {
    if (canComment && postId && commentId) {
      refetchComments({eventId, postId, parentId: commentId});
    }
  }, [canComment, eventId, postId, commentId, refetchComments]);

  // Defer showing AddComment to avoid layout jump and focus issues when opening the screen
  useEffect(() => {
    const timeout = setTimeout(() => setShowAddComment(true), 300);
    return () => clearTimeout(timeout);
  }, []);

  const onRefetch = useCallback(() => {
    refetchComments({eventId, postId, parentId: commentId});
  }, [eventId, postId, commentId, refetchComments]);

  const onGetNextPage = useCallback(() => {
    getNextPage({eventId, postId, parentId: commentId});
  }, [eventId, postId, commentId, getNextPage]);

  const onReplyPress = useCallback(() => {
    addCommentRef.current?.focus();
  }, []);

  const onCommentReplyPress = useCallback(
    (index: number) => (item: CommentType) => {
      flatListRef.current?.scrollToIndex({index, animated: true});
      addCommentRef.current?.initiateTag(
        item.user.id,
        getCompleteName(item.user),
      );
    },
    [],
  );

  const scrollToBottom = useCallback(async () => {
    // Brief delay so the new comment is rendered before scrolling into view
    await wait(300);
    flatListRef.current?.scrollToEnd({animated: true});
  }, []);

  const updatePostLastComment = useEventPostsStore(
    state => state.updatePostLastComment,
  );
  const updatePost = useEventPostsStore(state => state.updatePost);

  const createPostCommentMutation = useMutation({
    mutationFn: createEventPostComment,
    onMutate: scrollToBottom,
    onSuccess: response => {
      eventCommentsActions.addComment({
        postId,
        comment: response,
        parentId: commentId,
      });
      updatePostLastComment({
        eventId,
        postId,
        comment: response,
        parentId: commentId,
      });
    },
  });

  const updateCommentMutation = useMutation({
    mutationFn: updateComment,
    onSuccess: response => {
      eventCommentsActions.updateComment({
        postId,
        commentId: response.id,
        parentId: response.parentId ?? undefined,
        updater: () => response,
      });
      // Keep feed's lastComment in sync when the edited comment is the one shown there
      if (response.parentId == null) {
        updatePost(eventId, postId, post => {
          const first = post.lastComments?.[0];
          if (first?.id !== response.id) return post;
          return {
            ...post,
            lastComments: [{...response, children: first.children ?? []}],
          };
        });
      }
    },
    onSettled: () => {
      dispatch(setCommentToEdit(null));
    },
  });

  const handlePublishComment = useCallback(
    ({
      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 = getPublishFormData({
        body: messageBody,
        images: images as Attachment[],
        videos: videos as Attachment[],
        taggedUsers,
        gif,
      });

      if (commentToEdit) {
        const commentableId = commentToEdit.id;
        return updateCommentMutation.mutateAsync({
          ...formData,
          id: commentableId,
          commentableId,
          parentId: commentId,
        });
      }
      const body = buildEventPostCommentBody(formData, commentId);
      return createPostCommentMutation.mutateAsync({
        eventId,
        postId,
        body,
      });
    },
    [
      t,
      eventId,
      postId,
      commentId,
      createPostCommentMutation,
      updateCommentMutation,
    ],
  );

  const renderComment = useCallback(
    ({item, index}: {item: number; index: number}) => (
      <EventCommentItem
        eventId={eventId}
        postId={postId}
        parentId={commentId}
        commentId={item}
        onReplyPress={onCommentReplyPress(index)}
        userId={userId}
      />
    ),
    [eventId, postId, commentId, onCommentReplyPress, userId],
  );

  if (!parentComment) 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(parentComment.user)}
          </Typography>
        </Typography>
      </View>
      <MarginKeyboardAwareView>
        <FlatList
          keyboardDismissMode={isIos ? 'interactive' : 'on-drag'}
          style={[
            styles.listContainer,
            {backgroundColor: theme.background.elements.default},
          ]}
          data={commentIds}
          ListHeaderComponent={
            <EventCommentItem
              eventId={eventId}
              postId={postId}
              commentId={commentId}
              onReplyPress={onReplyPress}
              userId={userId}
              isReply={false}
            />
          }
          keyExtractor={keyExtractor}
          ref={flatListRef}
          showsVerticalScrollIndicator={false}
          refreshControl={
            <RefreshControl
              refreshing={isLoadingComments}
              onRefresh={onRefetch}
            />
          }
          renderItem={renderComment}
          onEndReached={onGetNextPage}
          ListFooterComponent={
            <View style={styles.footerContainer}>
              {createPostCommentMutation.isPending && (
                <View style={styles.skeletonContainer}>
                  <CommentSkeleton />
                </View>
              )}
              {isFetchingNextPageComments && <Spinner />}
            </View>
          }
        />
      </MarginKeyboardAwareView>
      {canComment && showAddComment && (
        <AddComment
          keyProp="event-comment-detail-add-comment"
          autoFocus={isReplying}
          onPublishComment={handlePublishComment}
          ref={addCommentRef}
        />
      )}
    </View>
  );
}

function EventCommentDetail({
  route: {params},
}: Navigation<Screens.EVENT_COMMENT_DETAIL>) {
  const {eventId, postId, parentCommentId, isReplying} = params;
  return (
    <EventCommentDetailScreen
      eventId={eventId}
      postId={postId}
      commentId={parentCommentId}
      isReplying={isReplying}
    />
  );
}

export default EventCommentDetail;
