import { useEffect, useRef } from 'react';

/** Distance parcourue avant qu'un appui devienne un glissement, en pixels. */
const DRAG_THRESHOLD = 4;

/**
 * Rend une zone défilable horizontalement au glisser-déposer, en plus de la
 * molette et de la barre de défilement.
 *
 * Le ref se pose sur un conteneur ; le vrai élément défilant est cherché à
 * l'intérieur via `selector` (les tables shadcn créent leur propre wrapper
 * `[data-slot="table-container"]`, hors de notre portée).
 *
 * Trois précautions, sans lesquelles le glisser casse le tableau :
 *   - un appui sur un bouton, un lien ou un champ n'amorce jamais un glissement,
 *     sinon les actions de ligne deviennent inutilisables ;
 *   - le glissement ne commence qu'au-delà de DRAG_THRESHOLD, pour qu'un clic
 *     avec un micro-tremblement reste un clic ;
 *   - la sélection de texte n'est coupée qu'une fois le glissement engagé.
 */
export function useDragScroll<T extends HTMLElement = HTMLDivElement>(selector = '[data-slot="table-container"]') {
    const ref = useRef<T | null>(null);

    useEffect(() => {
        const root = ref.current;
        if (!root) {
            return;
        }

        const scroller = root.matches(selector) ? (root as HTMLElement) : root.querySelector<HTMLElement>(selector);
        if (!scroller) {
            return;
        }

        let pointerId: number | null = null;
        let startX = 0;
        let startScrollLeft = 0;
        let dragging = false;

        const isInteractive = (target: EventTarget | null) =>
            target instanceof Element && target.closest('button, a, input, select, textarea, [role="checkbox"], [data-slot="dropdown-menu-trigger"]') !== null;

        const stop = () => {
            if (pointerId !== null && scroller.hasPointerCapture(pointerId)) {
                scroller.releasePointerCapture(pointerId);
            }
            pointerId = null;
            dragging = false;
            scroller.style.removeProperty('cursor');
            scroller.style.removeProperty('user-select');
        };

        const onPointerDown = (event: PointerEvent) => {
            if (event.button !== 0 || event.pointerType === 'touch') {
                return; // Le tactile défile déjà nativement.
            }
            if (scroller.scrollWidth <= scroller.clientWidth || isInteractive(event.target)) {
                return;
            }

            pointerId = event.pointerId;
            startX = event.clientX;
            startScrollLeft = scroller.scrollLeft;
        };

        const onPointerMove = (event: PointerEvent) => {
            if (pointerId === null || event.pointerId !== pointerId) {
                return;
            }

            const delta = event.clientX - startX;

            if (!dragging) {
                if (Math.abs(delta) < DRAG_THRESHOLD) {
                    return;
                }
                dragging = true;
                scroller.setPointerCapture(pointerId);
                scroller.style.cursor = 'grabbing';
                scroller.style.userSelect = 'none';
            }

            scroller.scrollLeft = startScrollLeft - delta;
            event.preventDefault();
        };

        scroller.addEventListener('pointerdown', onPointerDown);
        scroller.addEventListener('pointermove', onPointerMove);
        scroller.addEventListener('pointerup', stop);
        scroller.addEventListener('pointercancel', stop);
        scroller.addEventListener('pointerleave', stop);

        return () => {
            scroller.removeEventListener('pointerdown', onPointerDown);
            scroller.removeEventListener('pointermove', onPointerMove);
            scroller.removeEventListener('pointerup', stop);
            scroller.removeEventListener('pointercancel', stop);
            scroller.removeEventListener('pointerleave', stop);
            stop();
        };
    }, [selector]);

    return ref;
}
