/**
 * CSP-safe event delegation
 * Replaces inline on* handlers with data-* attributes
 * Usage: data-onclick="myFunction('arg')" data-onsubmit="handleSubmit(this.form)"
 */
(function () {
    var H = {
        'data-onclick': 'click',
        'data-onsubmit': 'submit',
        'data-onchange': 'change',
        'data-oninput': 'input',
        'data-onkeyup': 'keyup',
        'data-onkeydown': 'keydown',
        'data-onkeypress': 'keypress',
        'data-onfocus': 'focusin',
        'data-onblur': 'focusout',
    };

    function _resolveFn(path, el) {
        var parts = path.split('.');
        // Try resolving from element first
        var obj = el;
        for (var i = 0; i < parts.length; i++) {
            if (obj && typeof obj[parts[i]] === 'function') {
                obj = obj[parts[i]];
            } else {
                obj = null;
                break;
            }
        }
        if (obj && typeof obj === 'function') return obj;
        
        // Try resolving from window, walking the chain
        obj = window;
        for (var i = 0; i < parts.length; i++) {
            if (obj && typeof obj[parts[i]] !== 'undefined') {
                obj = obj[parts[i]];
            } else {
                obj = null;
                break;
            }
        }
        return (obj && typeof obj === 'function') ? obj : null;
    }

    function _parseCall(code) {
        var s = code.trim();
        if (s.startsWith('return ')) s = s.slice(7).trim();
        var m = s.match(/^([\w.$]+)\((.*)\)$/);
        if (m) return { fn: m[1], argsStr: m[2] };
        if (/^[\w.$]+$/.test(s)) return { fn: s, argsStr: '' };
        return null;
    }

    function _splitArgs(str) {
        if (!str.trim()) return [];
        var args = [], current = '', depth = 0, inStr = false, strChar = '';
        for (var i = 0; i < str.length; i++) {
            var c = str[i];
            if (inStr) { if (c === strChar) inStr = false; current += c; continue; }
            if (c === '"' || c === "'") { inStr = true; strChar = c; current += c; continue; }
            if (c === '(' || c === '[' || c === '{') depth++;
            if (c === ')' || c === ']' || c === '}') depth--;
            if (c === ',' && depth === 0) { args.push(current.trim()); current = ''; continue; }
            current += c;
        }
        if (current.trim()) args.push(current.trim());
        return args;
    }

    function _invoke(code, el, e) {
        var parsed = _parseCall(code);
        if (!parsed) return undefined;
        var fn = _resolveFn(parsed.fn, el);
        if (!fn) { console.warn('CSP delegate: unknown function', parsed.fn); return undefined; }
        var args = _splitArgs(parsed.argsStr);
        args = args.map(function (a) {
            // Strip surrounding quotes from string literals
            if ((a.startsWith("'") && a.endsWith("'")) || (a.startsWith('"') && a.endsWith('"'))) {
                return a.slice(1, -1);
            }
            if (a === 'this') return el;
            if (a === 'event' || a === 'e') return e;
            if (a === 'this.value') return el.value;
            if (a === 'this.form') return el.form;
            if (a === 'this.checked') return el.checked;
            return a;
        });
        return fn.apply(window, args);
    }

    // Attach delegated listeners
    for (var attr in H) {
        (function (a, ev) {
            document.addEventListener(ev, function (e) {
                var el = e.target.closest('[' + a + ']');
                if (!el) return;
                var code = el.getAttribute(a);
                if (!code) return;
                var result = _invoke(code, el, e);
                if (result === false) {
                    e.preventDefault();
                    e.stopPropagation();
                }
            }, ev === 'focusin' || ev === 'focusout');
        })(attr, H[attr]);
    }

    // Error and load events
    ['error', 'load'].forEach(function (ev) {
        document.addEventListener(ev, function (e) {
            var code = e.target.getAttribute('data-' + ev);
            if (!code) return;
            _invoke(code, e.target, e);
        }, true);
    });
})();
