import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
import React, { useRef, useEffect, useCallback, useState, forwardRef } from "react";
import { cn, useWindowSize, findParentAttribute } from "@utils";
import mergeRefs from "react-merge-refs";
import { keyboardKeys, mobileBreakPoint } from "@constants";
import { updateCursor, resetCursor, handleScrollToHelper } from "./utils";
import { scrollbarVariants, scrollbarContentVariants, scrollbarYContainerVariants, scrollbarTrackYVariants, scrollbarThumbYVariants, scrollbarXContainerVariants, scrollbarTrackXVariants, scrollbarThumbXVariants } from "./scrollbarVariants";
/**
 * Scrollbar component provides a custom scrollbar with support for both vertical and horizontal scrolling.
 * It includes features like custom styling, touch support, RTL support, and keyboard navigation.
 */
export const Scrollbar = forwardRef(({ children, as = "div", className, alwaysVisible = false, scrollToClickPosition = false, id }, ref) => {
    // Set up the element tag
    const Tag = as;
    // Refs for DOM elements
    const scrollbarContainerRef = useRef(null);
    const contentRef = useRef(null);
    const trackRefY = useRef(null);
    const thumbRefY = useRef(null);
    const scrollerContainerRefY = useRef(null);
    const trackRefX = useRef(null);
    const thumbRefX = useRef(null);
    const scrollerContainerRefX = useRef(null);
    // State for RTL and mobile detection
    const [isRtl, setIsRtl] = useState(false);
    const { width } = useWindowSize();
    const isMobile = width <= mobileBreakPoint;
    const scrollThumbTimeoutRef = useRef(null);
    // Handle the scrolling event
    const handleScrollContent = useCallback(() => {
        const contentElement = contentRef.current;
        const thumbY = thumbRefY.current;
        const thumbX = thumbRefX.current;
        const trackY = trackRefY.current;
        const trackX = trackRefX.current;
        if (!thumbY || !contentElement || !thumbX || !trackY || !trackX) {
            return;
        }
        // Calculate the scroll percentage
        const scrollPercentageY = (contentElement.scrollTop * 100) / contentElement.scrollHeight;
        const scrollPercentageX = (contentElement.scrollLeft * 100) / contentElement.scrollWidth;
        // Set the Y scrollbar thumb position
        thumbY.style.top = `${scrollPercentageY}%`;
        // if in case of rtl, behaviour of scrolling on X-Axis would be inversed
        if (isRtl) {
            const maxScrollX = contentElement.scrollWidth - contentElement.clientWidth;
            const thumbPositionX = (maxScrollX * 100) / contentElement.scrollWidth;
            thumbX.style.left = `${thumbPositionX + scrollPercentageX}%`;
        }
        else {
            thumbX.style.left = `${scrollPercentageX}%`;
        }
        if (isMobile) {
            // For mobile browsers, show the scroll thumb immediately
            thumbY.style.opacity = "1";
            thumbX.style.opacity = "1";
            trackY.style.opacity = "1";
            trackX.style.opacity = "1";
            // Clear any existing timeout
            if (scrollThumbTimeoutRef.current) {
                clearTimeout(scrollThumbTimeoutRef.current);
            }
            // Set a new timeout to hide the scroll thumb after 1 second of inactivity
            scrollThumbTimeoutRef.current = setTimeout(() => {
                thumbY.style.opacity = "0";
                thumbX.style.opacity = "0";
                trackY.style.opacity = "0";
                trackX.style.opacity = "0";
            }, 1000);
        }
    }, [isMobile, isRtl]);
    /**
    * Handle the scrollbar track click
    * When the scrollbar track is clicked, it captures the click's position and scrolls toward it.
    */
    const handleClickTrack = useCallback((event, scrollDirection) => {
        const contentElement = contentRef.current;
        const trackY = trackRefY.current;
        const thumbY = thumbRefY.current;
        const trackX = trackRefX.current;
        const thumbX = thumbRefX.current;
        if (!trackY || !thumbY || !contentElement || !trackX || !thumbX) {
            return;
        }
        if (scrollToClickPosition) {
            if (scrollDirection === "Y") {
                const boundY = trackY.getBoundingClientRect();
                const percentageY = (event.clientY - boundY.top) / boundY.height;
                const scrollHeight = contentElement.scrollHeight - contentElement.clientHeight;
                const scrollToOffsetY = percentageY * scrollHeight;
                handleScrollToHelper(contentElement, scrollToOffsetY, "top");
            }
            else if (scrollDirection === "X") {
                const boundX = trackX.getBoundingClientRect();
                let percentageX = (event.clientX - boundX.left) / boundX.width;
                if (isRtl) {
                    percentageX = (event.clientX - boundX.right) / boundX.width;
                }
                const scrollWidth = contentElement.scrollWidth - contentElement.clientWidth;
                const scrollToOffsetX = percentageX * scrollWidth;
                handleScrollToHelper(contentElement, scrollToOffsetX, "left");
            }
        }
        else {
            if (scrollDirection === "Y") {
                const boundY = trackY.getBoundingClientRect();
                const thumbBoundY = thumbY.getBoundingClientRect();
                const clickPositionY = event.clientY - boundY.top;
                const thumbMidY = thumbBoundY.top + (thumbBoundY.bottom - thumbBoundY.top) / 2;
                const scrollAmount = contentElement.clientHeight * 0.5;
                const scrollDirectionUnits = clickPositionY > thumbMidY ? scrollAmount : -scrollAmount;
                handleScrollToHelper(contentElement, contentElement.scrollTop + scrollDirectionUnits, "top");
            }
            else if (scrollDirection === "X") {
                const boundX = trackX.getBoundingClientRect();
                const thumbBoundX = thumbX.getBoundingClientRect();
                const clickPositionX = event.clientX - boundX.left;
                const thumbMidX = thumbBoundX.left + (thumbBoundX.right - thumbBoundX.left) / 2;
                const scrollAmount = contentElement.clientWidth * 0.5;
                const scrollDirectionUnits = clickPositionX > thumbMidX ? scrollAmount : -scrollAmount;
                handleScrollToHelper(contentElement, contentElement.scrollLeft + scrollDirectionUnits, "left");
            }
        }
    }, [isRtl, scrollToClickPosition]);
    /**
    * Handles the mouse down event on the scrollbar thumb to enable scrolling by dragging the thumb.
    */
    const handleMouseDown = useCallback((parentEvent, scrollDirection) => {
        const contentElement = contentRef.current;
        const thumbY = thumbRefY.current;
        const thumbX = thumbRefX.current;
        if (!thumbY || !contentElement || !thumbX) {
            return;
        }
        const startPos = {
            top: contentElement.scrollTop,
            left: contentElement.scrollLeft,
            x: parentEvent.clientX,
            y: parentEvent.clientY
        };
        const handleMouseMove = (childEvent) => {
            const scrollRatioY = contentElement.clientHeight / contentElement.scrollHeight;
            const scrollRatioX = contentElement.clientWidth / contentElement.scrollWidth;
            if (scrollDirection === "Y") {
                const differenceY = childEvent.clientY - startPos.y;
                contentElement.scrollTop = startPos.top + differenceY / scrollRatioY;
            }
            else if (scrollDirection === "X") {
                let differenceX = childEvent.clientX - startPos.x;
                if (isRtl) {
                    differenceX = startPos.x - childEvent.clientX;
                }
                if (isRtl) {
                    contentElement.scrollLeft = startPos.left - differenceX / scrollRatioX;
                }
                else {
                    contentElement.scrollLeft = startPos.left + differenceX / scrollRatioX;
                }
            }
            updateCursor();
        };
        const preventSelection = (event) => {
            event.preventDefault(); // Prevent text selection
        };
        // Helper function to remove and reset the events and cursor style
        const handleMouseUp = () => {
            document.body.removeEventListener("mousemove", handleMouseMove);
            document.body.removeEventListener("mouseup", handleMouseUp);
            document.body.removeEventListener("selectstart", preventSelection);
            resetCursor();
        };
        // Attach the events to the body on mouseMove event and when mouse is released remove
        document.body.addEventListener("mousemove", handleMouseMove);
        document.body.addEventListener("mouseup", handleMouseUp);
        document.body.addEventListener("selectstart", preventSelection);
    }, [isRtl]);
    /**
    * To handle the thumb drag for screen touch devices
    */
    const handleTouchStart = useCallback((parentEvent, scrollDirection) => {
        const contentElement = contentRef.current;
        const thumbY = thumbRefY.current;
        const thumbX = thumbRefX.current;
        if (!thumbY || !contentElement || !thumbX) {
            return;
        }
        const parentTouchArray = Array.from(parentEvent.targetTouches);
        const [parentTouch] = parentTouchArray;
        const startPos = {
            top: contentElement.scrollTop,
            left: contentElement.scrollLeft,
            x: parentTouch.clientX,
            y: parentTouch.clientY
        };
        const handleTouchMove = (childEvent) => {
            const childTouchArray = Array.from(childEvent.targetTouches);
            const [childTouch] = childTouchArray;
            const scrollRatioY = contentElement.clientHeight / contentElement.scrollHeight;
            const scrollRatioX = contentElement.clientWidth / contentElement.scrollWidth;
            if (scrollDirection === "Y") {
                const differenceY = (childTouch === null || childTouch === void 0 ? void 0 : childTouch.clientY) - startPos.y;
                contentElement.scrollTop = startPos.top + differenceY / scrollRatioY;
            }
            else if (scrollDirection === "X") {
                let differenceX = (childTouch === null || childTouch === void 0 ? void 0 : childTouch.clientX) - startPos.x;
                if (isRtl) {
                    differenceX = startPos.x - (childTouch === null || childTouch === void 0 ? void 0 : childTouch.clientX);
                }
                // The scroll behaviour is reversed if the rtl value is true/enabled
                if (isRtl) {
                    contentElement.scrollLeft = startPos.left - differenceX / scrollRatioX;
                }
                else {
                    contentElement.scrollLeft = startPos.left + differenceX / scrollRatioX;
                }
            }
            updateCursor();
        };
        // Helper function to remove and reset the events and cursor style
        const handleTouchEnd = () => {
            if (scrollDirection === "Y") {
                thumbY.removeEventListener("touchmove", handleTouchMove);
                thumbY.removeEventListener("touchend", handleTouchEnd);
            }
            else {
                thumbX.removeEventListener("touchmove", handleTouchMove);
                thumbX.removeEventListener("touchend", handleTouchEnd);
            }
            resetCursor();
        };
        // Attach the events to the Thumb Element on touchMove event and when touch is released remove
        if (scrollDirection === "Y") {
            thumbY.addEventListener("touchmove", handleTouchMove);
            thumbY.addEventListener("touchend", handleTouchEnd);
        }
        else {
            thumbX.addEventListener("touchmove", handleTouchMove);
            thumbX.addEventListener("touchend", handleTouchEnd);
        }
    }, [isRtl]);
    /**
    * Calculates the dimensions of the scrollbar thumb elements based on the content size and visibility.
    */
    const calculateThumbDimensions = useCallback(() => {
        const contentElement = contentRef.current;
        const thumbY = thumbRefY.current;
        const scrollbarContainerY = scrollerContainerRefY.current;
        const thumbX = thumbRefX.current;
        const scrollbarContainerX = scrollerContainerRefX.current;
        if (!thumbY || !contentElement || !thumbX) {
            return;
        }
        const scrollRatioY = contentElement.clientHeight / contentElement.scrollHeight;
        if (scrollRatioY >= 1) {
            scrollbarContainerY === null || scrollbarContainerY === void 0 ? void 0 : scrollbarContainerY.classList.add("invisible");
        }
        else {
            scrollbarContainerY === null || scrollbarContainerY === void 0 ? void 0 : scrollbarContainerY.classList.remove("invisible");
            thumbY.style.height = `${scrollRatioY * 100}%`;
        }
        const scrollRatioX = contentElement.clientWidth / contentElement.scrollWidth;
        if (scrollRatioX >= 1) {
            scrollbarContainerX === null || scrollbarContainerX === void 0 ? void 0 : scrollbarContainerX.classList.add("invisible");
        }
        else {
            scrollbarContainerX === null || scrollbarContainerX === void 0 ? void 0 : scrollbarContainerX.classList.remove("invisible");
            thumbX.style.width = `${scrollRatioX * 100}%`;
        }
    }, []);
    // Check for RTL direction
    useEffect(() => {
        const scrollbarContainerElement = scrollbarContainerRef.current;
        if (scrollbarContainerElement) {
            const dir = findParentAttribute(scrollbarContainerElement, "dir");
            setIsRtl(dir === "rtl");
        }
    }, [scrollbarContainerRef]);
    // Calculate initial dimensions and handle RTL positioning
    useEffect(() => {
        calculateThumbDimensions();
        const contentElement = contentRef.current;
        const thumbX = thumbRefX.current;
        if (!contentElement || !thumbX) {
            return;
        }
        if (isRtl) {
            const maxScrollX = contentElement.scrollWidth - contentElement.clientWidth;
            const thumbPositionX = (maxScrollX * 100) / contentElement.scrollWidth;
            thumbX.style.left = `${thumbPositionX}%`;
        }
    }, [calculateThumbDimensions, isRtl]);
    // Set up observers and keyboard event handling
    useEffect(() => {
        const mutationObserver = new MutationObserver(calculateThumbDimensions);
        const resizeObserver = new ResizeObserver(calculateThumbDimensions);
        if (contentRef.current) {
            mutationObserver.observe(contentRef.current, { childList: true, subtree: true });
            resizeObserver.observe(contentRef.current);
        }
        const scrollbarContainerElement = scrollbarContainerRef.current;
        const handleKeyDown = (event) => {
            const { key } = event;
            const contentElement = contentRef.current;
            if (!contentElement) {
                return;
            }
            const scrollStep = 50;
            switch (key) {
                case keyboardKeys.arrowUp: {
                    handleScrollToHelper(contentElement, contentElement.scrollTop - scrollStep, "top");
                    break;
                }
                case keyboardKeys.arrowDown: {
                    handleScrollToHelper(contentElement, contentElement.scrollTop + scrollStep, "top");
                    break;
                }
                case keyboardKeys.arrowLeft: {
                    handleScrollToHelper(contentElement, contentElement.scrollLeft - scrollStep, "left");
                    break;
                }
                case keyboardKeys.arrowRight: {
                    handleScrollToHelper(contentElement, contentElement.scrollLeft + scrollStep, "left");
                    break;
                }
                case keyboardKeys.pageUp: {
                    handleScrollToHelper(contentElement, contentElement.scrollTop - contentElement.clientHeight, "top");
                    break;
                }
                case keyboardKeys.pageDown: {
                    handleScrollToHelper(contentElement, contentElement.scrollTop + contentElement.clientHeight, "top");
                    break;
                }
                case keyboardKeys.home: {
                    handleScrollToHelper(contentElement, 0, "top");
                    break;
                }
                case keyboardKeys.end: {
                    handleScrollToHelper(contentElement, contentElement.scrollHeight, "top");
                    break;
                }
                default:
                    break;
            }
        };
        if (scrollbarContainerElement) {
            scrollbarContainerElement.addEventListener("keydown", handleKeyDown);
        }
        return () => {
            mutationObserver.disconnect();
            resizeObserver.disconnect();
            if (scrollbarContainerElement) {
                scrollbarContainerElement.removeEventListener("keydown", handleKeyDown);
            }
        };
    }, [calculateThumbDimensions]);
    const visibility = alwaysVisible ? "alwaysVisible" : "default";
    return (_jsxs(Tag, { id: id, "data-testid": "scrollbar", className: cn(scrollbarVariants({ visibility }), className), ref: mergeRefs([ref, scrollbarContainerRef]), children: [_jsx("div", { className: cn(scrollbarContentVariants()), ref: contentRef, onScroll: handleScrollContent, children: children }), _jsxs("div", { ref: scrollerContainerRefY, "data-testid": "scrollbar-y", className: cn(scrollbarYContainerVariants({ visibility })), children: [_jsx("div", { "data-testid": "scrollbar-track-y", className: cn(scrollbarTrackYVariants({ visibility })), ref: trackRefY, onMouseDown: (event) => handleClickTrack(event, "Y") }), _jsx("div", { "data-testid": "scrollbar-thumb-y", className: cn(scrollbarThumbYVariants({ visibility })), ref: thumbRefY, onMouseDown: (event) => handleMouseDown(event, "Y"), onTouchStart: (event) => handleTouchStart(event, "Y") })] }), _jsxs("div", { "data-testid": "scrollbar-x", ref: scrollerContainerRefX, className: cn(scrollbarXContainerVariants({ visibility })), children: [_jsx("div", { className: cn(scrollbarTrackXVariants({ visibility })), ref: trackRefX, onMouseDown: (event) => handleClickTrack(event, "X") }), _jsx("div", { "data-testid": "scrollbar-thumb-x", className: cn(scrollbarThumbXVariants({ visibility })), ref: thumbRefX, onMouseDown: (event) => handleMouseDown(event, "X"), onTouchStart: (event) => handleTouchStart(event, "X") })] })] }));
});
Scrollbar.displayName = "Scrollbar";
//# sourceMappingURL=scrollbar.js.map