import { useState, useRef, useEffect, useCallback } from 'react';

// ---------------------------------------------------------------------------
// Constants
// ---------------------------------------------------------------------------
const DRAG_THRESHOLD = 3; // px dead zone before drag activates
// ---------------------------------------------------------------------------
// Hook
// ---------------------------------------------------------------------------
function useGanttDrag({ dayWidth, mode, barWidthPx, originalProgress, disabled = false, onDragEnd, onDragStateChange, }) {
    const [isDragging, setIsDragging] = useState(false);
    const [dragDeltaX, setDragDeltaX] = useState(0);
    const [dragProgress, setDragProgress] = useState(originalProgress);
    const didDragRef = useRef(false);
    const startXRef = useRef(0);
    const activatedRef = useRef(false);
    const cancelledRef = useRef(false);
    const cleanupRef = useRef(null);
    // Reset didDrag after a tick so click handler can read it
    const [didDrag, setDidDrag] = useState(false);
    useEffect(() => {
        if (didDrag) {
            const timer = setTimeout(() => setDidDrag(false), 0);
            return () => clearTimeout(timer);
        }
    }, [didDrag]);
    const handlePointerDown = useCallback((e) => {
        if (disabled || e.button !== 0)
            return;
        e.preventDefault();
        e.stopPropagation();
        startXRef.current = e.clientX;
        activatedRef.current = false;
        cancelledRef.current = false;
        didDragRef.current = false;
        const onPointerMove = (ev) => {
            var _a;
            if (cancelledRef.current)
                return;
            const rawDelta = ev.clientX - startXRef.current;
            // Dead zone: don't activate drag until threshold crossed
            if (!activatedRef.current) {
                if (Math.abs(rawDelta) < DRAG_THRESHOLD)
                    return;
                activatedRef.current = true;
                didDragRef.current = true;
                setIsDragging(true);
            }
            if (mode === "progress") {
                // Progress: map delta to 0-100 range
                const progressDelta = barWidthPx > 0 ? (rawDelta / barWidthPx) * 100 : 0;
                const newProgress = Math.round(Math.max(0, Math.min(100, originalProgress + progressDelta)));
                setDragProgress(newProgress);
                setDragDeltaX(rawDelta);
            }
            else {
                // Move/resize: snap to day grid
                const snappedDelta = Math.round(rawDelta / dayWidth) * dayWidth;
                setDragDeltaX(snappedDelta);
                (_a = onDragStateChangeRef.current) === null || _a === void 0 ? void 0 : _a.call(onDragStateChangeRef, true, snappedDelta);
            }
        };
        const onPointerUp = () => {
            var _a, _b;
            document.removeEventListener("pointermove", onPointerMove);
            document.removeEventListener("pointerup", onPointerUp);
            document.removeEventListener("keydown", onKeyDown);
            cleanupRef.current = null;
            if (cancelledRef.current) {
                setIsDragging(false);
                setDragDeltaX(0);
                setDragProgress(originalProgress);
                (_a = onDragStateChangeRef.current) === null || _a === void 0 ? void 0 : _a.call(onDragStateChangeRef, false, 0);
                return;
            }
            if (activatedRef.current) {
                setDidDrag(true);
                const finalDelta = dragDeltaXRef.current;
                const daysDelta = Math.round(finalDelta / dayWidth);
                if (mode === "progress") {
                    onDragEnd({
                        mode,
                        daysDelta: 0,
                        newProgress: dragProgressRef.current,
                    });
                }
                else if (daysDelta !== 0) {
                    onDragEnd({ mode, daysDelta, newProgress: originalProgress });
                }
            }
            (_b = onDragStateChangeRef.current) === null || _b === void 0 ? void 0 : _b.call(onDragStateChangeRef, false, 0);
            setIsDragging(false);
            setDragDeltaX(0);
            setDragProgress(originalProgress);
        };
        const onKeyDown = (ev) => {
            var _a;
            if (ev.key === "Escape") {
                cancelledRef.current = true;
                setIsDragging(false);
                setDragDeltaX(0);
                setDragProgress(originalProgress);
                (_a = onDragStateChangeRef.current) === null || _a === void 0 ? void 0 : _a.call(onDragStateChangeRef, false, 0);
                document.removeEventListener("pointermove", onPointerMove);
                document.removeEventListener("pointerup", onPointerUp);
                document.removeEventListener("keydown", onKeyDown);
                cleanupRef.current = null;
            }
        };
        document.addEventListener("pointermove", onPointerMove);
        document.addEventListener("pointerup", onPointerUp);
        document.addEventListener("keydown", onKeyDown);
        // Store cleanup so useEffect can remove listeners if component unmounts mid-drag
        cleanupRef.current = () => {
            document.removeEventListener("pointermove", onPointerMove);
            document.removeEventListener("pointerup", onPointerUp);
            document.removeEventListener("keydown", onKeyDown);
            cleanupRef.current = null;
        };
    }, [disabled, dayWidth, mode, barWidthPx, originalProgress, onDragEnd]);
    // Remove dangling listeners on unmount
    useEffect(() => {
        return () => {
            var _a;
            (_a = cleanupRef.current) === null || _a === void 0 ? void 0 : _a.call(cleanupRef);
        };
    }, []);
    // Keep refs in sync with state/callbacks so closures read current values
    const dragDeltaXRef = useRef(dragDeltaX);
    dragDeltaXRef.current = dragDeltaX;
    const dragProgressRef = useRef(dragProgress);
    dragProgressRef.current = dragProgress;
    const onDragStateChangeRef = useRef(onDragStateChange);
    onDragStateChangeRef.current = onDragStateChange;
    return {
        isDragging,
        didDrag,
        dragDeltaX,
        dragProgress,
        handlePointerDown,
    };
}

export { useGanttDrag };
