/* eslint-disable react/no-unstable-nested-components */
import React, {memo, useMemo, useState} from 'react';
import {TextStyle, StyleProp, Pressable} from 'react-native';
import {useTranslation} from 'react-i18next';
import {useMutation, useQueryClient} from 'react-query';
import {useNavigation} from '@react-navigation/native';
import {
  IconChartLine,
  IconClockEdit,
  IconDots,
  IconEdit,
  IconMessage,
  IconMessageX,
  IconPinned,
  IconPinnedOff,
  IconSpeakerphone,
  IconTrash,
} from '@tabler/icons-react-native';
import {Dialog, List, ListRow, PostState, Typography} from '@components';
import Text from '@components/Text';
import {UserPermissions} from '@interfaces/user';
import {
  CursorPaginatedPosts,
  OptionListItem,
  Post,
} from '@modules/post/interfaces';
import {
  pinGroupPost,
  unpinGroupPost,
  deleteGroupPost,
  changeGroupPostCommentsEnabled,
} from '@modules/group/services';
import {useMarketplaceStore} from '@modules/marketplace/stores/useMarketplaceStore';
import {postQueryKeys} from '@modules/post/constants';
import {
  changeCommentsEnabled,
  deletePost,
  pinPost,
  unpinPost,
} from '@modules/post/services';
import {
  deleteCommentWithStoreUpdate,
  pinPostInStore,
  removeFeedPost,
  unpinPostInStore,
} from '@modules/post/utils';
import {useGroupFeedStore} from '@modules/group/store/useGroupFeedStore';
import {usePermission} from '@redux/selectors';
import {showSnackbar} from '@redux/dispatchers';
import {Screens} from '@shared/constants';
import {useTheme} from '@shared/theme';
import {wait} from '@shared/utils';
import {useCommunityFeature} from '@stores/communityFeatures';

import {MAX_PINNED_POSTS_LENGTH} from './constants';
import {styles} from './styles';

interface Props {
  isComment?: boolean;
  isOwner?: boolean;
  isPinned?: boolean;
  post: Post;
  canManagePosts?: boolean;
  navigateToEditScreen: () => void;
  groupId?: number;
  goBackOnDelete?: boolean;
  text?: string;
  textStyle?: StyleProp<TextStyle>;
  isMarketplace?: boolean;
  articleId?: number;
  parentId?: number;
  postId?: number;
  canEdit?: boolean;
  onOptionsPress?: () => void;
}

const keyExtractor = (item: OptionListItem) => item.text;

function PostOptions({
  isComment = false,
  isOwner,
  isPinned,
  post,
  canEdit = true,
  canManagePosts,
  navigateToEditScreen,
  groupId,
  goBackOnDelete,
  text,
  textStyle,
  isMarketplace,
  parentId,
  postId,
  onOptionsPress,
}: Props) {
  const [isVisible, setIsVisible] = useState(false);
  const [isDeleteVisible, setIsDeleteVisible] = useState(false);
  const queryClient = useQueryClient();
  const navigation = useNavigation();
  const {t} = useTranslation();
  const {theme, iconSizes} = useTheme();
  const {
    getPinnedPostsLength,
    pinPost: pinGroupPostInStore,
    unpinPost: unpinGroupPostInStore,
  } = useGroupFeedStore();

  const groupPinnedPostsLength = getPinnedPostsLength(groupId);

  const pinnedPostsLength =
    queryClient.getQueryData<CursorPaginatedPosts>(postQueryKeys.pinnedPosts)
      ?.items?.length || 0;
  const canViewStats = useCommunityFeature('VIEW_POST_STATS');

  const canCreateKeyUpdates = usePermission(UserPermissions.CREATE_KEY_UPDATES);
  const canViewKeyUpdates = usePermission(UserPermissions.VIEW_KEY_UPDATES);
  const onChangeGroupPostCommentsEnabled = useMutation(
    changeGroupPostCommentsEnabled,
  );
  const cantPinPost =
    (groupId ? groupPinnedPostsLength : pinnedPostsLength) >=
    MAX_PINNED_POSTS_LENGTH;
  const deleteGroupPostMutation = useMutation(deleteGroupPost, {
    onSuccess: () => {
      showSnackbar({
        title: t('group.post_deleted'),
      });
    },
  });

  const openDeletePostDialog = () => {
    // wait for the prev dialog to be closed
    setTimeout(() => {
      setIsDeleteVisible(true);
    }, 600);
  };

  const items: OptionListItem[] = useMemo(
    () =>
      [
        {
          logoComponent: () =>
            post?.commentsEnabled ? (
              <IconMessageX size={iconSizes.x6} color={theme.neutralText} />
            ) : (
              <IconMessage size={iconSizes.x6} color={theme.neutralText} />
            ),
          text: t(
            post?.commentsEnabled
              ? 'post.disable_comments'
              : 'post.activate_comments',
          ),
          onPress: () => {
            if (groupId) {
              onChangeGroupPostCommentsEnabled.mutate({
                groupId,
                postId: post.id,
                commentsEnabled: !post?.commentsEnabled,
              });
            } else {
              changeCommentsEnabled({
                id: post.id,
                commentsEnabled: !post?.commentsEnabled,
              });
            }
            showSnackbar({
              title: t(
                post?.commentsEnabled
                  ? 'post.deactivated_post_comments'
                  : 'post.activated_post_comments',
              ),
              variant: 'success',
            });
          },
          hide: isComment || isMarketplace || !isOwner,
        },
        {
          logoComponent: () => (
            <IconSpeakerphone size={iconSizes.x6} color={theme.neutralText} />
          ),
          text: t('key_updates.set_as_key_update'),
          onPress: () => {
            navigation.navigate(Screens.PUBLISH_KEY_UPDATE, post);
          },
          hide:
            isMarketplace ||
            isComment ||
            !canManagePosts ||
            !canCreateKeyUpdates ||
            post?.hasBeenSegmented ||
            post?.isKeyUpdate ||
            !canViewKeyUpdates,
        },
        {
          logoComponent: () =>
            isPinned ? (
              <IconPinnedOff size={iconSizes.x6} color={theme.neutralText} />
            ) : (
              <IconPinned size={iconSizes.x6} color={theme.neutralText} />
            ),
          text: t(isPinned ? 'post.unpin' : 'post.pin'),
          onPress: async () => {
            if (groupId) {
              const commonParams = {groupId, postId: post.id};
              if (isPinned) {
                unpinGroupPostInStore(groupId, post.id);
                await unpinGroupPost(commonParams).catch(() => {
                  pinGroupPostInStore(groupId, post.id);
                });
              } else {
                pinGroupPostInStore(groupId, post.id);
                await pinGroupPost(commonParams).catch(() => {
                  unpinGroupPostInStore(groupId, post.id);
                });
              }
            } else {
              // Optimistically pinning/unpinning post
              if (isPinned) {
                unpinPostInStore(post.id);
                await unpinPost(post.id).catch(() => pinPostInStore(post));
              } else {
                pinPostInStore(post);
                await pinPost(post.id).catch(() => unpinPostInStore(post.id));
              }
            }

            showSnackbar({
              title: t(isPinned ? 'post.unpinned_post' : 'post.pinned_post'),
              variant: 'success',
            });
          },
          hide:
            isMarketplace ||
            isComment ||
            !canManagePosts ||
            (!isPinned && cantPinPost),
        },
        {
          logoComponent: () => (
            <IconChartLine size={iconSizes.x6} color={theme.neutralText} />
          ),
          text: t('post.analysis'),
          onPress: () => {
            navigation.navigate(Screens.INSIGHTS, {id: post.id, groupId});
          },
          hide: isMarketplace || isComment || !canViewStats,
        },
        {
          logoComponent: () => (
            <IconClockEdit size={iconSizes.x6} color={theme.neutralText} />
          ),
          text: t('screens.EditEndTime'),
          onPress: () => {
            navigation.navigate(Screens.EDIT_END_TIME, {
              endsAt: post.poll?.pollConfiguration.endsAt ?? null,
              postId: post.id,
              pollId: post.poll!.id,
              groupId,
            });
          },
          hide: !isOwner || !canEdit || !post.poll,
        },
        {
          logoComponent: () => (
            <IconEdit size={iconSizes.x6} color={theme.neutralText} />
          ),
          text: t('post.edit'),
          onPress: navigateToEditScreen,
          hide: !isOwner || !canEdit || !!post.poll?.totalAnswerCount,
        },
        {
          logoComponent: () => (
            <IconTrash size={iconSizes.x6} color={theme.neutralText} />
          ),
          color: theme.errorText,
          text: t('post.delete'),
          onPress: openDeletePostDialog,
          hide: false,
        },
      ].filter(item => !item.hide),
    [
      t,
      post,
      isComment,
      isMarketplace,
      isOwner,
      canManagePosts,
      canCreateKeyUpdates,
      canViewKeyUpdates,
      isPinned,
      cantPinPost,
      canViewStats,
      canEdit,
      navigateToEditScreen,
      theme.errorText,
      theme.neutralText,
      iconSizes.x6,
      groupId,
      onChangeGroupPostCommentsEnabled,
      navigation,
      unpinGroupPostInStore,
      pinGroupPostInStore,
    ],
  );

  const closeDeletePostDialog = () => {
    setIsDeleteVisible(false);
  };

  const handleDelete = async () => {
    if (post) {
      if (isComment) {
        await deleteCommentWithStoreUpdate({
          commentId: post.id!,
          postId,
          parentId: parentId ?? ('parentId' in post ? post.parentId : undefined),
          groupId,
          isMarketplace,
        });
        if (goBackOnDelete) {
          navigation.goBack();
        }
        showSnackbar({
          title: t('post.comment_deleted_success'),
          variant: 'success',
        });
      } else {
        if (goBackOnDelete) {
          navigation.goBack();
          // This 'wait' is to unmount completely PostDetail component before deleting the post and avoid a bug.
          // If not unmounted, then a PostDetail useEffect will be called, which calls GET /post/:id endpoint.
          // As the id is the id of a deleted post, then backend will return an error code and an error message will be shown.
          await wait(100);
        }
        if (isMarketplace) {
          const deleted = await useMarketplaceStore
            .getState()
            .deletePost(post.id!);
          if (deleted) {
            showSnackbar({
              title: t('marketplace.post_deleted'),
            });
          } else {
            showSnackbar({
              title: t('general.error'),
              variant: 'error',
            });
          }
        } else if (groupId) {
          deleteGroupPostMutation.mutate({
            groupId: groupId,
            postId: post.id!,
            isScheduled: (post as Post).state === PostState.Scheduled,
          });
        } else {
          deletePost({
            id: post.id!,
            isScheduled: (post as Post).state === PostState.Scheduled,
          });
          if (post.id) removeFeedPost(post.id);
          showSnackbar({
            title: t('post.post_deleted_success'),
            variant: 'success',
          });
        }
      }
    }
    setIsDeleteVisible(false);
  };

  const handlePress = () => {
    setIsVisible(true);
  };

  const onHide = () => {
    setIsVisible(false);
  };

  const onItemPress = (item: OptionListItem) => () => {
    setIsVisible(false);
    item.onPress();
  };

  const renderItem = ({item}: {item: OptionListItem}) => {
    return (
      <ListRow onPress={onItemPress(item)}>
        <ListRow.Avatar iconColor={item.color} Icon={item.logoComponent} />
        <Typography
          align="left"
          weight="semiBold"
          color={item.color}
          numberOfLines={1}>
          {item.text}
        </Typography>
      </ListRow>
    );
  };

  return (
    <>
      <Pressable
        onPress={onOptionsPress || handlePress}
        testID="post-options-button"
        {...(text ? {hitSlop: 16} : {style: styles.threeDots})}>
        {text ? (
          <Text style={textStyle}>{text}</Text>
        ) : (
          <IconDots size={iconSizes.x6} color={theme.neutralText} />
        )}
      </Pressable>
      <Dialog
        title={t('post.more_options')}
        onClose={onHide}
        isVisible={isVisible}>
        <List
          scrollEnabled={false}
          data={items}
          keyExtractor={keyExtractor}
          renderItem={renderItem}
          style={styles.list}
        />
      </Dialog>
      <Dialog
        title={t('general.confirmation')}
        onClose={closeDeletePostDialog}
        isVisible={isDeleteVisible}
        footer={{
          primaryButton: {
            text: t('post.delete'),
            onPress: handleDelete,
          },
          secondaryButton: {
            text: t('general.cancel'),
            onPress: closeDeletePostDialog,
          },
        }}>
        <Typography>{t('post.confirm_delete_post')}</Typography>
      </Dialog>
    </>
  );
}

export default memo(PostOptions);
