// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
import React, { useCallback, useState, useMemo, useEffect, useRef } from 'react';
import { mergeStyles, Stack } from '@fluentui/react';
import { Spinner, SpinnerSize } from '@fluentui/react';
import { callCompositeContainerStyles, compositeOuterContainerStyles } from './styles/CallWithChatCompositeStyles';
import { chatSpinnerContainerStyles } from './styles/CallWithChatCompositeStyles';
import { CallWithChatBackedCallAdapter } from './adapter/CallWithChatBackedCallAdapter';
import { CallWithChatBackedChatAdapter } from './adapter/CallWithChatBackedChatAdapter';
import { ChatComposite } from '../ChatComposite';
import { BaseProvider } from '../common/BaseComposite';
import { useTheme } from "../../../../react-components/src";
import { useId } from '@fluentui/react-hooks';
import { containerDivStyles } from '../common/ContainerRectProps';
import { useCallWithChatCompositeStrings } from './hooks/useCallWithChatCompositeStrings';
import { CallCompositeInner } from '../CallComposite/CallComposite';
import { ChatButtonWithUnreadMessagesBadge } from './ChatButton/ChatButtonWithUnreadMessagesBadge';
import { getDesktopCommonButtonStyles } from '../common/ControlBar/CommonCallControlBar';
import { isDisabled } from '../CallComposite/utils';
import { SidePaneHeader } from '../common/SidePaneHeader';
import { useUnreadMessagesTracker } from './ChatButton/useUnreadMessagesTracker';
import { useLocale } from '../localization';
const CallWithChatScreen = (props) => {
    var _a, _b;
    const { callWithChatAdapter, fluentTheme, formFactor = 'desktop' } = props;
    const { surveyOptions } = props;
    const mobileView = formFactor === 'mobile';
    if (!callWithChatAdapter) {
        throw new Error('CallWithChatAdapter is undefined');
    }
    const callAdapter = useMemo(() => new CallWithChatBackedCallAdapter(callWithChatAdapter), [callWithChatAdapter]);
    const [currentCallState, setCurrentCallState] = useState();
    const [isChatInitialized, setIsChatInitialized] = useState(false);
    const [currentPage, setCurrentPage] = useState();
    const [isChatOpen, setIsChatOpen] = useState(false);
    const containerRef = useRef(null);
    useEffect(() => {
        const updateCallWithChatPage = (newState) => {
            var _a;
            setCurrentPage(newState.page);
            setCurrentCallState((_a = newState.call) === null || _a === void 0 ? void 0 : _a.state);
            setIsChatInitialized(newState.chat ? true : false);
        };
        updateCallWithChatPage(callWithChatAdapter.getState());
        callWithChatAdapter.onStateChange(updateCallWithChatPage);
        return () => {
            callWithChatAdapter.offStateChange(updateCallWithChatPage);
        };
    }, [callWithChatAdapter]);
    const chatAdapter = useMemo(() => {
        return new CallWithChatBackedChatAdapter(callWithChatAdapter);
    }, [callWithChatAdapter]);
    /** Constant setting of id for the parent stack of the composite */
    const compositeParentDivId = useId('callWithChatCompositeParentDiv-internal');
    const closeChat = useCallback(() => {
        setIsChatOpen(false);
    }, []);
    const openChat = useCallback(() => {
        setIsChatOpen(true);
        // timeout is required to give the window time to render the sendbox so we have something to send focus to.
        // TODO: Selecting elements in the DOM via attributes is not stable. We should expose an API from ChatComposite to be able to focus on the sendbox.
        const chatFocusTimeout = setInterval(() => {
            const callWithChatCompositeRootDiv = document.querySelector(`[id="${compositeParentDivId}"]`);
            const sendbox = callWithChatCompositeRootDiv === null || callWithChatCompositeRootDiv === void 0 ? void 0 : callWithChatCompositeRootDiv.querySelector(`[id="sendbox"]`);
            if (sendbox !== null) {
                sendbox.focus();
                clearInterval(chatFocusTimeout);
            }
        }, 3);
        setTimeout(() => {
            clearInterval(chatFocusTimeout);
        }, 300);
    }, [compositeParentDivId]);
    const isOnHold = currentPage === 'hold';
    useEffect(() => {
        if (isOnHold) {
            closeChat();
        }
    }, [closeChat, isOnHold]);
    const hasJoinedCall = !!(currentPage && hasJoinedCallFn(currentPage, currentCallState !== null && currentCallState !== void 0 ? currentCallState : 'None'));
    const toggleChat = useCallback(() => {
        isChatOpen || !hasJoinedCall ? closeChat() : openChat();
    }, [closeChat, hasJoinedCall, isChatOpen, openChat]);
    const callWithChatStrings = useCallWithChatCompositeStrings();
    const chatButtonStrings = useMemo(() => ({
        label: callWithChatStrings.chatButtonLabel,
        tooltipOffContent: callWithChatStrings.chatButtonTooltipOpen,
        tooltipOnContent: callWithChatStrings.chatButtonTooltipClose
    }), [callWithChatStrings]);
    const theme = useTheme();
    const commonButtonStyles = useMemo(() => !mobileView ? getDesktopCommonButtonStyles(theme) : undefined, [mobileView, theme]);
    const showChatButton = checkShowChatButton(props.callControls);
    const chatButtonDisabled = showChatButton && (checkChatButtonIsDisabled(props.callControls) || !hasJoinedCall || isOnHold);
    const chatTabHeaderProps = useMemo(() => mobileView && showChatButton ? {
        onClick: toggleChat,
        disabled: chatButtonDisabled
    } : undefined, [chatButtonDisabled, mobileView, toggleChat, showChatButton]);
    const unreadChatMessagesCount = useUnreadMessagesTracker(chatAdapter, isChatOpen, isChatInitialized);
    const customChatButton = useCallback((args) => ({
        placement: mobileView ? 'primary' : 'secondary',
        onRenderButton: () => React.createElement(ChatButtonWithUnreadMessagesBadge, { checked: isChatOpen, showLabel: args.displayType !== 'compact', onClick: toggleChat, disabled: chatButtonDisabled, strings: chatButtonStrings, styles: commonButtonStyles, newMessageLabel: callWithChatStrings.chatButtonNewMessageNotificationLabel, unreadChatMessagesCount: unreadChatMessagesCount, 
            // As chat is disabled when on hold, we don't want to show the unread badge when on hold
            hideUnreadChatMessagesBadge: isOnHold, disableTooltip: mobileView })
    }), [callWithChatStrings.chatButtonNewMessageNotificationLabel, chatButtonStrings, commonButtonStyles, isChatOpen, chatButtonDisabled, mobileView, toggleChat, unreadChatMessagesCount, isOnHold]);
    const callControlOptionsFromProps = useMemo(() => (Object.assign({}, (typeof props.callControls === 'object' ? props.callControls : {}))), [props.callControls]);
    const injectedCustomButtonsFromProps = useMemo(() => {
        var _a;
        return [...((_a = callControlOptionsFromProps.onFetchCustomButtonProps) !== null && _a !== void 0 ? _a : [])];
    }, [callControlOptionsFromProps]);
    const callCompositeOptions = useMemo(() => ({
        callControls: props.callControls === false ? false : Object.assign(Object.assign({}, callControlOptionsFromProps), { onFetchCustomButtonProps: [...(showChatButton ? [customChatButton] : []), ...injectedCustomButtonsFromProps], legacyControlBarExperience: false }),
        remoteVideoTileMenuOptions: props.remoteVideoTileMenuOptions,
        galleryOptions: props.galleryOptions,
        localVideoTile: props.localVideoTile,
        surveyOptions: surveyOptions,
        branding: {
            logo: props.logo,
            backgroundImage: props.backgroundImage
        },
        spotlight: props.spotlight,
        joinCallOptions: props.joinCallOptions
    }), [props.callControls, callControlOptionsFromProps, showChatButton, customChatButton, injectedCustomButtonsFromProps, props.galleryOptions, props.localVideoTile, props.remoteVideoTileMenuOptions, surveyOptions, props.logo, props.backgroundImage, props.spotlight, props.joinCallOptions]);
    const chatCompositeOptions = useMemo(() => ({
        topic: false
    }), []);
    const chatSpinnerLabel = useLocale().strings.callWithChat.chatContentSpinnerLabel;
    const onRenderChatContent = useCallback(() => {
        if (!isChatInitialized) {
            return React.createElement(Stack, { styles: chatSpinnerContainerStyles },
                React.createElement(Spinner, { label: chatSpinnerLabel, size: SpinnerSize.large }));
        }
        return React.createElement(ChatComposite, { adapter: chatAdapter, fluentTheme: theme, options: chatCompositeOptions, onFetchAvatarPersonaData: props.onFetchAvatarPersonaData });
    }, [chatAdapter, props.onFetchAvatarPersonaData, chatCompositeOptions, theme, isChatInitialized, chatSpinnerLabel]);
    let chatPaneTitle = callWithChatStrings.chatPaneTitle;
    // If breakout room settings are defined then we know we are in a breakout room so we should
    // use the breakout room chat pane title.
    if ((_b = (_a = callAdapter.getState().call) === null || _a === void 0 ? void 0 : _a.breakoutRooms) === null || _b === void 0 ? void 0 : _b.breakoutRoomSettings) {
        chatPaneTitle = callWithChatStrings.breakoutRoomChatPaneTitle;
    }
    const sidePaneHeaderRenderer = useCallback(() => { var _a; return React.createElement(SidePaneHeader, { headingText: chatPaneTitle, onClose: closeChat, dismissSidePaneButtonAriaLabel: (_a = callWithChatStrings.dismissSidePaneButtonLabel) !== null && _a !== void 0 ? _a : '', mobileView: mobileView, chatButtonPresent: showChatButton }); }, [chatPaneTitle, closeChat, callWithChatStrings.dismissSidePaneButtonLabel, mobileView, showChatButton]);
    const sidePaneContentRenderer = useMemo(() => hasJoinedCall ? onRenderChatContent : undefined, [hasJoinedCall, onRenderChatContent]);
    const sidePaneRenderer = useMemo(() => ({
        contentRenderer: sidePaneContentRenderer,
        headerRenderer: sidePaneHeaderRenderer,
        id: 'chat'
    }), [sidePaneContentRenderer, sidePaneHeaderRenderer]);
    const overrideSidePaneProps = useMemo(() => ({
        renderer: sidePaneRenderer,
        isActive: isChatOpen,
        persistRenderingWhenClosed: true
    }), [isChatOpen, sidePaneRenderer]);
    const onSidePaneIdChange = useCallback((sidePaneId) => {
        // If the pane is switched to something other than chat, removing rendering chat.
        if (sidePaneId && sidePaneId !== 'chat') {
            closeChat();
        }
    }, [closeChat]);
    // When the call ends ensure the side pane is set to closed to prevent the side pane being open if the call is re-joined.
    useEffect(() => {
        callAdapter.on('callEnded', closeChat);
        return () => {
            callAdapter.off('callEnded', closeChat);
        };
    }, [callAdapter, closeChat]);
    return React.createElement("div", { ref: containerRef, className: mergeStyles(containerDivStyles) },
        React.createElement(Stack, { verticalFill: true, grow: true, styles: compositeOuterContainerStyles, id: compositeParentDivId },
            React.createElement(Stack, { horizontal: true, grow: true },
                React.createElement(Stack.Item, { grow: true, styles: callCompositeContainerStyles(mobileView) },
                    React.createElement(CallCompositeInner, Object.assign({}, props, { formFactor: formFactor, options: callCompositeOptions, adapter: callAdapter, fluentTheme: fluentTheme, callInvitationUrl: props.joinInvitationURL, overrideSidePane: overrideSidePaneProps, onSidePaneIdChange: onSidePaneIdChange, mobileChatTabHeader: chatTabHeaderProps, onCloseChatPane: closeChat }))))));
};
/**
 * CallWithChatComposite brings together key components to provide a full call with chat experience out of the box.
 *
 * @public
 */
export const CallWithChatComposite = (props) => {
    var _a, _b;
    const { adapter, fluentTheme, rtl, formFactor, joinInvitationURL, options } = props;
    return React.createElement(BaseProvider, { fluentTheme: fluentTheme, rtl: rtl, locale: props.locale, icons: props.icons, formFactor: props.formFactor },
        React.createElement(CallWithChatScreen, Object.assign({}, props, { callWithChatAdapter: adapter, formFactor: formFactor, callControls: options === null || options === void 0 ? void 0 : options.callControls, joinInvitationURL: joinInvitationURL, fluentTheme: fluentTheme, remoteVideoTileMenuOptions: options === null || options === void 0 ? void 0 : options.remoteVideoTileMenuOptions, localVideoTile: options === null || options === void 0 ? void 0 : options.localVideoTile, galleryOptions: options === null || options === void 0 ? void 0 : options.galleryOptions, logo: (_a = options === null || options === void 0 ? void 0 : options.branding) === null || _a === void 0 ? void 0 : _a.logo, backgroundImage: (_b = options === null || options === void 0 ? void 0 : options.branding) === null || _b === void 0 ? void 0 : _b.backgroundImage, surveyOptions: options === null || options === void 0 ? void 0 : options.surveyOptions, spotlight: options === null || options === void 0 ? void 0 : options.spotlight, joinCallOptions: options === null || options === void 0 ? void 0 : options.joinCallOptions })));
};
const hasJoinedCallFn = (page, callStatus) => {
    return page === 'call' && (callStatus === 'Connected' || callStatus === 'RemoteHold' || callStatus === 'Disconnecting') || page === 'hold' && (callStatus === 'LocalHold' || callStatus === 'Disconnecting');
};
const checkShowChatButton = (callControls) => {
    if (callControls === undefined || callControls === true) {
        return true;
    }
    if (callControls === false) {
        return false;
    }
    return callControls.chatButton !== false;
};
const checkChatButtonIsDisabled = (callControls) => {
    return typeof callControls === 'object' && isDisabled(callControls === null || callControls === void 0 ? void 0 : callControls.chatButton);
};
//# sourceMappingURL=CallWithChatComposite.js.map