import { memo, type TeactNode } from '../../../lib/teact/teact';
import { getActions, getGlobal, withGlobal } from '../../../global';

import type { ApiChat, ApiMessage, ApiPeer } from '../../../api/types';

import {
  GENERAL_TOPIC_ID,
  SERVICE_NOTIFICATIONS_USER_ID,
  STARS_CURRENCY_CODE,
  TME_LINK_PREFIX,
  TON_CURRENCY_CODE,
} from '../../../config';
import {
  getMainUsername,
  getMessageContent,
  getMessageInvoice,
  getMessageTextWithFallback,
  isChatChannel,
} from '../../../global/helpers';
import { getPeerTitle } from '../../../global/helpers/peers';
import { getMessageReplyInfo } from '../../../global/helpers/replies';
import {
  selectChat,
  selectChatMessage,
  selectMonoforumChannel,
  selectPeer,
  selectSender,
  selectTopic,
} from '../../../global/selectors';
import { selectThreadIdFromMessage } from '../../../global/selectors/threads';
import { ensureProtocol } from '../../../util/browser/url';
import {
  formatDateTimeToString, formatScheduledDateTime, formatShortDuration,
} from '../../../util/dates/oldDateFormat';
import { convertTonFromNanos, formatCurrency } from '../../../util/formatCurrency';
import { formatCurrencyAmountAsText, formatStarsAsText, formatTonAsText } from '../../../util/localization/format';
import { conjuctionWithNodes } from '../../../util/localization/utils';
import { getServerTime } from '../../../util/serverTime';
import renderText from '../../common/helpers/renderText';
import { renderTextWithEntities } from '../../common/helpers/renderTextWithEntities';
import {
  getCallMessageKey,
  getPinnedMediaValue,
  renderMessageLink,
  renderPeerLink,
  renderTopicLink,
  translateWithYou,
} from './helpers/messageActions';

import useLang from '../../../hooks/useLang';
import useLastCallback from '../../../hooks/useLastCallback';
import useOldLang from '../../../hooks/useOldLang';

import CustomEmoji from '../../common/CustomEmoji';
import TopicDefaultIcon from '../../common/TopicDefaultIcon';
import Link from '../../ui/Link';

import styles from './ActionMessage.module.scss';

type OwnProps = {
  message: ApiMessage;
  isInsideTopic?: boolean;
  asPreview?: boolean;
};

type StateProps = {
  currentUserId?: string;
  sender?: ApiPeer;
  replyMessage?: ApiMessage;
  chat?: ApiChat;
};

const NBSP = '\u00A0';
const DEFAULT_TOPIC_ICON_ID = '0';

const UNSUPPORTED_LANG_KEY = 'ActionUnsupported';

const ActionMessageText = ({
  message,
  currentUserId,
  sender,
  chat,
  replyMessage,
  isInsideTopic,
  asPreview,
}: OwnProps & StateProps) => {
  const {
    openTelegramLink,
    openUrl,
  } = getActions();
  const { chatId, isOutgoing } = message;
  const action = message.content.action!;

  const lang = useLang();
  const oldLang = useOldLang();

  function renderStrong(text: TeactNode) {
    if (asPreview) return text;
    return <span className={styles.strong}>{text}</span>;
  }

  const renderActionText = useLastCallback(() => {
    const global = getGlobal();

    const isServiceNotificationsChat = chatId === SERVICE_NOTIFICATIONS_USER_ID;
    const isSavedMessages = chatId === currentUserId;

    const isChannel = chat && isChatChannel(chat);
    const senderTitle = sender && getPeerTitle(lang, sender);
    const chatTitle = chat && getPeerTitle(lang, chat);

    const userFallbackText = lang('ActionFallbackUser');
    const chatFallbackText = lang('ActionFallbackChat');
    const channelFallbackText = lang('ActionFallbackChannel');

    const senderLink = renderPeerLink(sender?.id, senderTitle || userFallbackText, asPreview);
    const chatLink = renderPeerLink(chat?.id, chatTitle || chatFallbackText, asPreview);

    switch (action.type) {
      case 'pinMessage': {
        if (replyMessage) {
          const formattedText = getMessageTextWithFallback(lang, replyMessage);
          if (formattedText) {
            const textLink = renderMessageLink(
              replyMessage,
              renderTextWithEntities({
                text: formattedText.text,
                entities: formattedText.entities,
                asPreview: true,
              }),
              asPreview,
            );

            return translateWithYou(
              lang, 'ActionPinnedText', isOutgoing, { text: textLink, from: senderLink },
            );
          }

          const mediaValue = getPinnedMediaValue(lang, replyMessage);
          if (mediaValue) {
            const messageLink = renderMessageLink(replyMessage, mediaValue, asPreview);
            return translateWithYou(
              lang, 'ActionPinnedMedia', isOutgoing, { from: senderLink, media: messageLink },
            );
          }
        }

        return translateWithYou(
          lang,
          'ActionPinnedNotFound',
          isOutgoing,
          { from: senderLink },
        );
      }

      case 'gameScore': {
        const { score } = action;

        const gameTitle = replyMessage?.content.game?.title;
        const gameLink = gameTitle && renderMessageLink(replyMessage, renderText(gameTitle), asPreview);

        if (gameLink) {
          return translateWithYou(
            lang,
            'ActionGameScore',
            isOutgoing,
            { from: senderLink, count: score, game: gameLink },
            { pluralValue: score },
          );
        }

        return translateWithYou(
          lang, 'ActionGameScoreNoGame', isOutgoing, { from: senderLink, count: score }, { pluralValue: score },
        );
      }

      case 'chatJoinedByLink':
        return lang('ActionUserJoinedByLink', { from: senderLink }, { withNodes: true });

      case 'chatJoinedByRequest':
        return translateWithYou(lang, 'ActionJoinedByRequest', isOutgoing, { from: senderLink });

      case 'channelJoined': {
        const { isViaRequest, inviterId } = action;
        const inviter = inviterId ? selectPeer(global, inviterId) : undefined;
        if (inviter && inviterId !== currentUserId) {
          const inviterLink = renderPeerLink(inviterId, getPeerTitle(lang, inviter) || userFallbackText, asPreview);
          return lang('ActionAddYou', { from: inviterLink }, { withNodes: true });
        }

        return lang(isViaRequest ? 'ActionChannelJoinedByRequestChannelYou' : 'ActionChannelJoinedYou');
      }

      case 'chatEditTitle': {
        const { title } = action;
        if (isChannel) return lang('ActionChangedTitleChannel', { title });
        return translateWithYou(lang, 'ActionChangedTitle', isOutgoing, { title, from: senderLink });
      }

      case 'chatDeletePhoto':
        return isChannel ? lang('ActionRemovedPhotoChannel')
          : translateWithYou(lang, 'ActionRemovedPhoto', isOutgoing, { from: senderLink });

      case 'chatEditPhoto':
        return isChannel ? lang('ActionChangedPhotoChannel')
          : translateWithYou(lang, 'ActionChangedPhoto', isOutgoing, { from: senderLink });

      case 'chatCreate': {
        const { title } = action;
        return lang('ActionCreatedChat', { title, from: senderLink }, { withNodes: true });
      }

      case 'channelCreate': {
        const { title } = action;
        return isChannel ? lang('ActionCreatedChannel')
          : translateWithYou(lang, 'ActionCreatedChat', isOutgoing, { title, from: senderLink });
      }

      case 'chatMigrateTo': {
        const { channelId } = action;
        const channel = selectChat(global, channelId)!;
        const channelLink = renderPeerLink(channelId, getPeerTitle(lang, channel)!, asPreview);
        return lang('ActionMigratedTo', { chat: channelLink }, { withNodes: true });
      }

      case 'channelMigrateFrom': {
        const { chatId: originalChatId, title } = action;
        const originalChatLink = renderPeerLink(originalChatId, title || chatFallbackText, asPreview);
        return lang('ActionMigratedFrom', { chat: originalChatLink }, { withNodes: true });
      }

      case 'topicCreate': {
        const { title, iconColor, iconEmojiId } = action;

        const topicId = selectThreadIdFromMessage(global, message);

        const topicLinkContent = (
          <>
            {iconEmojiId ? <CustomEmoji documentId={iconEmojiId} isSelectable />
              : <TopicDefaultIcon topicId={topicId} title={title} iconColor={iconColor} />}
            {NBSP}
            {renderText(title)}
          </>
        );
        const topicLink = renderTopicLink(chatId, Number(topicId), topicLinkContent, asPreview);
        return lang('ActionTopicCreated', { topic: topicLink }, { withNodes: true });
      }

      case 'topicEdit': {
        const {
          iconEmojiId, isClosed, isHidden, title,
        } = action;

        const topicId = selectThreadIdFromMessage(global, message);
        const currentTopic = selectTopic(global, chatId, topicId);
        const topicLinkContent = (
          <>
            {iconEmojiId && iconEmojiId !== DEFAULT_TOPIC_ICON_ID
              ? <CustomEmoji documentId={iconEmojiId} isSelectable />
              : (
                <TopicDefaultIcon
                  topicId={topicId}
                  title={title || currentTopic?.title || lang('ActionTopicPlaceholder')}
                  iconColor={currentTopic?.iconColor}
                />
              )}
            {topicId !== GENERAL_TOPIC_ID && NBSP}
            {renderText(title || currentTopic?.title || lang('ActionTopicPlaceholder'))}
          </>
        );
        const topicLink = renderTopicLink(chatId, Number(topicId), topicLinkContent, asPreview);

        const topicPlaceholderLink = renderTopicLink(
          chatId, Number(topicId), lang('ActionTopicPlaceholder'), asPreview,
        );

        if (isClosed !== undefined) {
          if (isInsideTopic) {
            return lang(isClosed ? 'ActionTopicClosedInside' : 'ActionTopicReopenedInside');
          }

          return lang(
            isClosed ? 'ActionTopicClosed' : 'ActionTopicReopened',
            { from: senderLink, topic: topicLink },
            { withNodes: true },
          );
        }

        if (isHidden !== undefined) {
          if (isInsideTopic) {
            return lang(isHidden ? 'ActionTopicHiddenInside' : 'ActionTopicUnhiddenInside');
          }
          return lang(
            isHidden ? 'ActionTopicHidden' : 'ActionTopicUnhidden',
            { topic: topicLink },
            { withNodes: true },
          );
        }

        if (title && iconEmojiId) {
          return lang(
            'ActionTopicIconAndRenamed',
            {
              from: senderLink,
              link: topicPlaceholderLink,
              topic: topicLink,
            },
            { withNodes: true },
          );
        }

        if (title === undefined) {
          if (!iconEmojiId || iconEmojiId === DEFAULT_TOPIC_ICON_ID) {
            return lang(
              'ActionTopicIconRemoved', { from: senderLink, link: topicPlaceholderLink }, { withNodes: true },
            );
          }
          return lang(
            'ActionTopicIconChanged',
            {
              from: senderLink,
              link: topicPlaceholderLink,
              emoji: <CustomEmoji documentId={iconEmojiId} loopLimit={2} />,
            },
            { withNodes: true },
          );
        }

        return lang('ActionTopicRenamed', { from: senderLink, link: topicPlaceholderLink, title }, { withNodes: true });
      }

      case 'boostApply':
        return translateWithYou(
          lang,
          'ActionBoostApply',
          isOutgoing,
          { from: senderLink, count: action.boosts },
          { pluralValue: action.boosts },
        );

      case 'chatAddUser': {
        const { userIds } = action;

        if (sender?.id === userIds[0]) {
          return translateWithYou(lang, 'ActionUserJoined', isOutgoing, { from: senderLink });
        }

        if (userIds.length === 1) {
          const user = selectPeer(global, userIds[0]);
          const userTitle = (user && getPeerTitle(lang, user)) || userFallbackText;
          const userLink = renderPeerLink(user?.id, userTitle, asPreview);
          return translateWithYou(lang, 'ActionAddUser', isOutgoing, { from: senderLink, user: userLink });
        }

        const users = userIds.map((userId) => selectPeer(global, userId)).filter(Boolean);

        if (!users.length) {
          return translateWithYou(lang, 'ActionAddUser', isOutgoing, { from: senderLink, user: userFallbackText });
        }

        const userLinks = users.map((user) => (
          renderPeerLink(user.id, getPeerTitle(lang, user) || userFallbackText, asPreview)
        ));

        return translateWithYou(
          lang, 'ActionAddUsersMany', isOutgoing, { from: senderLink, users: conjuctionWithNodes(lang, userLinks) },
        );
      }

      case 'chatDeleteUser': {
        const { userId } = action;
        if (sender?.id === userId) {
          return translateWithYou(lang, 'ActionUserLeft', isOutgoing, { from: senderLink });
        }

        const user = selectPeer(global, userId);
        const userTitle = (user && getPeerTitle(lang, user)) || userFallbackText;
        const userLink = renderPeerLink(user?.id, userTitle, asPreview);
        return translateWithYou(lang, 'ActionKickUser', isOutgoing, { from: senderLink, user: userLink });
      }

      case 'botAllowed': {
        const {
          app, domain, isAttachMenu, isFromRequest,
        } = action;
        if (isAttachMenu) return lang('ActionAttachMenuBotAllowed');
        if (isFromRequest) return lang('ActionWebappBotAllowed');
        if (app) {
          const senderUsername = sender && getMainUsername(sender);
          const link = senderUsername && `${TME_LINK_PREFIX + senderUsername}/${app.shortName}`;
          const appLink = link
            ? <Link onClick={() => openTelegramLink({ url: link })}>{app.title}</Link>
            : lang('ActionBotAppPlaceholder');
          return lang('ActionBotAllowedFromApp', { app: appLink }, { withNodes: true });
        }

        if (!domain) return lang(UNSUPPORTED_LANG_KEY);

        const url = ensureProtocol(domain);

        const link = <Link onClick={() => openUrl({ url })}>{domain}</Link>;
        return lang('ActionBotAllowedFromDomain', { domain: link }, { withNodes: true });
      }

      case 'giveawayLaunch': {
        const { stars } = action;

        if (stars) {
          return lang(
            isChannel ? 'ActionGiveawayStarsStarted' : 'ActionGiveawayStarsStartedGroup',
            { from: senderLink, amount: renderStrong(formatStarsAsText(lang, stars)) },
            { withNodes: true },
          );
        }

        return lang(
          isChannel ? 'ActionGiveawayStarted' : 'ActionGiveawayStartedGroup',
          { from: senderLink },
          { withNodes: true },
        );
      }

      case 'giveawayResults': {
        const { winnersCount, isStars, unclaimedCount } = action;
        if (!winnersCount) return lang('ActionGiveawayResultsNone');
        if (unclaimedCount) {
          return lang(isStars ? 'ActionGiveawayResultsStarsSome' : 'ActionGiveawayResultsSome');
        }

        return lang(
          isStars ? 'ActionGiveawayResultsStars' : 'ActionGiveawayResults',
          { count: winnersCount },
          { pluralValue: winnersCount },
        );
      }

      case 'giftStars':
      case 'giftPremium':
      case 'giftTon': {
        const {
          amount, currency, cryptoAmount, cryptoCurrency, type,
        } = action;

        const price = formatCurrency(lang, amount, currency, { asFontIcon: true });
        const cryptoPrice = cryptoAmount && type !== 'giftTon'
          ? formatCurrency(lang, cryptoAmount, cryptoCurrency!) : undefined;

        const cost = cryptoPrice ? lang('ActionGiftCostCrypto', { price, cryptoPrice }, { withNodes: true }) : price;

        if (isServiceNotificationsChat) {
          return lang('ActionGiftTextCostAnonymous', { cost }, { withNodes: true });
        }
        return translateWithYou(
          lang, 'ActionGiftTextCost', isOutgoing, { from: senderLink, cost: renderStrong(cost) },
        );
      }

      case 'prizeStars':
      case 'giftCode': {
        return translateWithYou(lang, 'ActionGiftTextUnknown', isOutgoing, undefined);
      }

      case 'groupCall': {
        const { duration } = action;
        const durationText = duration ? formatShortDuration(lang, duration) : undefined;
        if (durationText) {
          if (isChannel) {
            return lang('ActionGroupCallFinishedChannel', { duration: durationText });
          }
          return lang(
            'ActionGroupCallFinishedGroup', { from: senderLink, duration: durationText }, { withNodes: true },
          );
        }

        if (isChannel) return lang('ActionGroupCallStartedChannel');
        return lang('ActionGroupCallStartedGroup', { from: senderLink }, { withNodes: true });
      }

      case 'groupCallScheduled': {
        const { scheduleDate } = action;
        const formattedDate = formatDateTimeToString(scheduleDate * 1000, lang.code, true);

        if (isChannel) return lang('ActionGroupCallScheduledChannel', { date: formattedDate });
        return lang('ActionGroupCallScheduledGroup', { from: senderLink, date: formattedDate }, { withNodes: true });
      }

      case 'inviteToGroupCall': {
        const { userIds } = action;

        if (userIds.length === 1) {
          const user = selectPeer(global, userIds[0]);
          const userTitle = (user && getPeerTitle(lang, user)) || userFallbackText;
          const userLink = renderPeerLink(user?.id, userTitle, asPreview);
          return translateWithYou(lang, 'ActionVideoInvited', isOutgoing, { from: senderLink, user: userLink });
        }

        const users = userIds.map((userId) => selectPeer(global, userId)).filter(Boolean);

        if (!users.length) {
          return translateWithYou(
            lang, 'ActionVideoInvited', isOutgoing, { from: senderLink, user: userFallbackText },
          );
        }

        const userLinks = users.map((user) => (
          renderPeerLink(user.id, getPeerTitle(lang, user) || userFallbackText, asPreview)
        ));

        return translateWithYou(
          lang, 'ActionVideoInvitedMany', isOutgoing, { from: senderLink, users: conjuctionWithNodes(lang, userLinks) },
        );
      }

      case 'paymentSent': {
        const {
          currency, totalAmount, isRecurringInit, isRecurringUsed,
        } = action;

        const cost = renderStrong(formatCurrency(lang, totalAmount, currency, { asFontIcon: true }));
        const invoice = replyMessage && getMessageInvoice(replyMessage);
        const invoiceTitle = invoice?.title;

        if (isRecurringUsed) {
          return lang('ActionPaymentUsedRecurring', { amount: cost }, { withNodes: true });
        }

        if (!invoiceTitle) {
          if (isRecurringInit) {
            return lang('ActionPaymentInitRecurring', { amount: cost, user: chatLink }, { withNodes: true });
          }

          return lang('ActionPaymentDone', { amount: cost, user: chatLink }, { withNodes: true });
        }

        if (isRecurringInit) {
          return lang(
            'ActionPaymentInitRecurringFor',
            { amount: cost, user: chatLink, invoice: renderMessageLink(replyMessage, invoiceTitle, asPreview) },
            { withNodes: true },
          );
        }

        return lang(
          'ActionPaymentDoneFor',
          { amount: cost, user: chatLink, invoice: renderMessageLink(replyMessage, invoiceTitle, asPreview) },
          { withNodes: true },
        );
      }

      case 'paymentRefunded': {
        const { currency, totalAmount, peerId } = action;
        const peer = selectPeer(global, peerId);
        const peerTitle = (peer && getPeerTitle(lang, peer)) || userFallbackText;
        const peerLink = renderPeerLink(peer?.id, peerTitle, asPreview);
        const amount = formatCurrency(lang, totalAmount, currency, { asFontIcon: true });

        return lang('ActionPaymentRefunded', { peer: peerLink, amount }, { withNodes: true });
      }

      case 'starGift': {
        const {
          gift, alreadyPaidUpgradeStars, peerId, savedId, fromId, isPrepaidUpgrade, isAuctionAcquired,
        } = action;
        const isToChannel = Boolean(peerId && savedId);

        const fromPeer = fromId ? selectPeer(global, fromId) : sender;
        const fromTitle = (fromPeer && getPeerTitle(lang, fromPeer)) || userFallbackText;
        const fromLink = renderPeerLink(fromPeer?.id, fromTitle, asPreview);

        const toPeer = peerId ? selectPeer(global, peerId) : undefined;
        const toTitle = (toPeer && getPeerTitle(lang, toPeer))
          || (isToChannel ? channelFallbackText : userFallbackText);
        const toLink = renderPeerLink(toPeer?.id, toTitle, asPreview);

        const starsAmount = gift.stars + (alreadyPaidUpgradeStars || 0);
        const cost = renderStrong(formatStarsAsText(lang, starsAmount));

        if (isAuctionAcquired) {
          return lang('ActionStarGiftAuctionWon', { cost }, { withNodes: true });
        }

        if (isPrepaidUpgrade && gift.upgradeStars) {
          const upgradeCost = renderStrong(formatStarsAsText(lang, gift.upgradeStars));

          return translateWithYou(
            lang, 'ActionStarGiftPrepaidUpgrade', isOutgoing, {
              peer: isOutgoing ? toLink : senderLink,
              cost: upgradeCost,
            },
          );
        }

        if (isToChannel) {
          const channelPeer = selectPeer(global, peerId!);
          const isYou = fromPeer?.id === currentUserId;

          const channelTitle = (channelPeer && getPeerTitle(lang, channelPeer)) || channelFallbackText;
          const channelLink = renderPeerLink(peerId, channelTitle, asPreview);
          return translateWithYou(
            lang, 'ActionStarGiftSentChannel', isYou, { user: fromLink, channel: channelLink, cost },
          );
        }

        if (isServiceNotificationsChat) {
          return lang('ActionStarGiftReceivedAnonymous', { cost }, { withNodes: true });
        }

        if (isSavedMessages) {
          return lang('ActionStarGiftSelfBought', { cost }, { withNodes: true });
        }

        if (isOutgoing) {
          return lang('ActionStarGiftSent', { cost }, { withNodes: true });
        }

        return lang('ActionStarGiftReceived', { user: senderLink, cost }, { withNodes: true });
      }

      case 'starGiftUnique': {
        const {
          isTransferred, isUpgrade, savedId, peerId, fromId, resaleAmount, gift, transferStars, isPrepaidUpgrade,
          isFromOffer,
        } = action;

        const isToChannel = Boolean(peerId && savedId);

        const fromPeer = fromId ? selectPeer(global, fromId) : sender;
        const fromTitle = (fromPeer && getPeerTitle(lang, fromPeer)) || userFallbackText;
        const fromLink = renderPeerLink(fromPeer?.id, fromTitle, asPreview);

        const toPeer = peerId ? selectPeer(global, peerId) : undefined;
        const toTitle = (toPeer && getPeerTitle(lang, toPeer))
          || (isToChannel ? channelFallbackText : userFallbackText);
        const toLink = renderPeerLink(toPeer?.id, toTitle, asPreview);

        if (isFromOffer && resaleAmount) {
          const giftName = lang('GiftUnique', { title: gift.title, number: gift.number });
          const amountText = formatCurrencyAmountAsText(lang, resaleAmount);

          const formattedAmountText = asPreview ? amountText : renderStrong(amountText);
          const formattedGiftName = asPreview ? giftName : renderStrong(giftName);

          if (isOutgoing) {
            return lang(
              'ActionStarGiftSoldFromOffer',
              { user: chatLink, gift: formattedGiftName, cost: formattedAmountText },
              { withNodes: true },
            );
          }

          return lang(
            'ActionStarGiftBoughtFromOffer',
            { user: senderLink, gift: formattedGiftName, cost: formattedAmountText },
            { withNodes: true },
          );
        }

        if (isPrepaidUpgrade) {
          if (isOutgoing) {
            return lang('ActionStarGiftPrepaidUpgradedYou');
          }
          return lang('ActionStarGiftPrepaidUpgraded', { user: toLink }, { withNodes: true });
        }

        if (resaleAmount && !transferStars) {
          const amountText = formatCurrencyAmountAsText(lang, resaleAmount);

          return lang(
            isOutgoing
              ? 'ApiMessageMessageActionResaleStarGiftUniqueOutgoing'
              : 'ApiMessageMessageActionResaleStarGiftUniqueIncoming',
            {
              gift: lang('GiftUnique', { title: gift.title, number: gift.number }),
              stars: renderStrong(amountText),
            },
            { withNodes: true },
          );
        }

        if (isToChannel) {
          const channelPeer = selectPeer(global, peerId!);
          const isYou = fromPeer?.id === currentUserId;
          const isAnonymous = fromPeer?.id === SERVICE_NOTIFICATIONS_USER_ID;

          const channelTitle = (channelPeer && getPeerTitle(lang, channelPeer)) || channelFallbackText;
          const channelLink = renderPeerLink(peerId, channelTitle, asPreview);

          if (isUpgrade) {
            return translateWithYou(
              lang, 'ActionStarGiftUpgradedChannel', isYou, { user: fromLink, channel: channelLink },
            );
          }

          if (isTransferred) {
            if (isAnonymous) {
              return lang('ActionStarGiftTransferredUnknownChannel', { channel: channelLink }, { withNodes: true });
            }

            return translateWithYou(
              lang, 'ActionStarGiftTransferredChannel', isYou, { user: fromLink, channel: channelLink },
            );
          }
        }

        if (isSavedMessages) {
          if (isUpgrade) return lang('ActionStarGiftUpgradedSelf');
          if (isTransferred) return lang('ActionStarGiftTransferredSelf');
          if (resaleAmount) {
            const amountText = formatCurrencyAmountAsText(lang, resaleAmount);
            return lang(
              'ApiMessageMessageActionResaleStarGiftUniqueOutgoing',
              {
                gift: lang('GiftUnique', { title: gift.title, number: gift.number }),
                stars: asPreview ? amountText : renderStrong(amountText),
              },
              { withNodes: true },
            );
          }
          if (gift.isCrafted) return lang('ActionStarGiftCraftedSelf');
        }

        if (isUpgrade) {
          if (isOutgoing) {
            return lang('ActionStarGiftUpgradedMine', { user: chatLink }, { withNodes: true });
          }

          if (isSavedMessages) {
            return lang('ActionStarGiftUpgradedSelf');
          }

          return lang('ActionStarGiftUpgradedUser', { user: senderLink }, { withNodes: true });
        }

        if ((isTransferred || transferStars) && !resaleAmount) {
          if (sender?.id === SERVICE_NOTIFICATIONS_USER_ID) {
            return lang('ActionStarGiftTransferredUnknown');
          }

          if (isSavedMessages) {
            return lang('ActionStarGiftTransferredSelf');
          }

          if (isOutgoing) {
            return lang('ActionStarGiftTransferredMine', { user: chatLink }, { withNodes: true });
          }

          return lang('ActionStarGiftTransferred', { user: senderLink }, { withNodes: true });
        }

        if (isOutgoing) {
          return lang('ActionGiftUniqueSent');
        }

        return lang('ActionGiftUniqueReceived', { user: senderLink }, { withNodes: true });
      }

      case 'suggestProfilePhoto': {
        const actionPeer = (isOutgoing ? chat : sender)!;
        const actionPeerLink = renderPeerLink(
          actionPeer.id, getPeerTitle(lang, actionPeer) || userFallbackText, asPreview,
        );

        return translateWithYou(lang, 'ActionSuggestedPhoto', isOutgoing, { user: actionPeerLink });
      }

      case 'webViewDataSent':
        return lang('ActionWebviewDataDone', { text: action.text });

      case 'expired': {
        const { isRoundVideo, isVoice } = action;
        if (isVoice) return lang('ActionExpiredVoice');
        if (isRoundVideo) return lang('ActionExpiredVideo');

        return lang(UNSUPPORTED_LANG_KEY);
      }

      case 'historyClear':
        return lang('ActionHistoryCleared');

      case 'screenshotTaken':
        return translateWithYou(lang, 'ActionScreenshotTaken', isOutgoing, { from: senderLink });

      case 'contactSignUp':
        return lang('ActionUserRegistered', { from: senderLink }, { withNodes: true });

      case 'customAction':
        return action.message;

      case 'paidMessagesPrice': {
        const { stars, isAllowedInChannel } = action;
        if (stars === 0) {
          if (isChannel) {
            return lang(
              isAllowedInChannel ? 'ActionMessageChannelFree' : 'ActionMessageChannelDisabled',
              { peer: chatLink },
              { withNodes: true },
            );
          }
          return translateWithYou(lang, 'ActionPaidMessagePriceFree', isOutgoing, { peer: senderLink });
        }
        return translateWithYou(lang, 'ActionPaidMessagePrice', isOutgoing, {
          peer: senderLink,
          amount: formatStarsAsText(lang, stars),
        }, { withMarkdown: true });
      }

      case 'paidMessagesRefunded': {
        const { stars } = action;
        const user = selectPeer(global, chatId);
        const userTitle = (user && getPeerTitle(lang, user)) || userFallbackText;

        const key = isOutgoing
          ? 'ApiMessageActionPaidMessagesRefundedOutgoing'
          : 'ApiMessageActionPaidMessagesRefundedIncoming';

        return lang(key, {
          stars: formatStarsAsText(lang, stars),
          user: renderPeerLink(user?.id, userTitle),
        }, { withNodes: true, withMarkdown: true });
      }

      case 'suggestedPostSuccess': {
        const { amount: price } = action;
        const currency = price?.currency || STARS_CURRENCY_CODE;
        const amount = price?.amount || 0;

        const channel = chat?.isMonoforum ? selectMonoforumChannel(global, chatId) : chat;
        const channelTitle = channel && getPeerTitle(lang, channel);
        const channelLink = renderPeerLink(channel?.id, channelTitle || channelFallbackText, asPreview);

        const formattedAmount = currency === TON_CURRENCY_CODE
          ? formatTonAsText(lang, convertTonFromNanos(amount))
          : formatStarsAsText(lang, amount);

        return lang('ActionSuggestedPostSuccess', {
          channel: channelLink,
          amount: formattedAmount,
        }, { withNodes: true });
      }
      case 'suggestedPostRefund': {
        const { payerInitiated } = action;

        const postSender = replyMessage ? selectSender(global, replyMessage) : sender;
        const postSenderTitle = postSender && getPeerTitle(lang, postSender);
        const postSenderLink = renderPeerLink(postSender?.id, postSenderTitle || userFallbackText, asPreview);

        const price = replyMessage?.suggestedPostInfo?.price;
        const currency = price?.currency || STARS_CURRENCY_CODE;
        const amount = price?.amount || 0;

        const channel = chat?.isMonoforum ? selectMonoforumChannel(global, chatId) : chat;
        const channelTitle = channel && getPeerTitle(lang, channel);
        const channelLink = renderPeerLink(channel?.id, channelTitle || channelFallbackText, asPreview);

        const formattedAmount = currency === TON_CURRENCY_CODE
          ? formatTonAsText(lang, convertTonFromNanos(amount))
          : formatStarsAsText(lang, amount);

        if (payerInitiated) {
          return lang('SuggestedPostRefundedByUser', {
            amount: formattedAmount,
            user: postSenderLink,
            channel: channelLink,
          }, { withNodes: true, withMarkdown: true });
        }

        return lang('SuggestedPostRefundedByChannel', {
          amount: formattedAmount,
          peer: postSenderLink,
          channel: channelLink,
        }, { withNodes: true, withMarkdown: true });
      }
      case 'suggestedPostApproval': {
        const { isRejected, isBalanceTooLow, rejectComment } = action;

        if (isRejected) {
          return translateWithYou(
            lang,
            rejectComment ? 'SuggestedPostRejectedWithReason' : 'SuggestedPostRejected',
            isOutgoing,
            { peer: senderLink },
            { withMarkdown: true },
          );
        }

        if (isBalanceTooLow) {
          const replyMessageSender = replyMessage ? selectSender(global, replyMessage) : sender;
          const replyPeerTitle = replyMessageSender && getPeerTitle(lang, replyMessageSender);
          const userLink = renderPeerLink(replyMessageSender?.id, replyPeerTitle || userFallbackText, asPreview);

          const currency = replyMessage?.suggestedPostInfo?.price?.currency || STARS_CURRENCY_CODE;
          const currencyName = currency === TON_CURRENCY_CODE ? lang('CurrencyTon') : lang('CurrencyStars');

          return lang('SuggestedPostBalanceTooLow', {
            peer: userLink,
            currency: currencyName,
          }, { withNodes: true, withMarkdown: true });
        }

        const channel = chat?.isMonoforum ? selectMonoforumChannel(global, chatId) : chat;
        const channelTitle = channel && getPeerTitle(lang, channel);
        const channelLink = renderPeerLink(channel?.id, channelTitle || channelFallbackText, asPreview);

        const { scheduleDate } = action;
        if (scheduleDate) {
          const publishDate = formatScheduledDateTime(scheduleDate, lang, oldLang);
          const isPostPublished = scheduleDate <= getServerTime();

          return translateWithYou(
            lang,
            isPostPublished ? 'SuggestedPostPublished' : 'SuggestedPostPublishSchedule',
            isOutgoing,
            { peer: channelLink, date: publishDate },
            { withMarkdown: true },
          );
        }

        return lang(UNSUPPORTED_LANG_KEY);
      }
      case 'todoCompletions': {
        const { completedIds, incompletedIds } = action;

        let completedItem;
        let incompletedItem;
        const { todo } = replyMessage ? getMessageContent(replyMessage) : {};
        if (todo) {
          const todoItems = todo.todo.items;
          completedItem = todoItems.find((item) => completedIds.includes(item.id));
          incompletedItem = todoItems.find((item) => incompletedIds.includes(item.id));
        }

        if (incompletedItem) {
          const incompletedTaskTitle = incompletedItem.title;

          const incompletedTaskLink = renderMessageLink(
            replyMessage,
            renderTextWithEntities({
              text: incompletedTaskTitle.text,
              entities: incompletedTaskTitle.entities,
              asPreview: true,
            }),
            asPreview,
          );

          return translateWithYou(lang, 'MessageActionTodoCompletionsAsNotDone', isOutgoing, {
            peer: senderLink,
            task: incompletedTaskLink,
          });
        }

        if (completedItem) {
          const completedTaskTitle = completedItem.title;
          const completedTaskLink = renderMessageLink(
            replyMessage,
            renderTextWithEntities({
              text: completedTaskTitle.text,
              entities: completedTaskTitle.entities,
              asPreview: true,
            }),
            asPreview,
          );

          return translateWithYou(lang, 'MessageActionTodoCompletionsAsDone', isOutgoing, {
            peer: senderLink,
            task: completedTaskLink,
          });
        }

        if (completedIds) {
          const completedText = lang('MessageActionTodoTaskCount', {
            count: completedIds.length,
          }, { pluralValue: completedIds.length });
          const completedLink = renderMessageLink(
            replyMessage,
            renderTextWithEntities({
              text: completedText,
              asPreview: true,
            }),
            asPreview,
            { noEllipsis: true },
          );
          return translateWithYou(lang, 'MessageActionTodoCompletionsAsDone', isOutgoing, {
            peer: senderLink,
            task: completedLink,
          });
        }

        const incompletedText = lang('MessageActionTodoTaskCount', {
          count: incompletedIds.length,
        }, { pluralValue: incompletedIds.length });
        const incompletedLink = renderMessageLink(
          replyMessage,
          renderTextWithEntities({
            text: incompletedText,
            asPreview: true,
          }),
          asPreview,
          { noEllipsis: true },
        );

        return translateWithYou(lang, 'MessageActionTodoCompletionsAsNotDone', isOutgoing, {
          peer: senderLink,
          task: incompletedLink,
        });
      }

      case 'todoAppendTasks': {
        const { items } = action;
        const { todo } = replyMessage ? getMessageContent(replyMessage) : {};

        const listTitle = todo?.todo.title.text || '';
        const listLink = renderMessageLink(
          replyMessage,
          renderTextWithEntities({
            text: listTitle,
            asPreview: true,
          }),
          asPreview,
        );

        if (items.length === 1) {
          const taskTitle = items[0].title;
          const taskLink = renderMessageLink(
            replyMessage,
            renderTextWithEntities({
              text: taskTitle.text,
              entities: taskTitle.entities,
              asPreview: true,
            }),
            asPreview,
          );

          return translateWithYou(lang, 'MessageActionAppendTodo', isOutgoing, {
            peer: senderLink,
            task: taskLink,
            list: listLink,
          });
        }

        const tasksText = lang('MessageActionTodoTaskCount', {
          count: items.length,
        }, { pluralValue: items.length });
        const tasksLink = renderMessageLink(
          replyMessage,
          renderTextWithEntities({
            text: tasksText,
            asPreview: true,
          }),
          asPreview,
          { noEllipsis: true },
        );

        return translateWithYou(lang, 'MessageActionAppendTodoMultiple', isOutgoing, {
          peer: senderLink,
          tasks: tasksLink,
          list: listLink,
        });
      }

      case 'pollAppendAnswer':
      case 'pollDeleteAnswer': {
        const optionLink = renderMessageLink(
          replyMessage,
          renderTextWithEntities({
            text: action.answer.text.text,
            entities: action.answer.text.entities,
            asPreview: true,
          }),
          asPreview,
        );

        return translateWithYou(
          lang,
          action.type === 'pollAppendAnswer' ? 'MessageActionPollAppendAnswer' : 'MessageActionPollDeleteAnswer',
          isOutgoing,
          {
            peer: senderLink,
            option: optionLink,
          },
        );
      }

      case 'phoneCall': // Rendered as a regular message, but considered an action for the summary
        return lang(getCallMessageKey(action, isOutgoing));

      case 'noForwardsToggle': {
        const { prevValue, newValue } = action;
        if (newValue && newValue === prevValue) {
          return lang('ActionSharingStillDisabled');
        }
        return translateWithYou(
          lang,
          newValue ? 'ActionSharingDisabled' : 'ActionSharingEnabled',
          isOutgoing,
          { from: senderLink },
        );
      }

      case 'noForwardsRequest': {
        return isOutgoing
          ? lang('NoForwardsRequestYouTitle')
          : lang('NoForwardsRequestTitle', { user: senderLink }, { withNodes: true });
      }

      case 'newCreatorPending': {
        const { newCreatorId } = action;
        const newCreator = selectPeer(global, newCreatorId);
        const newCreatorTitle = (newCreator && getPeerTitle(lang, newCreator)) || userFallbackText;
        const newCreatorLink = renderPeerLink(newCreator?.id, newCreatorTitle, asPreview);
        return lang('ActionNewCreatorPending', { user: newCreatorLink, from: senderLink }, { withNodes: true });
      }

      case 'changeCreator': {
        const { newCreatorId } = action;
        const newCreator = selectPeer(global, newCreatorId);
        const newCreatorTitle = (newCreator && getPeerTitle(lang, newCreator)) || userFallbackText;
        const newCreatorLink = renderPeerLink(newCreator?.id, newCreatorTitle, asPreview);
        return lang('ActionChangeCreator', { user: newCreatorLink, from: senderLink }, { withNodes: true });
      }

      case 'starGiftPurchaseOffer': {
        const { gift, price } = action;

        const peer = isOutgoing ? chat : sender;
        const peerTitle = (peer && getPeerTitle(lang, peer)) || userFallbackText;
        const peerLink = renderPeerLink(peer?.id, peerTitle, asPreview);

        const giftName = lang('GiftUnique', { title: gift.title, number: gift.number });
        const priceText = formatCurrencyAmountAsText(lang, price);

        const formattedGiftName = asPreview ? giftName : renderStrong(giftName);
        const formattedPriceText = asPreview ? priceText : renderStrong(priceText);

        return lang(
          isOutgoing ? 'ActionStarGiftOfferOutgoing' : 'ActionStarGiftOfferIncoming',
          {
            peer: peerLink,
            cost: formattedPriceText,
            gift: formattedGiftName,
          },
          { withNodes: true },
        );
      }

      case 'starGiftPurchaseOfferDeclined': {
        const { gift, price } = action;

        const peer = isOutgoing ? chat : sender;
        const peerTitle = (peer && getPeerTitle(lang, peer)) || userFallbackText;
        const peerLink = renderPeerLink(peer?.id, peerTitle, asPreview);

        const giftName = lang('GiftUnique', { title: gift.title, number: gift.number });
        const priceText = formatCurrencyAmountAsText(lang, price);

        const formattedGiftName = asPreview ? giftName : renderStrong(giftName);
        const formattedPriceText = asPreview ? priceText : renderStrong(priceText);

        return lang(
          isOutgoing ? 'ActionStarGiftOfferDeclinedOutgoing' : 'ActionStarGiftOfferDeclinedIncoming',
          {
            peer: peerLink,
            gift: formattedGiftName,
            cost: formattedPriceText,
          },
          { withNodes: true },
        );
      }

      default:
        return lang(UNSUPPORTED_LANG_KEY);
    }
  });

  return renderActionText();
};

export default memo(withGlobal<OwnProps>(
  (global, { message }): Complete<StateProps> => {
    const chat = selectChat(global, message.chatId);
    const sender = selectSender(global, message);

    const { replyToMsgId, replyToPeerId } = getMessageReplyInfo(message) || {};
    const replyMessage = replyToMsgId
      ? selectChatMessage(global, replyToPeerId || message.chatId, replyToMsgId) : undefined;

    return {
      currentUserId: global.currentUserId,
      replyMessage,
      chat,
      sender,
    };
  },
)(ActionMessageText));
