// wwwroot/js/barcodeWedge.js
// Listens globally for keydown events and detects "wedge" scans.

const _instances = new Map();

function nowMs() { return performance.now ? performance.now() : Date.now(); }

function createListener(dotnetRef, options) {
    const cfg = {
        interKeyThresholdMs: 250,   // max delay between keys to still count as "scanner-speed"
        minLength: 60,              // minimum length to consider a valid scan
        completeOn: ['Enter', 'Tab'], // which keys finish a scan
        idleCompleteMs: 250,        // auto-complete if no key arrives within this window during a burst
        ...options
    };

    let buffer = '';
    let lastTs = 0;
    let timerId = null;
    let inBurst = false;

    const reset = () => {
        buffer = '';
        lastTs = 0;
        inBurst = false;
        if (timerId) { clearTimeout(timerId); timerId = null; }
    };

    const tryScheduleIdleComplete = () => {
        if (timerId) clearTimeout(timerId);
        timerId = setTimeout(async () => {
            // Burst ended by time gap; if it looks like a scan, send it.
            if (inBurst && buffer.length >= cfg.minLength) {
                try { await dotnetRef.invokeMethodAsync('OnBarcodeScanned', buffer); } catch { }
            }
            reset();
        }, cfg.idleCompleteMs);
    };

    const handler = async (e) => {
        // Ignore meta keys / composition
        if (e.ctrlKey || e.altKey || e.metaKey) return;
        if (e.isComposing) return;

        const ts = nowMs();

        // Finish on Enter/Tab if we already have something
        if (cfg.completeOn.includes(e.key)) {
            if (buffer.length >= cfg.minLength) {
                // blur active element if it's an interactive control
                const ae = document.activeElement;
                if (ae && ae !== document.body) {
                    const tag = ae.tagName;
                    const type = ae.type;
                    const role = ae.getAttribute('role');

                    const shouldBlur =
                        tag === 'BUTTON' ||
                        tag === 'A' ||
                        (tag === 'INPUT' && (type === 'button' || type === 'submit' || type === 'text')) ||
                        tag === 'TEXTAREA' ||
                        tag === 'SELECT' ||
                        role === 'button' ||
                        role === 'link';

                    if (shouldBlur) {
                        ae.blur();
                    }
                }

                try { await dotnetRef.invokeMethodAsync('OnBarcodeScanned', buffer); } catch { }
                reset();
                // swallow default click/submit + stop bubbling
                e.preventDefault();
                e.stopPropagation();
                if (e.stopImmediatePropagation) e.stopImmediatePropagation();
            } else {
                reset();
            }
            return;
        }

        // Accept single printable characters only
        if (e.key.length === 1) {
            const delta = lastTs === 0 ? 0 : (ts - lastTs);
            const fast = lastTs === 0 || delta <= cfg.interKeyThresholdMs;

            if (!inBurst) {
                // Start burst on first printable key
                inBurst = true;
                buffer = e.key;
                lastTs = ts;
                tryScheduleIdleComplete();
            } else {
                if (fast) {
                    buffer += e.key;
                    lastTs = ts;
                    tryScheduleIdleComplete();
                } else {
                    // Human-speed: end previous burst, maybe emit, then start a new burst
                    if (buffer.length >= cfg.minLength) {
                        try { await dotnetRef.invokeMethodAsync('OnBarcodeScanned', buffer); } catch { }
                    }
                    reset();
                    inBurst = true;
                    buffer = e.key;
                    lastTs = ts;
                    tryScheduleIdleComplete();
                }
            }
        } else {
            // Non-printable, ignore (except Enter/Tab above)
        }
    };

    return { handler, reset };
}

export function start(instanceId, dotnetRef, options) {
    if (_instances.has(instanceId)) return; // already started
    const l = createListener(dotnetRef, options);
    window.addEventListener('keydown', l.handler, true);
    _instances.set(instanceId, l);
}

export function stop(instanceId) {
    const l = _instances.get(instanceId);
    if (!l) return;
    window.removeEventListener('keydown', l.handler, true);
    l.reset();
    _instances.delete(instanceId);
}
