(function (app) {
    'use strict';

    const util = {};

    util.h = function (tag, attrs) {
        const el = document.createElement(tag);
        if (attrs) {
            for (const k of Object.keys(attrs)) {
                const v = attrs[k];
                if (v == null || v === false) {
                    continue;
                }
                if (k === 'className') {
                    el.className = v;
                } else if (k.length > 2 && k.slice(0, 2) === 'on' && typeof v === 'function') {
                    el.addEventListener(k.slice(2).toLowerCase(), v);
                } else if (v === true) {
                    el.setAttribute(k, '');
                } else {
                    el.setAttribute(k, v);
                }
            }
        }
        for (let i = 2; i < arguments.length; i++) {
            const c = arguments[i];
            if (c == null || c === false) {
                continue;
            }
            if (typeof c === 'string' || typeof c === 'number') {
                el.appendChild(document.createTextNode(String(c)));
            } else {
                el.appendChild(c);
            }
        }
        return el;
    };

    // Resolve a column's `field` against a row data object.
    // - "" or "/" → the whole object
    // - "/foo/bar" → JSON Pointer (RFC 6901) lookup
    // - "foo" → top-level key
    util.resolveField = function (data, field) {
        if (data == null) {
            return undefined;
        }
        if (!field || field === '/') {
            return data;
        }
        if (field.charAt(0) !== '/') {
            return data[field];
        }
        const segments = field.split('/').slice(1).map(function (s) {
            return s.replace(/~1/g, '/').replace(/~0/g, '~');
        });
        let cur = data;
        for (let i = 0; i < segments.length; i++) {
            if (cur == null) {
                return undefined;
            }
            if (Array.isArray(cur)) {
                const idx = parseInt(segments[i], 10);
                if (Number.isNaN(idx)) {
                    return undefined;
                }
                cur = cur[idx];
            } else if (typeof cur === 'object') {
                cur = cur[segments[i]];
            } else {
                return undefined;
            }
        }
        return cur;
    };

    // Format a raw cell value per column's `format` hint.
    util.formatCell = function (value, format) {
        if (value == null || value === '') {
            return '';
        }
        if (format === 'date') {
            const d = new Date(value);
            if (!isNaN(d.getTime())) {
                return d.toISOString().slice(0, 10);
            }
            return String(value);
        }
        if (format === 'datetime') {
            const d = new Date(value);
            if (!isNaN(d.getTime())) {
                return d.toLocaleString();
            }
            return String(value);
        }
        if (format === 'number') {
            const n = Number(value);
            if (Number.isFinite(n)) {
                return n.toLocaleString();
            }
            return String(value);
        }
        if (format === 'bool' || typeof value === 'boolean') {
            return value ? '✓' : '';
        }
        if (typeof value === 'object') {
            try {
                return JSON.stringify(value);
            } catch (e) {
                return String(value);
            }
        }
        return String(value);
    };

    // Compare two cell values for sorting. null/undefined sort last.
    // Numbers compared numerically, dates compared as Date, otherwise string compare.
    util.compareCells = function (a, b, format) {
        const aMissing = a == null || a === '';
        const bMissing = b == null || b === '';
        if (aMissing && bMissing) {
            return 0;
        }
        if (aMissing) {
            return 1;
        }
        if (bMissing) {
            return -1;
        }
        if (format === 'date' || format === 'datetime') {
            const da = new Date(a).getTime();
            const db = new Date(b).getTime();
            if (!isNaN(da) && !isNaN(db)) {
                return da - db;
            }
        }
        if (format === 'number' || (typeof a === 'number' && typeof b === 'number')) {
            const na = Number(a);
            const nb = Number(b);
            if (Number.isFinite(na) && Number.isFinite(nb)) {
                return na - nb;
            }
        }
        const sa = String(a).toLowerCase();
        const sb = String(b).toLowerCase();
        if (sa < sb) return -1;
        if (sa > sb) return 1;
        return 0;
    };

    app.modules.util = util;
})(window.tablesApp);
