import React, { useEffect, useLayoutEffect, useRef, useState } from "react";
import useNotifications, { useNotificationUI, moveToToolbarMonitor, getNotificationConfig, } from "../../../../hooks/useNotifications";
import { useTitle } from "../../../../hooks";
import { NotificationCardShell } from "../shared/NotificationCardShell";
import { CSSTransition } from "react-transition-group";
import "../../css/notifications-toasts.css";
import { FEAGlobals } from "../../../../../common";
import { getNotificationsPreferences } from "../../../userPreferences/components/content/notificationViews/notificationViewsUtils";
import { setOpaqueClassName } from "../notificationsCenter/NotificationCenterUtils";
const ToastCard = (props) => {
    const { notification, informationalNotificationTimeout, removeFunction, isActiveFunction, actionableTimeoutEnabled } = props;
    const notificationRef = useRef(notification);
    const [slideIn, setSlideIn] = useState(isActiveFunction(notification));
    const [showNotification] = useState(isActiveFunction(notification));
    const [timeoutId, setTimeoutId] = useState();
    useEffect(() => {
        var _a;
        if (timeoutId) {
            clearTimeout(timeoutId);
        }
        let timeoutLength;
        // Timeout on the notification takes precedence
        if (notification.timeout && Number.isInteger(notification.timeout)) {
            timeoutLength = notification.timeout;
        }
        /**
         * If timeout length is not already set and there informational timeout from the config exists
         * AND it's an informational notification or the config indicates actionable notifications should timeout
         */
        if (!timeoutLength &&
            informationalNotificationTimeout !== false &&
            (!notification.actions || notification.actions.length == 0 || actionableTimeoutEnabled)) {
            // Convert to milliseconds
            timeoutLength = informationalNotificationTimeout * 1000;
        }
        let timeout;
        if (timeoutLength) {
            timeout = setTimeout(() => {
                setSlideIn(false);
            }, timeoutLength);
            // Allow node based mocha tests to exit without waiting for timeouts to complete
            (_a = timeout.unref) === null || _a === void 0 ? void 0 : _a.call(timeout);
            setTimeoutId(timeout);
        }
        if (!isActiveFunction(notificationRef.current)) {
            removeFunction(notification);
        }
        setSlideIn(isActiveFunction(notification));
        if (timeoutId) {
            clearTimeout(timeoutId);
        }
        return () => {
            if (timeout) {
                clearTimeout(timeout);
            }
        };
    }, [notification]);
    const onExited = () => {
        removeFunction(notification);
    };
    const Card = props.notificationCard ? props.notificationCard : NotificationCardShell;
    return (showNotification && (React.createElement(CSSTransition, { in: slideIn, timeout: 500, onExited: onExited, classNames: "notifications-toast" },
        React.createElement("div", { className: "notifications-toast__offscreen" },
            React.createElement(Card, { notification: notification, uiContext: "toasts", removeNotification: () => {
                    setSlideIn(false);
                } }),
            React.createElement("div", { className: "divider" })))));
};
export const NotificationsToasts = (props) => {
    var _a;
    const { notifications, notificationIsActive, removeNotification, activeNotifications } = useNotifications({
        config: { notificationsHistory: { options: { sentAfter: "startup" } } },
    });
    const config = getNotificationConfig();
    const [isInitialized, setIsInitialized] = useState(false);
    const [isCorrectMonitor, setIsCorrectMonitor] = useState(false);
    const [isBoundsSet, setIsBoundsSet] = useState(false);
    const [isWindowShowing, setIsWindowShowing] = useState(false);
    const [closeTimeout, setCloseTimeout] = useState(null);
    const [uiState] = useNotificationUI();
    const isNotificationActive = (notification) => {
        const notificationCenterNotActive = !uiState.showCenter || !uiState.isDocked;
        return notificationIsActive(notification) && notificationCenterNotActive;
    };
    const [informationalTimeout, setInformationalTimeout] = useState(false);
    const [actionableTimeoutEnabled, setActionableTimeoutEnabled] = useState(true);
    useTitle((_a = props.title) !== null && _a !== void 0 ? _a : "Notification");
    /**
     * Get initial config
     */
    useEffect(() => {
        document.body.classList.add("notifications-toasts-body");
        setOpaqueClassName(!!config.isTransparent);
        getNotificationsPreferences().then((preferences) => {
            setInformationalTimeout(preferences.informationalNotificationToastPeriodSeconds);
            setActionableTimeoutEnabled(preferences.toastTimeoutOnActionableNotifications);
            setIsInitialized(true);
        });
        return () => {
            document.body.classList.remove("notifications-toasts-body");
        };
    }, []);
    /**
     * Listen for changing config
     */
    useEffect(() => {
        async function listenForPreferenceChange(error, response) {
            if (error) {
                console.error("Error getting preference change: ", error);
                FEAGlobals.FSBL.Clients.Logger.system.error("Error getting preference change: ", error);
            }
            else {
                const { data } = response;
                if (data.notificationPreferences) {
                    const { informationalNotificationToastPeriodSeconds, toastTimeoutOnActionableNotifications } = data.notificationPreferences;
                    if (informationalNotificationToastPeriodSeconds) {
                        setInformationalTimeout(informationalNotificationToastPeriodSeconds);
                    }
                    if (typeof toastTimeoutOnActionableNotifications !== "undefined") {
                        setActionableTimeoutEnabled(toastTimeoutOnActionableNotifications);
                    }
                }
            }
        }
        FEAGlobals.FSBL.Clients.RouterClient.addListener("FinsembleUserPreferencesChannel", listenForPreferenceChange);
        return () => {
            FEAGlobals.FSBL.Clients.RouterClient.removeListener("FinsembleUserPreferencesChannel", listenForPreferenceChange);
        };
    }, []);
    /**
     * Window level hook to move the window to the correct screen
     * Monitor does not change position while toasts are showing. Only corrects position when no toasts show
     */
    useEffect(() => {
        if (!isCorrectMonitor && activeNotifications(notifications).length > 0) {
            moveToToolbarMonitor().then(() => {
                setIsCorrectMonitor(true);
            });
        }
    }, [notifications]);
    /**
     * Window level hook to resize the window
     *
     * Window is on the correct screen and some notifications should be rendered
     * on the page. Resize the window to fit the content
     */
    useLayoutEffect(() => {
        const resizeWindow = async () => {
            var _a, _b;
            const rect = document.getElementsByTagName("body")[0].getBoundingClientRect();
            const { err, data } = await FEAGlobals.FSBL.Clients.LauncherClient.getMonitorInfo({ monitor: "mine" });
            if (err || !data) {
                FEAGlobals.FSBL.Clients.Logger.system.error("Error getting monitor info: ", err);
                console.error("Error getting monitor info: ", err);
                return;
            }
            const { data: bounds } = await FEAGlobals.finsembleWindow.getBounds();
            const width = ((_a = bounds === null || bounds === void 0 ? void 0 : bounds.right) !== null && _a !== void 0 ? _a : 0) - ((_b = bounds === null || bounds === void 0 ? void 0 : bounds.left) !== null && _b !== void 0 ? _b : 0);
            // Does not exceed monitor height
            const height = Math.min(Math.round(rect === null || rect === void 0 ? void 0 : rect.height) + 16, data["availableRect"]["height"]);
            await FEAGlobals.finsembleWindow.setBounds({
                bounds: {
                    top: data["availableRect"]["top"],
                    left: data["availableRect"]["right"] - width,
                    height: height,
                    width: width,
                },
            });
            setIsBoundsSet(true);
        };
        if (isCorrectMonitor && activeNotifications(notifications).length > 0) {
            resizeWindow();
        }
    }, [isCorrectMonitor, notifications]);
    /**
     * Window Level Hook to show and hide the window.
     * The window position and size manipulations have been completed show / hide it.
     */
    useEffect(() => {
        if (!isWindowShowing) {
            /**
             * IF the window is not showing
             * AND the window has been moved to the correct monitor
             * AND The correct initial bounds have been set (prevents flashing)
             * AND there are some notifications
             * THEN show the window
             */
            if (isCorrectMonitor && isBoundsSet && activeNotifications(notifications).length > 0) {
                setIsWindowShowing(true);
                FEAGlobals.finsembleWindow.show();
            }
        }
        else {
            // Hide the window if it is showing and there are 0 notifications in the list
            if (activeNotifications(notifications).length === 0) {
                if (!closeTimeout) {
                    const timerId = setTimeout(() => {
                        // Toasts slide out so delay hiding the window until after the toasts are gone
                        setIsWindowShowing(false);
                        setIsCorrectMonitor(false);
                        setIsBoundsSet(false);
                        setCloseTimeout(null);
                        FEAGlobals.finsembleWindow.hide();
                    }, 400);
                    setCloseTimeout(timerId);
                }
            }
            else {
                if (closeTimeout) {
                    // If new toasts have come in before the window has been hidden cancel hiding it.
                    clearTimeout(closeTimeout);
                    setCloseTimeout(null);
                }
            }
        }
    }, [isCorrectMonitor, isBoundsSet, notifications]);
    return (React.createElement("div", null, isInitialized &&
        isCorrectMonitor &&
        notifications.map((notification) => (React.createElement(ToastCard, { key: `toast-card-${notification.id}`, notification: notification, informationalNotificationTimeout: informationalTimeout, actionableTimeoutEnabled: actionableTimeoutEnabled, removeFunction: removeNotification, isActiveFunction: isNotificationActive, ...props })))));
};
//# sourceMappingURL=NotificationsToasts.js.map