import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
import { useState, useRef, useCallback, useEffect } from "react";
import styles from "./ContinuousRangeComponent.module.scss";
import clsx from "clsx";
const ContinuousRangeComponent = ({ minValue, maxValue, getRange, initialMinValue = 0, initialMaxValue = 10001, disabled = false, displayLimit = 10000, }) => {
    const clampedMin = Math.max(initialMinValue, Math.min(minValue, initialMaxValue));
    const clampedMax = Math.max(initialMinValue, Math.min(maxValue, initialMaxValue));
    // Ensure minimum gap of 1 between initial values
    const adjustedMin = clampedMin;
    const adjustedMax = Math.max(clampedMax, clampedMin + 1);
    const [minSliderValue, setMinSliderValue] = useState(adjustedMin);
    const [maxSliderValue, setMaxSliderValue] = useState(adjustedMax);
    const [isDragging, setIsDragging] = useState(null);
    const [isAnimating, setIsAnimating] = useState(false); // For smooth track click transitions
    const [, forceUpdate] = useState({}); // For forcing re-renders during dragging
    const sliderRef = useRef(null);
    const isSliderMoving = useRef(false);
    const isDraggingRef = useRef(false);
    const animationFrameRef = useRef(null);
    const lastUpdateTimeRef = useRef(0);
    const animationTimeoutRef = useRef(null);
    // Track visual positions for smooth active track rendering during drag
    const visualMinValueRef = useRef(adjustedMin);
    const visualMaxValueRef = useRef(adjustedMax);
    // Throttling constants for smooth movement
    const UPDATE_THROTTLE_MS = 32; // ~30fps for controlled dragging
    const compositeClassNames = clsx({
        [styles.discreteSliderGeneric]: true,
        [styles.discreteSliderDisabled]: disabled,
    });
    useEffect(() => {
        if (!isSliderMoving.current) {
            const newMinValue = Math.max(0, Math.max(initialMinValue, Math.min(minValue, initialMaxValue)));
            const newMaxValue = Math.max(0, Math.max(initialMinValue, Math.min(maxValue, initialMaxValue)));
            // Ensure minimum gap of 1 between thumbs
            if (newMaxValue - newMinValue < 1) {
                // If values are too close, adjust them to maintain minimum gap
                const midPoint = (newMinValue + newMaxValue) / 2;
                const adjustedMin = Math.max(0, Math.floor(midPoint - 0.5));
                const adjustedMax = Math.min(initialMaxValue, Math.ceil(midPoint + 0.5));
                setMinSliderValue(adjustedMin);
                setMaxSliderValue(adjustedMax);
                visualMinValueRef.current = adjustedMin;
                visualMaxValueRef.current = adjustedMax;
            }
            else {
                setMinSliderValue(newMinValue);
                setMaxSliderValue(newMaxValue);
                visualMinValueRef.current = newMinValue;
                visualMaxValueRef.current = newMaxValue;
            }
        }
    }, [minValue, maxValue, initialMinValue, initialMaxValue]);
    const formatLabel = (value) => {
        if (disabled)
            return "";
        return `${value.toLocaleString()}`;
    };
    const getPercentage = (value) => {
        return (((value - initialMinValue) / (initialMaxValue - initialMinValue)) * 100);
    };
    const getValueFromPercentage = (percentage) => {
        return Math.round(initialMinValue + (percentage / 100) * (initialMaxValue - initialMinValue));
    };
    // Function to determine which thumb is closest to the clicked position
    const getClosestThumb = (value) => {
        const distanceToMin = Math.abs(value - minSliderValue);
        const distanceToMax = Math.abs(value - maxSliderValue);
        return distanceToMin <= distanceToMax ? "min" : "max";
    };
    // Handle track click to move closest thumb to clicked position with smooth animation
    const handleTrackClick = useCallback((e) => {
        if (disabled || isDragging)
            return;
        // Prevent event from bubbling to avoid conflicts with thumb events
        e.preventDefault();
        e.stopPropagation();
        if (!sliderRef.current)
            return;
        const rect = sliderRef.current.getBoundingClientRect();
        const percentage = Math.max(0, Math.min(100, ((e.clientX - rect.left) / rect.width) * 100));
        const value = getValueFromPercentage(percentage);
        // Determine which thumb is closest and move it
        const closestThumb = getClosestThumb(value);
        // Enable smooth transition animation
        setIsAnimating(true);
        // Clear any existing animation timeout
        if (animationTimeoutRef.current) {
            clearTimeout(animationTimeoutRef.current);
        }
        if (closestThumb === "min") {
            // Ensure minimum gap of 1 between thumbs
            const newMinValue = Math.max(0, Math.min(value, maxSliderValue - 1));
            if (newMinValue !== minSliderValue) {
                setMinSliderValue(newMinValue);
                visualMinValueRef.current = newMinValue;
                getRange({
                    minRange: newMinValue,
                    maxRange: Math.min(displayLimit, Math.max(0, maxSliderValue)),
                });
            }
        }
        else {
            // Ensure minimum gap of 1 between thumbs
            const newMaxValue = Math.min(displayLimit, Math.max(minSliderValue + 1, Math.max(value, minSliderValue)));
            if (newMaxValue !== maxSliderValue) {
                setMaxSliderValue(newMaxValue);
                visualMaxValueRef.current = newMaxValue;
                getRange({
                    minRange: Math.max(0, minSliderValue),
                    maxRange: newMaxValue,
                });
            }
        }
        // Disable animation after transition completes (300ms to match CSS transition)
        animationTimeoutRef.current = window.setTimeout(() => {
            setIsAnimating(false);
        }, 300);
    }, [
        disabled,
        isDragging,
        minSliderValue,
        maxSliderValue,
        displayLimit,
        getRange,
    ]);
    const handleMouseDown = useCallback((type) => (e) => {
        if (disabled)
            return;
        e.preventDefault();
        e.stopPropagation(); // Prevent track click when clicking on thumb
        // Clear any pending animation timeout and disable animation when starting drag
        if (animationTimeoutRef.current) {
            clearTimeout(animationTimeoutRef.current);
        }
        setIsAnimating(false);
        // Initialize visual refs with current state values
        visualMinValueRef.current = minSliderValue;
        visualMaxValueRef.current = maxSliderValue;
        setIsDragging(type);
        isSliderMoving.current = true;
        isDraggingRef.current = true;
    }, [disabled, minSliderValue, maxSliderValue]);
    const handleMouseMove = useCallback((e) => {
        if (!isDragging ||
            !sliderRef.current ||
            disabled ||
            !isDraggingRef.current)
            return;
        // Cancel any pending animation frame to prevent stacking
        if (animationFrameRef.current) {
            cancelAnimationFrame(animationFrameRef.current);
        }
        // Throttle updates similar to react-input-range approach
        const now = Date.now();
        const timeDiff = now - lastUpdateTimeRef.current;
        if (timeDiff < UPDATE_THROTTLE_MS) {
            // Schedule update for next frame if we're updating too frequently
            animationFrameRef.current = requestAnimationFrame(() => {
                handleMouseMove(e);
            });
            return;
        }
        // Update last time here to ensure consistent throttling
        lastUpdateTimeRef.current = now;
        // Use requestAnimationFrame for smooth updates (similar to react-input-range)
        animationFrameRef.current = requestAnimationFrame(() => {
            if (!sliderRef.current || !isDraggingRef.current)
                return;
            const rect = sliderRef.current.getBoundingClientRect();
            // Get clientX from either mouse or touch event
            const clientX = 'touches' in e ? e.touches[0]?.clientX : e.clientX;
            if (clientX === undefined)
                return;
            const percentage = Math.max(0, Math.min(100, ((clientX - rect.left) / rect.width) * 100));
            const value = getValueFromPercentage(percentage);
            if (isDragging === "min") {
                // Ensure minimum gap of 1 between thumbs
                const newMinValue = Math.max(0, Math.min(value, maxSliderValue - 1));
                // Update visual ref for smooth active track rendering
                visualMinValueRef.current = Math.max(0, Math.min(value, visualMaxValueRef.current - 1));
                // Force re-render for smooth visual updates
                forceUpdate({});
                // Update state for callback when value actually changes
                if (newMinValue !== minSliderValue) {
                    setMinSliderValue(newMinValue);
                    getRange({
                        minRange: newMinValue,
                        maxRange: Math.min(displayLimit, Math.max(0, maxSliderValue)),
                    });
                }
            }
            else if (isDragging === "max") {
                // Ensure minimum gap of 1 between thumbs
                const newMaxValue = Math.min(displayLimit, Math.max(minSliderValue + 1, Math.max(value, minSliderValue)));
                // Update visual ref for smooth active track rendering
                visualMaxValueRef.current = Math.min(displayLimit, Math.max(visualMinValueRef.current + 1, Math.max(value, visualMinValueRef.current)));
                // Force re-render for smooth visual updates
                forceUpdate({});
                // Update state for callback when value actually changes
                if (newMaxValue !== maxSliderValue) {
                    setMaxSliderValue(newMaxValue);
                    getRange({
                        minRange: Math.max(0, minSliderValue),
                        maxRange: newMaxValue,
                    });
                }
            }
        });
    }, [
        isDragging,
        maxSliderValue,
        minSliderValue,
        disabled,
        getRange,
        displayLimit,
    ]);
    const handleMouseUp = useCallback(() => {
        if (isDragging) {
            isSliderMoving.current = false;
        }
        // Clean up any pending animation frames
        if (animationFrameRef.current) {
            cancelAnimationFrame(animationFrameRef.current);
            animationFrameRef.current = null;
        }
        setIsDragging(null);
        isDraggingRef.current = false;
        // Sync visual refs with final state values
        visualMinValueRef.current = minSliderValue;
        visualMaxValueRef.current = maxSliderValue;
    }, [isDragging, minSliderValue, maxSliderValue]);
    useEffect(() => {
        if (isDragging) {
            document.addEventListener("mousemove", handleMouseMove);
            document.addEventListener("mouseup", handleMouseUp);
            document.addEventListener("touchmove", handleMouseMove, { passive: false });
            document.addEventListener("touchend", handleMouseUp);
            return () => {
                document.removeEventListener("mousemove", handleMouseMove);
                document.removeEventListener("mouseup", handleMouseUp);
                document.removeEventListener("touchmove", handleMouseMove);
                document.removeEventListener("touchend", handleMouseUp);
                // Clean up any pending animation frames when effect unmounts
                if (animationFrameRef.current) {
                    cancelAnimationFrame(animationFrameRef.current);
                    animationFrameRef.current = null;
                }
            };
        }
    }, [isDragging, handleMouseMove, handleMouseUp]);
    // Cleanup animation frames on component unmount
    useEffect(() => {
        return () => {
            if (animationFrameRef.current) {
                cancelAnimationFrame(animationFrameRef.current);
            }
            if (animationTimeoutRef.current) {
                clearTimeout(animationTimeoutRef.current);
            }
        };
    }, []);
    // Use visual refs during dragging for smooth active track, otherwise use state values
    const currentMinValue = isDragging
        ? visualMinValueRef.current
        : minSliderValue;
    const currentMaxValue = isDragging
        ? visualMaxValueRef.current
        : maxSliderValue;
    const minPercentage = getPercentage(currentMinValue);
    const maxPercentage = getPercentage(currentMaxValue);
    return (_jsx("span", { className: compositeClassNames, children: _jsxs("div", { className: `${styles["range-slider-container"]} ${isDragging ? styles.dragging : ""}`, ref: sliderRef, onClick: handleTrackClick, children: [_jsx("div", { className: styles["range-slider-track"], children: _jsx("div", { className: styles["range-slider-track-active"], style: {
                            left: `${minPercentage}%`,
                            width: `${maxPercentage - minPercentage}%`,
                        } }) }), _jsx("div", { className: `${styles["range-slider-thumb"]} ${styles["range-slider-thumb--min"]} ${isDragging === "min" ? styles.dragging : ""} ${isAnimating ? styles.animating : ""}`, style: { left: `${minPercentage}%` }, onMouseDown: handleMouseDown("min"), onTouchStart: handleMouseDown("min"), children: _jsx("div", { className: styles["range-slider-tooltip"], children: formatLabel(Math.round(currentMinValue)) }) }), _jsx("div", { className: `${styles["range-slider-thumb"]} ${styles["range-slider-thumb--max"]} ${isDragging === "max" ? styles.dragging : ""} ${isAnimating ? styles.animating : ""}`, style: { left: `${maxPercentage}%` }, onMouseDown: handleMouseDown("max"), onTouchStart: handleMouseDown("max"), children: _jsx("div", { className: styles["range-slider-tooltip"], children: formatLabel(Math.round(currentMaxValue)) }) })] }) }));
};
export default ContinuousRangeComponent;
