(function (global) {
    'use strict';

    const Internal = global.ReaderV2Internal = global.ReaderV2Internal || {};
    const attachedDocuments = new WeakMap();

    function selectionText(doc) {
        try {
            return String(doc.defaultView.getSelection().toString() || '').trim();
        } catch (_) {
            return '';
        }
    }

    function interactiveTarget(target) {
        return Boolean(target && target.closest && target.closest('a,button,input,textarea,select,summary,[contenteditable="true"]'));
    }

    function attachGestures(doc, options) {
        if (!doc || attachedDocuments.has(doc)) return attachedDocuments.get(doc) || function () {};

        let startX = 0;
        let startY = 0;
        let startAt = 0;
        let startTarget = null;
        let consumedAt = 0;
        let moved = false;
        let brightnessCandidate = false;

        function preferences() {
            return typeof options.getPreferences === 'function' ? options.getPreferences() : {};
        }

        function onTouchStart(event) {
            if (!event.touches || event.touches.length !== 1) return;
            const touch = event.touches[0];
            startX = touch.clientX;
            startY = touch.clientY;
            startAt = Date.now();
            startTarget = event.target;
            moved = false;
            const width = doc.documentElement.clientWidth || global.innerWidth;
            brightnessCandidate = Boolean(preferences().brightnessGesture && startX <= width * .18);
        }

        function onTouchMove(event) {
            if (!event.touches || event.touches.length !== 1) return;
            const touch = event.touches[0];
            const dx = touch.clientX - startX;
            const dy = touch.clientY - startY;
            if (Math.abs(dx) > 10 || Math.abs(dy) > 10) moved = true;
            if (brightnessCandidate && Math.abs(dy) > 6 && Math.abs(dy) > Math.abs(dx) * 1.15) {
                event.preventDefault();
            }
        }

        function doTap(x, width) {
            const current = preferences();
            const mode = current.pageTurnMode || 'tapSwipe';
            const normalized = x / Math.max(1, width);
            if (normalized >= .28 && normalized <= .72) {
                options.toggleControls();
                return;
            }
            if (mode !== 'tapSwipe' && mode !== 'tap') return;
            if (normalized < .28) current.invertZones ? options.next() : options.previous();
            else current.invertZones ? options.previous() : options.next();
        }

        function onTouchEnd(event) {
            const touch = event.changedTouches && event.changedTouches[0];
            if (!touch || !startAt) return;
            const elapsed = Date.now() - startAt;
            const dx = touch.clientX - startX;
            const dy = touch.clientY - startY;
            const horizontal = Math.abs(dx) > Math.abs(dy) * 1.25;
            const current = preferences();
            const mode = current.pageTurnMode || 'tapSwipe';
            const viewportWidth = doc.documentElement.clientWidth || global.innerWidth;
            const viewportHeight = doc.documentElement.clientHeight || global.innerHeight;

            if (interactiveTarget(startTarget) || selectionText(doc)) return;

            if (
                current.brightnessGesture &&
                startX <= viewportWidth * .18 &&
                elapsed < 1200 &&
                Math.abs(dy) >= 28 &&
                Math.abs(dy) > Math.abs(dx) * 1.35
            ) {
                Internal.post('brightnessDelta', {
                    delta: Internal.clamp((-dy / Math.max(1, viewportHeight)) * 100, -100, 100)
                });
                consumedAt = Date.now();
                return;
            }

            if (elapsed < 650 && horizontal && Math.abs(dx) >= 48 && (mode === 'tapSwipe' || mode === 'swipe')) {
                dx < 0 ? options.next() : options.previous();
                consumedAt = Date.now();
                return;
            }

            if (elapsed < 300 && !moved && Math.abs(dx) < 12 && Math.abs(dy) < 12) {
                doTap(touch.clientX, doc.documentElement.clientWidth || global.innerWidth);
                consumedAt = Date.now();
            }
        }

        function onClick(event) {
            if (Date.now() - consumedAt < 500 || interactiveTarget(event.target) || selectionText(doc)) return;
            doTap(event.clientX, doc.documentElement.clientWidth || global.innerWidth);
        }

        function onKeyDown(event) {
            if (interactiveTarget(event.target)) return;
            if (event.key === 'ArrowLeft' || event.key === 'PageUp') {
                event.preventDefault();
                options.previous();
            } else if (event.key === 'ArrowRight' || event.key === 'PageDown' || event.key === ' ') {
                event.preventDefault();
                options.next();
            } else if (event.key === 'Escape') {
                options.toggleControls();
            }
        }

        doc.addEventListener('touchstart', onTouchStart, { passive: true });
        doc.addEventListener('touchmove', onTouchMove, { passive: false });
        doc.addEventListener('touchend', onTouchEnd, { passive: true });
        doc.addEventListener('click', onClick, false);
        doc.addEventListener('keydown', onKeyDown, false);

        const detach = function () {
            doc.removeEventListener('touchstart', onTouchStart);
            doc.removeEventListener('touchmove', onTouchMove);
            doc.removeEventListener('touchend', onTouchEnd);
            doc.removeEventListener('click', onClick);
            doc.removeEventListener('keydown', onKeyDown);
            attachedDocuments.delete(doc);
        };
        attachedDocuments.set(doc, detach);
        return detach;
    }

    Internal.attachGestures = attachGestures;
})(window);
