import {
  memo, useEffect, useMemo, useRef,
} from '../../../lib/teact/teact';
import { getActions, getGlobal, withGlobal } from '../../../global';

import type {
  ApiChat, ApiInputMessageReplyInfo, ApiInputSuggestedPostInfo, ApiMessage, ApiPeer,
} from '../../../api/types';
import type { MessageListType, ThemeKey, ThreadId } from '../../../types/index';

import { canEditMediaInEditor, isChatChannel, stripCustomEmoji } from '../../../global/helpers';
import {
  selectCanAnimateInterface,
  selectChat,
  selectChatMessage,
  selectEditingMessage,
  selectForwardedSender,
  selectIsChatWithSelf,
  selectIsCurrentUserPremium,
  selectSender,
  selectTabState,
  selectTheme,
} from '../../../global/selectors';
import { selectIsMediaNsfw } from '../../../global/selectors/media';
import { selectDraft, selectEditingId, selectEditingScheduledId } from '../../../global/selectors/threads';
import buildClassName from '../../../util/buildClassName';
import captureEscKeyListener from '../../../util/captureEscKeyListener';
import { unique } from '../../../util/iteratees';

import useAppLayout from '../../../hooks/useAppLayout';
import useContextMenuHandlers from '../../../hooks/useContextMenuHandlers';
import useCurrentOrPrev from '../../../hooks/useCurrentOrPrev';
import useFrozenProps from '../../../hooks/useFrozenProps';
import useLang from '../../../hooks/useLang';
import useLastCallback from '../../../hooks/useLastCallback';
import useOldLang from '../../../hooks/useOldLang';
import usePeerColor from '../../../hooks/usePeerColor';
import useShowTransitionDeprecated from '../../../hooks/useShowTransitionDeprecated';

import EmbeddedMessage from '../../common/embedded/EmbeddedMessage';
import Icon from '../../common/icons/Icon';
import Button from '../../ui/Button';
import Menu from '../../ui/Menu';
import MenuItem from '../../ui/MenuItem';
import MenuSeparator from '../../ui/MenuSeparator';

import './ComposerEmbeddedMessage.scss';

type StateProps = {
  replyInfo?: ApiInputMessageReplyInfo;
  suggestedPostInfo?: ApiInputSuggestedPostInfo;
  editingId?: number;
  message?: ApiMessage;
  sender?: ApiPeer;
  shouldAnimate?: boolean;
  forwardedMessagesCount?: number;
  noAuthors?: boolean;
  noCaptions?: boolean;
  forwardsHaveCaptions?: boolean;
  isCurrentUserPremium?: boolean;
  isContextMenuDisabled?: boolean;
  isReplyToDiscussion?: boolean;
  isInChangingRecipientMode?: boolean;
  shouldPreventComposerAnimation?: boolean;
  senderChat?: ApiChat;
  isSenderChannel?: boolean;
  currentUserId?: string;
  forwardMessageIds?: number[];
  fromChatId?: string;
  isMediaNsfw?: boolean;
  theme: ThemeKey;
};

type OwnProps = {
  shouldForceShowEditing?: boolean;
  chatId: string;
  threadId: ThreadId;
  messageListType: MessageListType;
  onClear?: NoneToVoidFunction;
};

const CLOSE_DURATION = 350;

const ComposerEmbeddedMessage = (props: OwnProps & StateProps) => {
  const {
    shouldAnimate,
    isReplyToDiscussion,
    isInChangingRecipientMode,
    forwardMessageIds,
    fromChatId,
    replyInfo,
    editingId,
    suggestedPostInfo,
    shouldForceShowEditing,
    message,
    forwardedMessagesCount,
  } = props;

  const {
    resetDraftReplyInfo,
    resetDraftSuggestedPostInfo,
    updateDraftReplyInfo,
    setEditingId,
    focusMessage,
    changeRecipient,
    openChatOrTopicWithReplyInDraft,
    setForwardNoAuthors,
    setForwardNoCaptions,
    exitForwardMode,
    setShouldPreventComposerAnimation,
    openSuggestMessageModal,
    requestMessageMediaEditor,
  } = getActions();
  const ref = useRef<HTMLDivElement>();
  const oldLang = useOldLang();
  const lang = useLang();
  const { isMobile } = useAppLayout();

  const isReplyToTopicStart = message?.content.action?.type === 'topicCreate';
  const isShowingSuggestedPost = Boolean(suggestedPostInfo) && !shouldForceShowEditing;
  const isForwarding = Boolean(forwardedMessagesCount);

  const selectSenderFromForwardedMessage = useLastCallback((forwardedMessage: ApiMessage) => {
    const global = getGlobal();
    let localSender = selectForwardedSender(global, forwardedMessage);
    if (!localSender) {
      localSender = selectSender(global, forwardedMessage);
    }
    return localSender;
  });

  const forwardSenders = useMemo(() => {
    if (!isForwarding) return undefined;
    const forwardedMessages = forwardMessageIds?.map((id) => selectChatMessage(getGlobal(), fromChatId!, id))
      .filter(Boolean);
    const senders = forwardedMessages?.map((m) => selectSenderFromForwardedMessage(m)).filter(Boolean);
    return senders ? unique(senders) : undefined;
  }, [isForwarding, forwardMessageIds, fromChatId]);

  const isShown = (() => {
    if (isInChangingRecipientMode) return false;
    if (message && (replyInfo || editingId)) return true;
    if (forwardSenders && isForwarding) return true;
    if (isShowingSuggestedPost) return true;
    return false;
  })();

  const {
    shouldRender, transitionClassNames, isClosing,
  } = useShowTransitionDeprecated(
    isShown && !isReplyToTopicStart && !isReplyToDiscussion,
    undefined,
    !shouldAnimate,
    undefined,
    !shouldAnimate,
    CLOSE_DURATION,
    !shouldAnimate,
  );

  const {
    chatId,
    currentUserId,
    theme,
    onClear,
    isCurrentUserPremium,
    isContextMenuDisabled,
    shouldPreventComposerAnimation,
    sender,
    senderChat,
    isMediaNsfw,
    noAuthors,
    noCaptions,
    forwardsHaveCaptions,
    forwardedMessagesCount: frozenForwardedMessagesCount,
    message: frozenMessage,
    shouldForceShowEditing: frozenShouldForceShowEditing,
    suggestedPostInfo: frozenSuggestedPostInfo,
    replyInfo: frozenReplyInfo,
    editingId: frozenEditingId,
    isSenderChannel,
  } = useFrozenProps(props, isClosing);

  const isForwardingRendering = Boolean(frozenForwardedMessagesCount);
  const isShowingReplyRendering = Boolean(frozenReplyInfo) && !frozenShouldForceShowEditing;
  const isReplyWithQuoteRendering = Boolean(frozenReplyInfo?.quoteText);
  const isShowingSuggestedPostRendering = Boolean(frozenSuggestedPostInfo) && !frozenShouldForceShowEditing;

  const canMediaBeEdited = frozenMessage && canEditMediaInEditor(frozenMessage) && !isMobile;

  useEffect(() => {
    if (shouldPreventComposerAnimation) {
      setShouldPreventComposerAnimation({ shouldPreventComposerAnimation: false });
    }
  });

  const clearEmbedded = useLastCallback(() => {
    if (frozenEditingId) {
      setEditingId({ messageId: undefined });
    } else if (frozenForwardedMessagesCount) {
      exitForwardMode();
    } else if (isShowingSuggestedPostRendering) {
      resetDraftSuggestedPostInfo();
      resetDraftReplyInfo();
    } else if (frozenReplyInfo && !frozenShouldForceShowEditing) {
      resetDraftReplyInfo();
    }
    onClear?.();
  });

  useEffect(() => (isShown ? captureEscKeyListener(clearEmbedded) : undefined), [isShown, clearEmbedded]);

  const {
    isContextMenuOpen, contextMenuAnchor, handleContextMenu,
    handleContextMenuClose, handleContextMenuHide,
  } = useContextMenuHandlers(ref);

  const focusMessageFromDraft = () => {
    focusMessage({ chatId: frozenMessage!.chatId, messageId: frozenMessage!.id, noForumTopicPanel: true });
  };
  const handleMessageClick = useLastCallback((e: React.MouseEvent): void => {
    if (frozenSuggestedPostInfo) {
      openSuggestMessageModal({ chatId });
      return;
    }
    handleContextMenu(e);
  });

  const handlePictogramClick = useLastCallback((e: React.MouseEvent): void => {
    e.stopPropagation();
    if ((frozenEditingId || frozenReplyInfo?.type === 'message') && canMediaBeEdited) {
      requestMessageMediaEditor();
      return;
    }
  });

  const handleClearClick = useLastCallback((e: React.MouseEvent<HTMLButtonElement, MouseEvent>): void => {
    e.stopPropagation();
    clearEmbedded();
    handleContextMenuHide();
  });
  const buildAutoCloseMenuItemHandler = (action: NoneToVoidFunction) => {
    return () => {
      handleContextMenuClose();
      action();
    };
  };
  const handleForwardToAnotherChatClick = useLastCallback(buildAutoCloseMenuItemHandler(changeRecipient));
  const handleShowMessageClick = useLastCallback(buildAutoCloseMenuItemHandler(focusMessageFromDraft));
  const handleRemoveQuoteClick = useLastCallback(buildAutoCloseMenuItemHandler(
    () => updateDraftReplyInfo({ quoteText: undefined }),
  ));
  const handleChangeReplyRecipientClick = useLastCallback(buildAutoCloseMenuItemHandler(changeRecipient));
  const handleReplyInSenderChat = useLastCallback(() => {
    handleContextMenuClose();
    if (!sender) return;
    openChatOrTopicWithReplyInDraft({ chatId: sender.id });
  });
  const handleDoNotReplyClick = useLastCallback(buildAutoCloseMenuItemHandler(clearEmbedded));

  const handleIconKeyDown = useLastCallback((e: React.KeyboardEvent<HTMLDivElement>) => {
    if (e.key === 'Enter' || e.key === ' ') {
      e.preventDefault();
      handleContextMenu(e as unknown as React.MouseEvent);
    }
  });

  const getTriggerElement = useLastCallback(() => ref.current);
  const getRootElement = useLastCallback(() => ref.current!);
  const getMenuElement = useLastCallback(() => ref.current!.querySelector('.forward-context-menu .bubble'));

  useEffect(() => {
    if (!shouldRender) {
      handleContextMenuClose();
      handleContextMenuHide();
    }
  }, [handleContextMenuClose, handleContextMenuHide, shouldRender]);

  const className = buildClassName('ComposerEmbeddedMessage', transitionClassNames);

  const { className: peerColorClass, style: peerColorStyle } = usePeerColor({
    peer: sender,
    theme,
  });
  const innerClassName = buildClassName('ComposerEmbeddedMessage_inner', peerColorClass);

  const leftIcon = useMemo(() => {
    if (frozenEditingId) {
      return 'edit';
    }
    if (isShowingSuggestedPostRendering) {
      return 'cash-circle';
    }
    if (isForwardingRendering) {
      return 'forward';
    }
    if (isShowingReplyRendering) {
      return 'reply';
    }

    return undefined;
  }, [frozenEditingId, isForwardingRendering, isShowingReplyRendering, isShowingSuggestedPostRendering]);

  const customText = frozenForwardedMessagesCount && frozenForwardedMessagesCount > 1
    ? oldLang('ForwardedMessageCount', frozenForwardedMessagesCount)
    : undefined;

  const strippedMessage = useMemo(() => {
    if (!frozenMessage || !isForwardingRendering || !frozenMessage.content.text
      || !noAuthors || isCurrentUserPremium) return frozenMessage;

    const strippedText = stripCustomEmoji(frozenMessage.content.text);
    return {
      ...frozenMessage,
      content: {
        ...frozenMessage.content,
        text: strippedText,
      },
    };
  }, [isCurrentUserPremium, isForwardingRendering, frozenMessage, noAuthors]);

  const renderingLeftIcon = useCurrentOrPrev(leftIcon, true);

  if (!shouldRender) {
    return undefined;
  }

  const canReplyInSenderChat = sender && !isSenderChannel
    && chatId !== sender.id && sender.id !== currentUserId;

  return (
    <div className={className} ref={ref} onContextMenu={handleContextMenu}>
      <div className={innerClassName} style={peerColorStyle}>
        <div
          className="embedded-left-icon"
          role="button"
          tabIndex={0}
          onClick={handleContextMenu}
          onKeyDown={handleIconKeyDown}
        >
          {renderingLeftIcon && <Icon name={renderingLeftIcon} />}
          {Boolean(frozenReplyInfo?.quoteText) && (
            <Icon name="quote" className="quote-reply" />
          )}
        </div>
        <EmbeddedMessage
          isOpen={isShown}
          className="inside-input"
          replyInfo={frozenReplyInfo}
          suggestedPostInfo={frozenSuggestedPostInfo}
          isMediaNsfw={isMediaNsfw}
          isInComposer
          message={strippedMessage}
          sender={!noAuthors ? sender : undefined}
          composerForwardSenders={forwardSenders}
          customText={customText}
          noCaptions={noCaptions}
          title={(frozenEditingId && !isShowingReplyRendering) ? oldLang('EditMessage')
            : noAuthors ? oldLang('HiddenSendersNameDescription') : undefined}
          onClick={handleMessageClick}
          onPictogramClick={canMediaBeEdited ? handlePictogramClick : undefined}
          pictogramActionIcon={canMediaBeEdited ? 'edit' : undefined}
          senderChat={senderChat}
        />
        <Button
          className="embedded-cancel"
          round
          faded
          color="translucent"
          ariaLabel={oldLang('Cancel')}
          onClick={handleClearClick}
          iconName="close"
        />
        {(isShowingReplyRendering || isForwardingRendering) && !isContextMenuDisabled && (
          <Menu
            isOpen={isContextMenuOpen}
            anchor={contextMenuAnchor}
            getTriggerElement={getTriggerElement}
            getRootElement={getRootElement}
            getMenuElement={getMenuElement}
            className="forward-context-menu"
            onClose={handleContextMenuClose}
            onCloseAnimationEnd={handleContextMenuHide}
          >
            {isForwardingRendering && (
              <>
                <MenuItem
                  icon={!noAuthors ? 'message-succeeded' : undefined}
                  customIcon={noAuthors ? <Icon name="placeholder" /> : undefined}

                  onClick={() => setForwardNoAuthors({
                    noAuthors: false,
                  })}
                >
                  {oldLang(frozenForwardedMessagesCount > 1 ? 'ShowSenderNames' : 'ShowSendersName')}
                </MenuItem>
                <MenuItem
                  icon={noAuthors ? 'message-succeeded' : undefined}
                  customIcon={!noAuthors ? <Icon name="placeholder" /> : undefined}

                  onClick={() => setForwardNoAuthors({
                    noAuthors: true,
                  })}
                >
                  {oldLang(frozenForwardedMessagesCount > 1 ? 'HideSenderNames' : 'HideSendersName')}
                </MenuItem>
                {forwardsHaveCaptions && (
                  <>
                    <MenuSeparator />
                    <MenuItem
                      icon={!noCaptions ? 'message-succeeded' : undefined}
                      customIcon={noCaptions ? <Icon name="placeholder" /> : undefined}

                      onClick={() => setForwardNoCaptions({
                        noCaptions: false,
                      })}
                    >
                      {oldLang(frozenForwardedMessagesCount > 1
                        ? 'Conversation.ForwardOptions.ShowCaption' : 'ShowCaption')}
                    </MenuItem>
                    <MenuItem
                      icon={noCaptions ? 'message-succeeded' : undefined}
                      customIcon={!noCaptions ? <Icon name="placeholder" /> : undefined}

                      onClick={() => setForwardNoCaptions({
                        noCaptions: true,
                      })}
                    >
                      {oldLang(frozenForwardedMessagesCount > 1
                        ? 'Conversation.ForwardOptions.HideCaption' : 'HideCaption')}
                    </MenuItem>
                  </>
                )}
                <MenuSeparator />
                <MenuItem icon="replace" onClick={handleForwardToAnotherChatClick}>
                  {oldLang('ForwardAnotherChat')}
                </MenuItem>
              </>
            )}
            {isShowingReplyRendering && (
              <>
                <MenuItem
                  icon="show-message"
                  onClick={handleShowMessageClick}
                >
                  {oldLang('Message.Context.Goto')}
                </MenuItem>
                {isReplyWithQuoteRendering && (
                  <MenuItem
                    icon="remove-quote"
                    onClick={handleRemoveQuoteClick}
                  >
                    {oldLang('RemoveQuote')}
                  </MenuItem>
                )}
                {canReplyInSenderChat && (
                  <MenuItem icon="user" onClick={handleReplyInSenderChat}>
                    {lang('ReplyInPrivateMessage')}
                  </MenuItem>
                )}
                <MenuItem icon="replace" onClick={handleChangeReplyRecipientClick}>
                  {oldLang('ReplyToAnotherChat')}
                </MenuItem>
                <MenuItem icon="delete" onClick={handleDoNotReplyClick}>
                  {oldLang('DoNotReply')}
                </MenuItem>
              </>
            )}
          </Menu>
        )}
      </div>
    </div>
  );
};

export default memo(withGlobal<OwnProps>(
  (global, {
    shouldForceShowEditing, chatId, threadId, messageListType,
  }): Complete<StateProps> => {
    const {
      forwardMessages: {
        fromChatId, toChatId, messageIds: forwardMessageIds, noAuthors, noCaptions,
      },
      isShareMessageModalShown: isModalShown,
      shouldPreventComposerAnimation,
    } = selectTabState(global);

    const editingId = messageListType === 'scheduled'
      ? selectEditingScheduledId(global, chatId)
      : selectEditingId(global, chatId, threadId);
    const shouldAnimate = selectCanAnimateInterface(global) && !shouldPreventComposerAnimation;
    const isForwarding = toChatId === chatId;
    const forwardedMessages = forwardMessageIds?.map((id) => selectChatMessage(global, fromChatId!, id)!);

    const draft = selectDraft(global, chatId, threadId);
    const replyInfo = draft?.replyInfo;
    const suggestedPostInfo = draft?.suggestedPostInfo;
    const replyToPeerId = replyInfo?.replyToPeerId;
    const senderChat = replyToPeerId ? selectChat(global, replyToPeerId) : undefined;

    let message: ApiMessage | undefined;
    if (editingId) {
      message = selectEditingMessage(global, chatId, threadId, messageListType);
    } else if (isForwarding && forwardMessageIds!.length === 1) {
      message = forwardedMessages?.[0];
    } else if (replyInfo && !shouldForceShowEditing) {
      message = selectChatMessage(global, replyInfo.replyToPeerId || chatId, replyInfo.replyToMsgId);
    }

    let sender: ApiPeer | undefined;

    const selectSenderFromForwardedMessage = (forwardedMessage: ApiMessage) => {
      sender = selectForwardedSender(global, forwardedMessage);
      if (!sender) {
        sender = selectSender(global, forwardedMessage);
      }
      return sender;
    };

    if (editingId && message) {
      sender = selectSender(global, message);
    } else if (isForwarding) {
      let forwardSenders = forwardedMessages?.map((m) => selectSenderFromForwardedMessage(m)).filter(Boolean);
      forwardSenders = forwardSenders ? unique(forwardSenders) : undefined;
      sender = forwardSenders?.length === 1 ? forwardSenders?.[0] : undefined;
    } else if (replyInfo && message && !shouldForceShowEditing) {
      const { forwardInfo } = message;
      const isChatWithSelf = selectIsChatWithSelf(global, chatId);
      if (forwardInfo && (forwardInfo.isChannelPost || isChatWithSelf)) {
        sender = selectForwardedSender(global, message);
      }

      if (!sender && (!forwardInfo?.hiddenUserName || Boolean(replyInfo.quoteText))) {
        sender = selectSender(global, message);
      }
    }

    const chat = sender && selectChat(global, sender.id);
    const isSenderChannel = chat && isChatChannel(chat);

    const forwardsHaveCaptions = forwardedMessages?.some((forward) => (
      forward?.content.text && Object.keys(forward.content).length > 1
    ));

    const isContextMenuDisabled = isForwarding && forwardMessageIds!.length === 1
      && Boolean(message?.content.storyData);

    const isReplyToDiscussion = replyInfo?.replyToMsgId === threadId && !replyInfo.replyToPeerId;

    const isMediaNsfw = message && selectIsMediaNsfw(global, message);

    return {
      replyInfo,
      suggestedPostInfo,
      editingId,
      message,
      sender,
      shouldAnimate,
      forwardedMessagesCount: isForwarding ? forwardMessageIds!.length : undefined,
      noAuthors,
      noCaptions,
      forwardsHaveCaptions,
      isCurrentUserPremium: selectIsCurrentUserPremium(global),
      isContextMenuDisabled,
      isReplyToDiscussion,
      isInChangingRecipientMode: isModalShown,
      shouldPreventComposerAnimation,
      senderChat,
      currentUserId: global.currentUserId,
      isSenderChannel,
      forwardMessageIds,
      fromChatId,
      isMediaNsfw,
      theme: selectTheme(global),
    };
  },
)(ComposerEmbeddedMessage));
