import React, {useCallback, useMemo, useRef, useState} from 'react';
import {InteractionManager} from 'react-native';
import {useTranslation} from 'react-i18next';
import {useDispatch} from 'react-redux';
import {
  IconCopy,
  IconEdit,
  IconInfoCircle,
  IconPinned,
  IconPinnedOff,
  IconShare3,
  IconTrash,
} from '@tabler/icons-react-native';
import {File} from 'expo-file-system';
import * as Haptics from 'expo-haptics';
import {useNavigation} from '@react-navigation/native';
import {Dialog, DialogProps, Typography} from '@components';
import {getEstimatedMessageSize, getMessageMargins} from '@modules/chats/utils';
import {
  cleanupAndCloseMessagesSearch,
  usePinnedMessagesForChannel,
  useMessageReactionsCount,
  useShallowConversation,
  useUserReactions,
} from '@modules/chats/store';
import {useMessageActions} from '@modules/chats/hooks';
import {openReactionPicker} from '@modules/app/redux/actions';
import {
  MAX_PINNED_MESSAGES,
  MAX_REACTIONS_FOR_MESSAGE,
  PIN_DURATION_OPTIONS,
  PinDuration,
} from '@modules/chats/constants';
import {useMessageActionsContext} from '@modules/chats/contexts';
import {MessageStatus, PictureAsset} from '@modules/chats/interfaces';
import {
  FileDownloadStatus,
  getAttachmentData,
} from '@modules/chats/store/zustand/useAttachments';
import {DefaultMessage} from '@modules/chats/components';
import {useIsChatUserDeactivated} from '@modules/chats/store/zustand/useGroups';
import {showSnackbar} from '@redux/dispatchers';
import {SPACING} from '@shared/theme';
import {copyToClipboard} from '@shared/utils';
import {Screens} from '@shared/constants';
import {useFeatureFlag} from '@stores/featureFlags';

import {ContextReactionsOption} from './components/ContextReactions';
import {ContextMenuOption} from './components/ContextMenu';
import {ContextModal} from './components/ContextModal';
import {DialogPinDurationContent} from './components/DialogPinDurationContent';
import {MessageContextMenu} from './components/MessageContextMenu';

interface Props {
  channel: string;
}

export function MessageActions({channel}: Props) {
  const {t} = useTranslation();
  const dispatch = useDispatch();
  const conversation = useShallowConversation(channel, state => ({
    is_channel: !!state.is_channel,
    is_im: state.is_im,
  }));

  const isUserDeactivated = useIsChatUserDeactivated(channel);

  const [isDiscardEditionVisible, setIsDiscardEditionVisible] = useState(false);
  const [isDeleteConfirmationVisible, setIsDeleteConfirmationVisible] =
    useState(false);
  const [isPinDurationVisible, setIsPinDurationVisible] = useState(false);
  const [isReplacePinVisible, setIsReplacePinVisible] = useState(false);
  const {
    targetMessage,
    isActive,
    isActionMenuVisible,
    isReactMenuVisible,
    menuTop,
    closeMenus,
    closeAll,
    reset,
    startReplying,
    startEditing,
    startDeleting,
    isEditing,
    isDeleting,
    startForwarding,
    isPinFailedVisible,
    togglePinFailed,
  } = useMessageActionsContext();
  const {deleteMessage, toggleReaction, togglePin} = useMessageActions({
    channel,
  });
  const {pinned} = usePinnedMessagesForChannel(channel);
  const {navigate} = useNavigation();
  const clientMsgId = targetMessage?.data.client_msg_id;
  const messageTs = targetMessage?.data.ts;
  const messageStatus = targetMessage?.data.status;
  const isSentByCurrentUser = targetMessage?.isSentByCurrentUser;
  const reactionsCount = useMessageReactionsCount(clientMsgId ?? '');

  const canReact =
    isActive &&
    isReactMenuVisible &&
    (messageStatus === 'SENT' || !messageStatus) &&
    reactionsCount < MAX_REACTIONS_FOR_MESSAGE &&
    !isUserDeactivated;
  const userReactions = useUserReactions(clientMsgId);
  const isPinMessageEnabled = useFeatureFlag('CHATS_V2_PIN_MESSAGE');
  const itemHeight = useMemo(() => {
    if (!targetMessage) {
      return 0;
    }
    const height = getEstimatedMessageSize(targetMessage, -1);
    const marginBottom = getMessageMargins(targetMessage).marginBottom;
    return height - marginBottom;
  }, [targetMessage]);

  const onReactionPress = useCallback(
    (name: string) => () => {
      if (clientMsgId) {
        toggleReaction({
          name,
          clientMsgId,
        });
      }

      reset();
    },
    [reset, toggleReaction, clientMsgId],
  );

  const onMoreReactionsPress = useCallback(() => {
    reset();

    if (clientMsgId) {
      dispatch(
        openReactionPicker({
          channel,
          clientMsgId,
          reactions: [],
        }),
      );
    }
  }, [reset, clientMsgId, dispatch, channel]);

  const closeDiscardEdition = useCallback(() => {
    setIsDiscardEditionVisible(false);
  }, []);

  const closeDeleteConfirmation = useCallback(() => {
    setIsDeleteConfirmationVisible(false);
    reset();
  }, [reset]);

  const onConfirmDelete = useCallback(() => {
    if (clientMsgId) {
      deleteMessage({clientMsgId});
    }
    reset();
  }, [clientMsgId, deleteMessage, reset]);

  const closeReplacePin = useCallback(() => {
    setIsReplacePinVisible(false);
    reset();
  }, [reset]);

  const replacePinTsRef = useRef<string | undefined>(undefined);

  const onConfirmReplacePin = useCallback(() => {
    replacePinTsRef.current = pinned.length > 0 ? pinned[0].ts : undefined;
    setIsReplacePinVisible(false);
    setIsPinDurationVisible(true);
  }, [pinned]);

  const closePinDuration = useCallback(() => {
    replacePinTsRef.current = undefined;
    setIsPinDurationVisible(false);
    reset();
  }, [reset]);

  const onPinDurationPress = useCallback(
    (duration: PinDuration) => {
      if (clientMsgId) {
        togglePin({
          clientMsgId,
          duration,
          tsToReplace: replacePinTsRef.current,
        });
      }
      replacePinTsRef.current = undefined;
      setIsPinDurationVisible(false);
      reset();
    },
    [clientMsgId, togglePin, reset],
  );

  const pinDurationOptions = useMemo(
    () =>
      PIN_DURATION_OPTIONS.map(option => ({
        label: t(`chat.pin.dialog.options.${option}`),
        value: option,
      })),
    [t],
  );

  const quickReactions = useMemo((): ContextReactionsOption[] => {
    if (!canReact) {
      return [];
    }

    return [
      {
        name: 'thumbsup',
        emoji: '👍',
        isReacted: userReactions.includes('👍'),
        onPress: onReactionPress('👍'),
      },
      {
        name: 'heart',
        emoji: '❤️',
        isReacted: userReactions.includes('❤️'),
        onPress: onReactionPress('❤️'),
      },
      {
        name: 'smile',
        emoji: '😄',
        isReacted: userReactions.includes('😄'),
        onPress: onReactionPress('😄'),
      },
      {
        name: 'open_mouth',
        emoji: '😮',
        isReacted: userReactions.includes('😮'),
        onPress: onReactionPress('😮'),
      },
      {
        name: 'laughing',
        emoji: '😂',
        isReacted: userReactions.includes('😂'),
        onPress: onReactionPress('😂'),
      },
      {
        name: 'cry',
        emoji: '😢',
        isReacted: userReactions.includes('😢'),
        onPress: onReactionPress('😢'),
      },
      {name: 'more', emoji: '➕', onPress: onMoreReactionsPress},
    ];
  }, [canReact, onReactionPress, userReactions, onMoreReactionsPress]);

  const options: ContextMenuOption[] = useMemo(() => {
    if (!isActionMenuVisible || !isActive) {
      return [];
    }

    const {attachments, text, formatted_text, status} =
      targetMessage?.data || {};

    const isReplyable =
      (!status || status === MessageStatus.Sent) && !isUserDeactivated;

    const isEditable =
      isSentByCurrentUser &&
      !!text &&
      (!status || status === MessageStatus.Sent) &&
      !targetMessage?.data.forwarded &&
      !isUserDeactivated;

    const isDeletable = isSentByCurrentUser;

    const isCopyable =
      (attachments?.length === 1 &&
        attachments[0].metadata.format === 'image') ||
      (!!text && !attachments?.length);

    const isForwardableOrPinneable =
      !targetMessage?.data.status ||
      targetMessage?.data.status === MessageStatus.Sent;

    // Group chats only
    const isMessageInfoViewable =
      conversation?.is_channel && isSentByCurrentUser;

    const isPinned = messageTs ? pinned.some(m => m.ts === messageTs) : false;
    return [
      isReplyable && {
        label: t('chat.messages.reply'),
        Icon: IconShare3,
        iconContainerStyle: {
          transform: [{scaleX: -1}],
        },
        onPress: () => {
          cleanupAndCloseMessagesSearch();
          startReplying();
          closeAll();
        },
      },
      isForwardableOrPinneable && {
        label: t('chats.forward'),
        Icon: IconShare3,
        onPress: () => {
          cleanupAndCloseMessagesSearch();
          closeAll();
          startForwarding();
        },
      },
      isEditable && {
        label: t('general.edit'),
        Icon: IconEdit,
        onPress: () => {
          cleanupAndCloseMessagesSearch();
          startEditing();
          closeAll();
        },
      },
      isCopyable && {
        label: t('general.copy'),
        Icon: IconCopy,
        onPress: async () => {
          try {
            if (
              attachments?.length === 1 &&
              attachments[0].metadata.format === 'image'
            ) {
              const image = attachments[0] as PictureAsset;
              const assetId = image.id;
              if (clientMsgId) {
                const attachmentData = getAttachmentData(clientMsgId, assetId);

                if (attachmentData?.state === FileDownloadStatus.DONE) {
                  const file = new File(
                    attachmentData.uri || attachmentData.remoteUrl,
                  );
                  const base64Data = await file.base64();
                  copyToClipboard({image: base64Data});
                }
              }
            } else {
              copyToClipboard({text: formatted_text || text});
            }
            Haptics.notificationAsync(Haptics.NotificationFeedbackType.Success);
          } catch (error) {
            showSnackbar({
              title: t('general.error'),
              variant: 'error',
            });
            Haptics.notificationAsync(Haptics.NotificationFeedbackType.Error);
          }

          reset();
        },
      },
      isMessageInfoViewable && {
        label: t('chat.messages.info'),
        Icon: IconInfoCircle,
        onPress: () => {
          closeAll();
          if (targetMessage) {
            requestAnimationFrame(() => {
              InteractionManager.runAfterInteractions(() => {
                navigate(Screens.CHAT_MESSAGE_INFO, {
                  message: targetMessage,
                });
              });
            });
          }
        },
      },
      isForwardableOrPinneable &&
        isPinMessageEnabled && {
          label: t(isPinned ? 'chat.pin.unpin' : 'chat.pin.pin'),
          Icon: isPinned ? IconPinnedOff : IconPinned,
          onPress: () => {
            if (isPinned) {
              if (clientMsgId) {
                togglePin({clientMsgId});
              }
              reset();
            } else {
              closeMenus();
              if (pinned.length >= MAX_PINNED_MESSAGES) {
                setIsReplacePinVisible(true);
              } else {
                setIsPinDurationVisible(true);
              }
            }
          },
        },
      isDeletable && {
        label: t('general.delete'),
        Icon: IconTrash,
        onPress: () => {
          cleanupAndCloseMessagesSearch();
          startDeleting();
          closeMenus();
          setIsDeleteConfirmationVisible(true);
        },
        isDanger: true,
      },
    ].filter(Boolean) as ContextMenuOption[];
  }, [
    isActionMenuVisible,
    isActive,
    targetMessage,
    isSentByCurrentUser,
    conversation?.is_channel,
    messageTs,
    pinned,
    t,
    isPinMessageEnabled,
    startReplying,
    closeAll,
    startForwarding,
    startEditing,
    reset,
    clientMsgId,
    navigate,
    startDeleting,
    closeMenus,
    togglePin,
    isUserDeactivated,
  ]);

  const dialogProps = useMemo((): DialogProps => {
    if (isEditing) {
      return {
        title: t('general.discard_changes'),
        isVisible: isDiscardEditionVisible,
        onClose: closeDiscardEdition,
        footer: {
          primaryButton: {
            text: t('general.discard'),
            onPress: reset,
          },
          secondaryButton: {
            text: t('general.cancel'),
            onPress: closeDiscardEdition,
          },
        },
      };
    }
    if (isDeleting) {
      return {
        title: t('chat.delete.confirmation'),
        titleNumberOfLines: 2,
        isVisible: isDeleteConfirmationVisible,
        onClose: closeDeleteConfirmation,
        footer: {
          primaryButton: {
            text: t('general.delete'),
            onPress: onConfirmDelete,
            IconRight: IconTrash,
          },
          secondaryButton: {
            text: t('general.cancel'),
            onPress: closeDeleteConfirmation,
          },
        },
      };
    }
    if (isReplacePinVisible) {
      return {
        title: t('chat.pin.replace_maximum_title'),
        titleNumberOfLines: 2,
        isVisible: true,
        onClose: closeReplacePin,
        footer: {
          primaryButton: {
            text: t('general.continue'),
            onPress: onConfirmReplacePin,
          },
          secondaryButton: {
            text: t('general.cancel'),
            onPress: closeReplacePin,
          },
        },
        children: (
          <Typography>{t('chat.pin.replace_maximum_subtitle')}</Typography>
        ),
      };
    }
    if (isPinDurationVisible) {
      return {
        title: t('chat.pin.dialog.title'),
        titleNumberOfLines: 2,
        isVisible: true,
        onClose: closePinDuration,
        children: (
          <DialogPinDurationContent
            options={pinDurationOptions}
            onDurationPress={onPinDurationPress}
          />
        ),
      };
    }
    if (isPinFailedVisible) {
      return {
        title: t('chat.pin.pin_failed'),
        isVisible: true,
        onClose: togglePinFailed,
        children: <Typography>{t('chat.pin.pin_failed_subtitle')}</Typography>,
        footer: {
          primaryButton: {
            text: t('general.okay'),
            onPress: togglePinFailed,
          },
        },
      };
    }
    return {
      isVisible: false,
    };
  }, [
    reset,
    closeDeleteConfirmation,
    closeDiscardEdition,
    closePinDuration,
    togglePinFailed,
    closeReplacePin,
    isDeleteConfirmationVisible,
    isDeleting,
    isDiscardEditionVisible,
    isEditing,
    isPinDurationVisible,
    isPinFailedVisible,
    isReplacePinVisible,
    onConfirmDelete,
    onConfirmReplacePin,
    onPinDurationPress,
    pinDurationOptions,
    t,
  ]);

  return (
    <>
      <ContextModal
        isVisible={isActive}
        onClose={isEditing ? closeDiscardEdition : reset}>
        {isActive && targetMessage && (
          <MessageContextMenu
            canReact={canReact}
            reactions={quickReactions}
            options={options}
            pageY={menuTop}
            xPosition={
              isSentByCurrentUser
                ? {right: -4}
                : {
                    left: conversation?.is_im
                      ? SPACING.x2
                      : SPACING.x2 + SPACING.x1 + SPACING.x4,
                  }
            }
            itemHeight={itemHeight}>
            {!isDeleting && !isEditing && (
              <DefaultMessage
                item={targetMessage}
                onPress={reset}
                pointerEvents="box-only"
              />
            )}
          </MessageContextMenu>
        )}
      </ContextModal>
      <Dialog {...dialogProps} />
    </>
  );
}
