var __rest = (this && this.__rest) || function (s, e) {
    var t = {};
    for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
        t[p] = s[p];
    if (s != null && typeof Object.getOwnPropertySymbols === "function")
        for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {
            if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i]))
                t[p[i]] = s[p[i]];
        }
    return t;
};
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
// ============================================================================
// Chatbot Main - Messages
// ============================================================================
import { useState, useRef, useCallback, useEffect, forwardRef, useImperativeHandle } from 'react';
import JumpButton from './JumpButton';
export const MessageBox = forwardRef((_a, ref) => {
    var { announcement, ariaLabel = 'Scrollable message log', children, className, position = 'top', onScrollToTopClick, onScrollToBottomClick, enableSmartScroll = false, jumpButtonTopProps, jumpButtonBottomProps, jumpButtonBottomTooltipProps, jumpButtonTopTooltipProps } = _a, props = __rest(_a, ["announcement", "ariaLabel", "children", "className", "position", "onScrollToTopClick", "onScrollToBottomClick", "enableSmartScroll", "jumpButtonTopProps", "jumpButtonBottomProps", "jumpButtonBottomTooltipProps", "jumpButtonTopTooltipProps"]);
    const [atTop, setAtTop] = useState(false);
    const [atBottom, setAtBottom] = useState(true);
    const [isOverflowing, setIsOverflowing] = useState(false);
    const [autoScroll, setAutoScroll] = useState(true);
    const lastScrollTop = useRef(0);
    const animationFrame = useRef(null);
    const debounceTimeout = useRef(null);
    const pauseAutoScrollRef = useRef(false);
    const messageBoxRef = useRef(null);
    const scrollQueued = useRef(false);
    const resetUserScrollIntentTimeout = useRef();
    // Configure handlers
    const handleScroll = useCallback(() => {
        const element = messageBoxRef.current;
        if (!element) {
            return;
        }
        const { scrollTop, scrollHeight, clientHeight } = element;
        const roundedScrollTop = Math.round(scrollTop);
        const roundedClientHeight = Math.round(clientHeight);
        const roundedScrollHeight = Math.round(scrollHeight);
        const distanceFromBottom = roundedScrollHeight - roundedScrollTop - roundedClientHeight;
        const isScrollingDown = roundedScrollTop > lastScrollTop.current;
        const DELTA_UP = 10;
        const DELTA_DOWN = 50;
        const DEBOUNCE_DELAY = 200;
        const delta = isScrollingDown ? DELTA_DOWN : DELTA_UP;
        const isAtBottom = distanceFromBottom <= delta;
        setAtTop(roundedScrollTop === 0);
        setAtBottom(roundedScrollTop + roundedClientHeight >= roundedScrollHeight - 1); // rounding means it could be within a pixel of the bottom
        if (!enableSmartScroll || scrollQueued.current) {
            return;
        }
        if (roundedScrollTop === 0) {
            pauseAutoScrollRef.current = false;
        }
        if (debounceTimeout.current) {
            clearTimeout(debounceTimeout.current);
        }
        if (!isAtBottom && !pauseAutoScrollRef.current) {
            setAutoScroll(false);
        }
        // User is near bottom and scrolling down - debounce re-enabling auto-scroll
        if (isAtBottom && isScrollingDown && !pauseAutoScrollRef.current) {
            debounceTimeout.current = setTimeout(() => {
                setAutoScroll(true);
            }, DEBOUNCE_DELAY);
        }
        lastScrollTop.current = roundedScrollTop;
    }, [messageBoxRef]);
    const checkOverflow = useCallback(() => {
        const element = messageBoxRef.current;
        if (element) {
            const { scrollHeight, clientHeight } = element;
            setIsOverflowing(scrollHeight >= clientHeight);
        }
    }, [messageBoxRef]);
    const resumeAutoScroll = useCallback(() => {
        if (!enableSmartScroll) {
            return;
        }
        pauseAutoScrollRef.current = false;
        setAutoScroll(true);
    }, [enableSmartScroll]);
    const pauseAutoScroll = useCallback(() => {
        if (!enableSmartScroll) {
            return;
        }
        pauseAutoScrollRef.current = true;
        setAutoScroll(false);
    }, [enableSmartScroll]);
    /**
     * Scrolls to the top of the message box.
     *
     */
    const scrollToTop = useCallback((options) => {
        const { behavior = 'smooth' } = options || {};
        const element = messageBoxRef.current;
        if (!element || scrollQueued.current) {
            return;
        }
        scrollQueued.current = true;
        pauseAutoScroll();
        if (animationFrame.current) {
            cancelAnimationFrame(animationFrame.current);
            animationFrame.current = null;
        }
        animationFrame.current = requestAnimationFrame(() => {
            element.scrollTo({ top: 0, behavior });
            scrollQueued.current = false;
        });
        onScrollToTopClick && onScrollToTopClick();
    }, [messageBoxRef]);
    /**
     * Scrolls to the bottom of the message box.
     *
     * @param options.resumeSmartScroll - If true, resumes smart scroll behavior;
     *                                    if false or omitted, scrolls without resuming auto-scroll.
     * @param options.scrollOptions - Additional scroll options. behavior can be 'smooth' or 'auto'.
     */
    const scrollToBottom = useCallback((options) => {
        const { behavior = 'smooth', resumeSmartScroll = false } = options || {};
        resumeSmartScroll && resumeAutoScroll();
        const element = messageBoxRef.current;
        if (!element || pauseAutoScrollRef.current || scrollQueued.current) {
            return;
        }
        scrollQueued.current = true;
        if (animationFrame.current) {
            cancelAnimationFrame(animationFrame.current);
        }
        animationFrame.current = requestAnimationFrame(() => {
            element.scrollTo({ top: element.scrollHeight, behavior });
            resumeAutoScroll();
            scrollQueued.current = false;
        });
        onScrollToBottomClick && onScrollToBottomClick();
    }, [messageBoxRef, enableSmartScroll]);
    // Detect scroll position
    useEffect(() => {
        const element = messageBoxRef.current;
        if (!element) {
            return;
        }
        // Listen for scroll events
        element.addEventListener('scroll', handleScroll);
        // Check initial position and overflow
        handleScroll();
        checkOverflow();
        return () => {
            element.removeEventListener('scroll', handleScroll);
        };
    }, [checkOverflow, handleScroll, messageBoxRef]);
    useImperativeHandle(ref, () => {
        const node = messageBoxRef.current;
        // Attach custom methods to the element
        node.scrollToTop = scrollToTop;
        node.scrollToBottom = scrollToBottom;
        node.isSmartScrollActive = () => enableSmartScroll && autoScroll;
        return node;
    });
    let lastTouchY = null;
    const onTouchEnd = (event) => {
        lastTouchY = null;
        props.onTouchEnd && props.onTouchEnd(event);
    };
    const handleUserScroll = (isScrollingDown) => {
        const container = messageBoxRef.current;
        if (!enableSmartScroll || !container) {
            return;
        }
        if (!isScrollingDown) {
            pauseAutoScrollRef.current = true;
            clearTimeout(resetUserScrollIntentTimeout.current);
            return;
        }
        const { scrollTop, scrollHeight, clientHeight } = container;
        const distanceFromBottom = scrollHeight - scrollTop - clientHeight;
        if (distanceFromBottom < 100) {
            pauseAutoScrollRef.current = false;
            setAutoScroll(true);
        }
    };
    const onWheel = (event) => {
        const isScrollingDown = event.deltaY > 0;
        handleUserScroll(isScrollingDown);
        props.onWheel && props.onWheel(event);
    };
    const onTouchMove = (event) => {
        const currentTouchY = event.touches[0].clientY;
        let isScrollingDown = false;
        if (lastTouchY !== null) {
            isScrollingDown = currentTouchY < lastTouchY;
        }
        lastTouchY = currentTouchY;
        handleUserScroll(isScrollingDown);
        props.onTouchMove && props.onTouchMove(event);
    };
    const smartScrollHandlers = {
        onWheel,
        onTouchMove,
        onTouchEnd
    };
    return (_jsxs(_Fragment, { children: [_jsx(JumpButton, { position: "top", isHidden: isOverflowing && atTop, onClick: scrollToTop, jumpButtonProps: jumpButtonTopProps, jumpButtonTooltipProps: jumpButtonTopTooltipProps }), _jsxs("div", Object.assign({ role: "region", tabIndex: 0, "aria-label": ariaLabel, className: `pf-chatbot__messagebox ${position === 'bottom' ? 'pf-chatbot__messagebox--bottom' : ''} ${className !== null && className !== void 0 ? className : ''}`, ref: messageBoxRef }, props, (enableSmartScroll ? Object.assign({}, smartScrollHandlers) : {}), { children: [children, _jsx("div", { className: "pf-chatbot__messagebox-announcement pf-chatbot-m-hidden", "aria-live": "polite", children: announcement })] })), _jsx(JumpButton, { position: "bottom", isHidden: isOverflowing && atBottom, onClick: () => scrollToBottom({ resumeSmartScroll: true }), jumpButtonProps: jumpButtonBottomProps, jumpButtonTooltipProps: jumpButtonBottomTooltipProps })] }));
});
export default MessageBox;
