(function (global) {
    'use strict';

    const Internal = global.ReaderV2Internal = global.ReaderV2Internal || {};
    const attachedDocuments = new WeakMap();
    const LEFT_ZONE_END = 1 / 3;
    const RIGHT_ZONE_START = 2 / 3;
    let lastHandledTouchAt = 0;

    function viewportWidth(doc) {
        const viewWidth = Number(doc && doc.defaultView && doc.defaultView.innerWidth);
        if (Number.isFinite(viewWidth) && viewWidth > 0) return viewWidth;
        const documentWidth = Number(doc && doc.documentElement && doc.documentElement.clientWidth);
        if (Number.isFinite(documentWidth) && documentWidth > 0) return documentWidth;
        return Math.max(1, Number(global.innerWidth) || 1);
    }

    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 = viewportWidth(doc);
            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) {
            if (options.nativeTapZones) return false;
            const current = preferences();
            const mode = current.pageTurnMode || 'tapSwipe';
            const normalized = x / Math.max(1, width);
            if (normalized >= LEFT_ZONE_END && normalized <= RIGHT_ZONE_START) {
                options.toggleControls();
                return true;
            }
            if (mode !== 'tapSwipe' && mode !== 'tap') return false;
            if (normalized < LEFT_ZONE_END) current.invertZones ? options.next() : options.previous();
            else current.invertZones ? options.previous() : options.next();
            return true;
        }

        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 width = viewportWidth(doc);
            const viewportHeight = doc.documentElement.clientHeight || global.innerHeight;

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

            if (
                current.brightnessGesture &&
                startX <= width * .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();
                lastHandledTouchAt = consumedAt;
                event.preventDefault();
                return;
            }

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

            if (elapsed < 300 && !moved && Math.abs(dx) < 12 && Math.abs(dy) < 12) {
                if (doTap(touch.clientX, width)) {
                    consumedAt = Date.now();
                    lastHandledTouchAt = consumedAt;
                    event.preventDefault();
                }
            }
        }

        function onClick(event) {
            const now = Date.now();
            if (
                now - consumedAt < 700 ||
                now - lastHandledTouchAt < 700 ||
                interactiveTarget(event.target) ||
                selectionText(doc)
            ) return;
            doTap(event.clientX, viewportWidth(doc));
        }

        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: false });
        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);
