// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
import { Text, mergeStyles } from '@fluentui/react';
import { ChatMyMessage } from '@fluentui-contrib/react-chat';
import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react';
import { chatMessageDateStyle, chatMessageFailedTagStyle, chatMessageDateFailedStyle } from '../../styles/ChatMessageComponent.styles';
import { useIdentifiers } from '../../../identifiers/IdentifierProvider';
import { useTheme } from '../../../theming';
import { ChatMessageActionFlyout } from '../ChatMessageActionsFlyout';
import { chatMessageActionMenuProps } from '../ChatMessageActionMenu';
import { useLocale } from '../../../localization';
import { createStyleFromV8Style } from '../../styles/v8StyleShim';
import { mergeClasses } from '@fluentui/react-components';
import { useChatMyMessageStyles, useChatMessageCommonStyles, chatMyMessageActionMenuClassName } from '../../styles/MessageThread.styles';
import { generateCustomizedTimestamp, generateDefaultTimestamp, getMessageBubbleContent, getMessageEditedDetails } from '../../utils/ChatMessageComponentUtils';
/** @private */
const MessageBubble = (props) => {
    var _a;
    const ids = useIdentifiers();
    const theme = useTheme();
    const locale = useLocale();
    const { userId, message, onRemoveClick, onResendClick, disableEditing, showDate, messageContainerStyle, strings, onEditClick, remoteParticipantsCount = 0, onRenderAvatar, showMessageStatus, messageStatus, inlineImageOptions, onDisplayDateTimeString, onRenderAttachmentDownloads, actionsForAttachment, shouldFocusFluentMessageBody } = props;
    const formattedTimestamp = useMemo(() => {
        const defaultTimeStamp = message.createdOn ? generateDefaultTimestamp(message.createdOn, showDate, strings) : undefined;
        const customTimestamp = message.createdOn ? generateCustomizedTimestamp(message.createdOn, locale, onDisplayDateTimeString) : '';
        return customTimestamp || defaultTimeStamp;
    }, [locale, message.createdOn, onDisplayDateTimeString, showDate, strings]);
    // Track if the action menu was opened by touch - if so we increase the touch targets for the items
    const [wasInteractionByTouch, setWasInteractionByTouch] = useState(false);
    // `focused` state is used for show/hide actionMenu
    const [focused, setFocused] = React.useState(false);
    // The chat message action flyout should target the Chat.Message action menu if clicked,
    // or target the chat message if opened via touch press.
    // Undefined indicates the flyout menu should not be being shown.
    const messageRef = useRef(null);
    const messageActionButtonRef = useRef(null);
    const [chatMessageActionFlyoutTarget, setChatMessageActionFlyoutTarget] = useState(undefined);
    const chatActionsEnabled = !disableEditing && message.status !== 'sending' && !!message.mine;
    const [messageReadBy, setMessageReadBy] = useState([]);
    const actionMenuProps = chatMessageActionMenuProps({
        ariaLabel: (_a = strings.actionMenuMoreOptions) !== null && _a !== void 0 ? _a : '',
        enabled: chatActionsEnabled,
        menuButtonRef: messageActionButtonRef,
        menuExpanded: chatMessageActionFlyoutTarget === messageActionButtonRef,
        onActionButtonClick: () => {
            if (message.messageType === 'chat') {
                props.onActionButtonClick(message, setMessageReadBy);
                setChatMessageActionFlyoutTarget(messageActionButtonRef);
            }
        },
        theme
    });
    useEffect(() => {
        if (shouldFocusFluentMessageBody) {
            // set focus in the next render cycle to avoid focus being stolen by other components
            setTimeout(() => {
                var _a;
                (_a = messageRef.current) === null || _a === void 0 ? void 0 : _a.focus();
            });
        }
    }, [shouldFocusFluentMessageBody]);
    const onActionFlyoutDismiss = useCallback(() => {
        // When the flyout dismiss is called, since we control if the action flyout is visible
        // or not we need to set the target to undefined here to actually hide the action flyout
        setChatMessageActionFlyoutTarget(undefined);
    }, [setChatMessageActionFlyoutTarget]);
    const getMessageDetails = useCallback(() => {
        if (messageStatus === 'failed') {
            return React.createElement("div", { className: chatMessageFailedTagStyle(theme) }, strings.failToSendTag);
        }
        else {
            return getMessageEditedDetails(message, theme, strings.editedTag);
        }
    }, [message, messageStatus, strings.editedTag, strings.failToSendTag, theme]);
    const isBlockedMessage = 
    // eslint-disable-next-line no-constant-binary-expression
    false;
    const chatMyMessageStyles = useChatMyMessageStyles();
    const chatMessageCommonStyles = useChatMessageCommonStyles();
    const attached = message.attached === true ? 'center' : message.attached === 'bottom' ? 'bottom' : 'top';
    const getActionsMenu = useCallback(() => {
        return React.createElement("div", { className: mergeClasses(
            // add the static class name to use it in useChatMyMessageStyles
            chatMyMessageActionMenuClassName, chatMyMessageStyles.menu, 
            // Make actions menu visible when the message is focused or the flyout is shown
            focused || (chatMessageActionFlyoutTarget === null || chatMessageActionFlyoutTarget === void 0 ? void 0 : chatMessageActionFlyoutTarget.current) ? chatMyMessageStyles.menuVisible : chatMyMessageStyles.menuHidden) }, actionMenuProps === null || actionMenuProps === void 0 ? void 0 : actionMenuProps.children);
    }, [actionMenuProps === null || actionMenuProps === void 0 ? void 0 : actionMenuProps.children, chatMessageActionFlyoutTarget, chatMyMessageStyles.menu, chatMyMessageStyles.menuHidden, chatMyMessageStyles.menuVisible, focused]);
    const getContent = useCallback(() => {
        return React.createElement("div", null,
            getMessageBubbleContent(message, strings, userId, inlineImageOptions, onRenderAttachmentDownloads, actionsForAttachment),
            getActionsMenu());
    }, [actionsForAttachment, getActionsMenu, inlineImageOptions, message, onRenderAttachmentDownloads, strings, userId]);
    const chatMessage = React.createElement(React.Fragment, null,
        React.createElement("div", { key: props.message.messageId },
            React.createElement(ChatMyMessage, { attached: attached, key: props.message.messageId, body: {
                    // messageContainerStyle used in className and style prop as style prop can't handle CSS selectors
                    className: mergeClasses(chatMessageCommonStyles.body, chatMyMessageStyles.body, isBlockedMessage ? chatMessageCommonStyles.blocked : props.message.status === 'failed' ? chatMessageCommonStyles.failed : undefined, attached !== 'top' ? chatMyMessageStyles.bodyAttached : undefined, mergeStyles(messageContainerStyle)),
                    style: Object.assign({}, createStyleFromV8Style(messageContainerStyle)),
                    ref: messageRef
                }, root: {
                    className: chatMyMessageStyles.root,
                    onBlur: e => {
                        // `focused` controls is focused the whole `ChatMessage` or any of its children. When we're navigating
                        // with keyboard the focused element will be changed and there is no way to use `:focus` selector
                        if (chatMessageActionFlyoutTarget === null || chatMessageActionFlyoutTarget === void 0 ? void 0 : chatMessageActionFlyoutTarget.current) {
                            // doesn't dismiss action button if flyout is open, otherwise, narrator's focus will stay on the closed action menu
                            return;
                        }
                        const shouldPreserveFocusState = e.currentTarget.contains(e.relatedTarget);
                        setFocused(shouldPreserveFocusState);
                    },
                    onFocus: () => {
                        // react onFocus is called even when nested component receives focus (i.e. it bubbles)
                        // so when focus moves within actionMenu, the `focus` state in chatMessage remains true, and keeps actionMenu visible
                        setFocused(true);
                    }
                }, "data-testid": "chat-composite-message", author: React.createElement(Text, { className: chatMessageDateStyle(theme) }, message.senderDisplayName), timestamp: React.createElement(Text, { className: props.message.status === 'failed' ? chatMessageDateFailedStyle(theme) : chatMessageDateStyle(theme), "data-testid": ids.messageTimestamp }, formattedTimestamp), details: getMessageDetails(), onTouchStart: () => setWasInteractionByTouch(true), onPointerDown: () => setWasInteractionByTouch(false), onKeyDown: () => setWasInteractionByTouch(false), onClick: () => {
                    if (!wasInteractionByTouch) {
                        return;
                    }
                    // If the message was touched via touch we immediately open the menu
                    // flyout (when using mouse the 3-dot menu that appears on hover
                    // must be clicked to open the flyout).
                    // In doing so here we set the target of the flyout to be the message and
                    // not the 3-dot menu button to position the flyout correctly.
                    setChatMessageActionFlyoutTarget(messageRef);
                    if (message.messageType === 'chat') {
                        props.onActionButtonClick(message, setMessageReadBy);
                    }
                } }, getContent())),
        chatActionsEnabled && React.createElement(ChatMessageActionFlyout, { hidden: !chatMessageActionFlyoutTarget, target: chatMessageActionFlyoutTarget, increaseFlyoutItemSize: wasInteractionByTouch, onDismiss: onActionFlyoutDismiss, onEditClick: onEditClick, onRemoveClick: onRemoveClick, onResendClick: onResendClick, strings: strings, messageReadBy: messageReadBy, messageStatus: messageStatus !== null && messageStatus !== void 0 ? messageStatus : 'failed', remoteParticipantsCount: remoteParticipantsCount, onRenderAvatar: onRenderAvatar, showMessageStatus: showMessageStatus }));
    return chatMessage;
};
/** @private */
export const ChatMyMessageComponentAsMessageBubble = React.memo(MessageBubble);
//# sourceMappingURL=ChatMyMessageComponentAsMessageBubble.js.map