/**
 * Long press vs arrastre vs tap (Pointer Events unificados).
 * No duplica el menú: llama callbacks para abrir el strip existente.
 */
(function (global) {
    'use strict';

    var DEFAULT_LONG_PRESS_MS = 420;
    var DEFAULT_MOVE_PX = 8;

    function createController(options) {
        var longPressMs = options.longPressMs != null ? options.longPressMs : DEFAULT_LONG_PRESS_MS;
        var menuLongPressMs = options.menuLongPressMs != null ? options.menuLongPressMs : longPressMs;
        var moveThresholdPx = options.moveThresholdPx != null ? options.moveThresholdPx : DEFAULT_MOVE_PX;
        var onLongPressMenu = options.onLongPressMenu;
        var onDragStart = options.onDragStart;
        var onDragMove = options.onDragMove;
        var onDragEnd = options.onDragEnd;
        var onSuppressClick = options.onSuppressClick;
        var mouseLongPressAction = options.mouseLongPressAction || 'menu'; // 'menu' | 'drag'
        var touchLongPressAction = options.touchLongPressAction || 'menu'; // 'menu' | 'drag'
        var dragHandleSelector = options.dragHandleSelector || null;
        var isDisabled = options.isDisabled || function () {
            return false;
        };

        var timer = null; // longPressMs (drag/menu según pointerType)
        var menuTimer = null; // menuLongPressMs (para touch principalmente)
        var startX = 0;
        var startY = 0;
        var dragging = false;
        var longPressFired = false;
        var pointerId = null;
        var el = options.element;
        var allowDragFromDown = true;
        var downPointerType = null;
        var lastClientX = 0;
        var lastClientY = 0;
        var suppressCtxUntil = 0;
        var armedForDrag = false;
        var menuEligible = false;
        var prevTouchAction = '';
        var menuOpened = false;
        var prevBodyOverflow = '';
        var prevBodyTouchAction = '';
        var prevHtmlOverflow = '';
        var prevHtmlTouchAction = '';
        var scrollLocked = false;

        function lockScroll() {
            if (scrollLocked) return;
            scrollLocked = true;
            try {
                var body = document.body;
                var html = document.documentElement;
                prevBodyOverflow = body && body.style ? (body.style.overflow || '') : '';
                prevBodyTouchAction = body && body.style ? (body.style.touchAction || '') : '';
                prevHtmlOverflow = html && html.style ? (html.style.overflow || '') : '';
                prevHtmlTouchAction = html && html.style ? (html.style.touchAction || '') : '';
                if (body && body.style) {
                    body.style.overflow = 'hidden';
                    body.style.touchAction = 'none';
                }
                if (html && html.style) {
                    html.style.overflow = 'hidden';
                    html.style.touchAction = 'none';
                }
            } catch (_) {}
        }

        function unlockScroll() {
            if (!scrollLocked) return;
            scrollLocked = false;
            try {
                var body = document.body;
                var html = document.documentElement;
                if (body && body.style) {
                    body.style.overflow = prevBodyOverflow || '';
                    body.style.touchAction = prevBodyTouchAction || '';
                }
                if (html && html.style) {
                    html.style.overflow = prevHtmlOverflow || '';
                    html.style.touchAction = prevHtmlTouchAction || '';
                }
            } catch (_) {}
        }

        function clearTimer() {
            if (timer) {
                clearTimeout(timer);
                timer = null;
            }
        }

        function clearMenuTimer() {
            if (menuTimer) {
                clearTimeout(menuTimer);
                menuTimer = null;
            }
        }

        function distance(ax, ay, bx, by) {
            return Math.sqrt((bx - ax) * (bx - ax) + (by - ay) * (by - ay));
        }

        function suppressClick() {
            if (typeof onSuppressClick === 'function') onSuppressClick();
        }

        function suppressNativeContextMenuFor(ms) {
            suppressCtxUntil = Date.now() + (ms || 800);
        }

        function contextMenuGuard(e) {
            if (!e) return;
            if (Date.now() > suppressCtxUntil) return;
            // Evitar que el navegador dispare contextmenu "nativo" tras long-press en touch,
            // y evitar que el handler global abra el menú del escritorio.
            try {
                e.preventDefault();
                e.stopPropagation();
                if (e.stopImmediatePropagation) e.stopImmediatePropagation();
            } catch (_) {}
        }

        function isOnHandle(target) {
            if (!dragHandleSelector) return true;
            if (!target || !target.closest) return false;
            var match = target.closest(dragHandleSelector);
            if (!match) return false;
            return el.contains(match);
        }

        function onPointerDown(e) {
            if (isDisabled()) return;
            if (e.button === 2 || (e.pointerType === 'mouse' && e.buttons === 2)) {
                return;
            }
            if (e.button !== 0) return;

            clearTimer();
            clearMenuTimer();
            dragging = false;
            longPressFired = false;
            armedForDrag = false;
            menuEligible = false;
            menuOpened = false;
            pointerId = e.pointerId;
            downPointerType = e.pointerType || null;
            startX = e.clientX;
            startY = e.clientY;
            lastClientX = e.clientX;
            lastClientY = e.clientY;
            allowDragFromDown = isOnHandle(e.target);
            prevTouchAction = '';
            try {
                prevTouchAction = el.style.touchAction || '';
            } catch (_) {}

            try {
                el.setPointerCapture(e.pointerId);
            } catch (_) {}

            timer = setTimeout(function () {
                timer = null;
                if (dragging) return;
                longPressFired = true;
                var wantsDrag =
                    (downPointerType === 'mouse' && mouseLongPressAction === 'drag') ||
                    (downPointerType === 'touch' && touchLongPressAction === 'drag') ||
                    (downPointerType === 'pen' && touchLongPressAction === 'drag');

                if (wantsDrag && allowDragFromDown && onDragStart) {
                    // En touch/pen: replicar Android. Long-press SOLO "arma" el drag.
                    // El drag real inicia cuando el usuario se desplaza (threshold).
                    if (downPointerType === 'touch' || downPointerType === 'pen') {
                        armedForDrag = true;
                        suppressClick();
                        suppressNativeContextMenuFor(1200);
                        try { el.style.touchAction = 'none'; } catch (_) {}
                        try { el.classList.add('launcher-drag-armed'); } catch (_) {}
                        lockScroll();
                        return;
                    }
                    // En mouse: long-press inicia drag inmediatamente.
                    dragging = true;
                    clearMenuTimer();
                    suppressClick();
                    suppressNativeContextMenuFor(1200);
                    onDragStart(e, { startX: startX, startY: startY, clientX: lastClientX, clientY: lastClientY });
                    return;
                }
                if (onLongPressMenu) {
                    suppressClick();
                    suppressNativeContextMenuFor(1200);
                    onLongPressMenu(e);
                }
            }, longPressMs);

            // Menú contextual "largo" (ej. 3s en móvil) sin interferir con el drag por longPressMs.
            if (menuLongPressMs && menuLongPressMs > longPressMs && onLongPressMenu) {
                menuTimer = setTimeout(function () {
                    menuTimer = null;
                    if (dragging) return;
                    if (!allowDragFromDown) return;
                    // En touch: no abrimos el menú mientras el usuario sigue presionando,
                    // lo mostramos si NO hubo drag y el dedo sigue en el tile (Android-like).
                    if (downPointerType === 'touch' || downPointerType === 'pen') {
                        if (menuOpened) return;
                        menuEligible = true;
                        // Solo mostrar si el dedo no ha salido del "cajón" del icono/tile
                        // y no se ha movido más del umbral.
                        var r = el.getBoundingClientRect ? el.getBoundingClientRect() : null;
                        var inside = !r || (lastClientX >= r.left && lastClientX <= r.right && lastClientY >= r.top && lastClientY <= r.bottom);
                        var moved = distance(startX, startY, lastClientX, lastClientY) > moveThresholdPx;
                        if (!inside || moved) return;

                        // Si está armado para mover pero no hubo drag, se cancela el mover y se abre menú.
                        armedForDrag = false;
                        try { el.classList.remove('launcher-drag-armed'); } catch (_) {}
                        try { el.style.touchAction = prevTouchAction || ''; } catch (_) {}
                        unlockScroll();
                        suppressClick();
                        suppressNativeContextMenuFor(1500);
                        menuOpened = true;
                        onLongPressMenu(e);
                        return;
                    }
                    suppressClick();
                    suppressNativeContextMenuFor(1500);
                    onLongPressMenu(e);
                }, menuLongPressMs);
            }
        }

        function onPointerMove(e) {
            if (pointerId === null) return;
            if (pointerId !== null && e.pointerId !== pointerId) return;
            if (isDisabled()) return;
            lastClientX = e.clientX;
            lastClientY = e.clientY;
            if (!allowDragFromDown) return;

            // En móvil, cuando está armado o arrastrando, bloqueamos scroll para permitir drag fluido.
            if ((downPointerType === 'touch' || downPointerType === 'pen') && (armedForDrag || dragging)) {
                try { e.preventDefault(); } catch (_) {}
                try { el.style.touchAction = 'none'; } catch (_) {}
            }
            var d = distance(startX, startY, e.clientX, e.clientY);
            if (d > moveThresholdPx) {
                // Si el drag estaba armado (Android-like), inicia drag al primer movimiento real.
                if (!dragging && armedForDrag && onDragStart) {
                    dragging = true;
                    armedForDrag = false;
                    clearMenuTimer();
                    suppressClick();
                    suppressNativeContextMenuFor(800);
                    try { el.style.touchAction = 'none'; } catch (_) {}
                    try { el.classList.remove('launcher-drag-armed'); } catch (_) {}
                    lockScroll();
                    onDragStart(e, { startX: startX, startY: startY });
                }

                var wantsDragOnLongPress =
                    (downPointerType === 'mouse' && mouseLongPressAction === 'drag') ||
                    ((downPointerType === 'touch' || downPointerType === 'pen') && touchLongPressAction === 'drag');

                // Si el drag es "por long-press", NO iniciamos drag por movimiento.
                if (!dragging && wantsDragOnLongPress) {
                    // En móvil, el movimiento normalmente es scroll: cancelamos el menú de 3s.
                    if (downPointerType === 'touch' || downPointerType === 'pen') {
                        clearMenuTimer();
                    }
                    // En PC, permitimos mover el mouse mientras sostienes clic sin cancelar el long-press.
                    return;
                }

                clearTimer();
                clearMenuTimer();
                if (!dragging && !longPressFired && onDragStart) {
                    dragging = true;
                    suppressClick();
                    suppressNativeContextMenuFor(800);
                    onDragStart(e, { startX: startX, startY: startY });
                }
                if (dragging && onDragMove) onDragMove(e);
            } else if (dragging && onDragMove) {
                onDragMove(e);
            }
        }

        function onPointerUp(e) {
            if (pointerId !== null && e.pointerId !== pointerId) return;
            clearTimer();
            clearMenuTimer();
            try {
                if (el.hasPointerCapture && el.hasPointerCapture(e.pointerId)) {
                    el.releasePointerCapture(e.pointerId);
                }
            } catch (_) {}
            pointerId = null;
            if (dragging && onDragEnd) {
                onDragEnd(e);
            }
            // Touch: si se armó el modo mover, no hubo drag y se cumplió el tiempo,
            // al soltar abrimos el menú contextual (Android-like).
            if (!menuOpened && !dragging && (downPointerType === 'touch' || downPointerType === 'pen') && armedForDrag && menuEligible && onLongPressMenu) {
                try {
                    // Solo mostrar menú si el dedo sigue dentro del tile (no salió del "espacio" del icono).
                    var r = el.getBoundingClientRect ? el.getBoundingClientRect() : null;
                    var inside = !r || (lastClientX >= r.left && lastClientX <= r.right && lastClientY >= r.top && lastClientY <= r.bottom);
                    try { el.classList.remove('launcher-drag-armed'); } catch (_) {}
                    if (inside) {
                        unlockScroll();
                        suppressClick();
                        suppressNativeContextMenuFor(1500);
                        menuOpened = true;
                        onLongPressMenu(e);
                    }
                } catch (_) {}
            }
            dragging = false;
            armedForDrag = false;
            menuEligible = false;
            menuOpened = false;
            longPressFired = false;
            downPointerType = null;
            allowDragFromDown = true;
            try { el.style.touchAction = prevTouchAction || ''; } catch (_) {}
            try { el.classList.remove('launcher-drag-armed'); } catch (_) {}
            unlockScroll();
        }

        function onPointerCancel(e) {
            onPointerUp(e);
        }

        el.addEventListener('pointerdown', onPointerDown, { passive: false });
        el.addEventListener('pointermove', onPointerMove, { passive: false });
        el.addEventListener('pointerup', onPointerUp, { passive: true });
        el.addEventListener('pointercancel', onPointerCancel, { passive: true });
        el.addEventListener('contextmenu', contextMenuGuard, true);
        document.addEventListener('contextmenu', contextMenuGuard, true);

        return {
            destroy: function () {
                clearTimer();
                clearMenuTimer();
                el.removeEventListener('pointerdown', onPointerDown);
                el.removeEventListener('pointermove', onPointerMove);
                el.removeEventListener('pointerup', onPointerUp);
                el.removeEventListener('pointercancel', onPointerCancel);
                el.removeEventListener('contextmenu', contextMenuGuard, true);
                document.removeEventListener('contextmenu', contextMenuGuard, true);
                unlockScroll();
            }
        };
    }

    global.GridLauncher = global.GridLauncher || {};
    global.GridLauncher.pointerInteraction = {
        createController: createController,
        DEFAULT_LONG_PRESS_MS: DEFAULT_LONG_PRESS_MS,
        DEFAULT_MOVE_PX: DEFAULT_MOVE_PX
    };
})(typeof window !== 'undefined' ? window : this);
