/**
 * pos-utils.js — Shared utility functions for all POS pages.
 *
 * Provides common operations that multiple pages need:
 *   - CSV export of any visible table
 *   - Print page (PDF via browser print dialog)
 *   - Toast notifications (if page doesn't have its own)
 *
 * Load this script in every POS template BEFORE page-specific JS.
 */
(function() {
    'use strict';

    // ── CSV Export ──────────────────────────────────────────────────
    // Finds the first visible <table> on the page and downloads it as CSV.
    // Works on inventory, customers, invoicing, reports, accounting.

    window.exportVisibleTableCSV = function(prefix) {
        prefix = prefix || 'datos';
        var tables = document.querySelectorAll('table');
        var table = null;

        // Find first visible table with data rows
        for (var i = 0; i < tables.length; i++) {
            if (tables[i].offsetParent !== null && tables[i].querySelector('tbody tr')) {
                table = tables[i];
                break;
            }
        }

        if (!table) {
            showToast('No hay tabla de datos para exportar en esta vista.', 'warn');
            return;
        }

        var rows = [];

        // Header row
        var ths = table.querySelectorAll('thead th');
        if (ths.length) {
            rows.push(Array.from(ths).map(function(th) {
                return '"' + th.textContent.trim().replace(/"/g, '""') + '"';
            }).join(','));
        }

        // Data rows
        table.querySelectorAll('tbody tr').forEach(function(tr) {
            var cells = tr.querySelectorAll('td');
            rows.push(Array.from(cells).map(function(td) {
                return '"' + td.textContent.trim().replace(/"/g, '""') + '"';
            }).join(','));
        });

        if (rows.length <= 1) {
            showToast('La tabla está vacía — no hay datos para exportar.', 'warn');
            return;
        }

        var csv = rows.join('\n');
        // BOM prefix so Excel opens UTF-8 correctly
        var blob = new Blob(['\uFEFF' + csv], { type: 'text/csv;charset=utf-8;' });
        var url = URL.createObjectURL(blob);
        var a = document.createElement('a');
        a.href = url;
        a.download = prefix + '_nexus_' + new Date().toISOString().slice(0, 10) + '.csv';
        document.body.appendChild(a);
        a.click();
        document.body.removeChild(a);
        URL.revokeObjectURL(url);

        showToast('CSV descargado: ' + a.download, 'ok');
    };

    // ── Print (PDF) ────────────────────────────────────────────────
    window.printPage = function() {
        window.print();
    };

    // ── Toast (enhanced with icons, progress bar, close button, actions) ──
    var _toastIcons = {
        ok: '<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"><polyline points="20 6 9 17 4 12"/></svg>',
        error: '<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"><line x1="18" y1="6" x2="6" y2="18"/><line x1="6" y1="6" x2="18" y2="18"/></svg>',
        warn: '<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"><path d="M10.29 3.86L1.82 18a2 2 0 001.71 3h16.94a2 2 0 001.71-3L13.71 3.86a2 2 0 00-3.42 0z"/><line x1="12" y1="9" x2="12" y2="13"/><line x1="12" y1="17" x2="12.01" y2="17"/></svg>',
        info: '<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="10"/><line x1="12" y1="16" x2="12" y2="12"/><line x1="12" y1="8" x2="12.01" y2="8"/></svg>'
    };
    var _toastTitles = { ok: 'Éxito', error: 'Error', warn: 'Advertencia', info: 'Información' };

    window.showToast = function(msg, type, opts) {
        type = type || 'info';
        opts = opts || {};
        var container = document.getElementById('toast-container');
        if (!container) {
            container = document.createElement('div');
            container.id = 'toast-container';
            document.body.appendChild(container);
        }

        var toast = document.createElement('div');
        toast.className = 'toast toast--' + type;

        var iconHtml = '<div class="toast__icon">' + (_toastIcons[type] || _toastIcons.info) + '</div>';
        var titleHtml = opts.title ? '<div class="toast__title">' + opts.title + '</div>' : '';
        var actionHtml = '';
        if (opts.action && opts.action.text) {
            actionHtml = '<div class="toast__action"><button onclick="this.closest(\'.toast\').__toastAction()">' + opts.action.text + '</button></div>';
            toast.__toastAction = function() {
                if (opts.action.callback) opts.action.callback();
                _removeToast(toast);
            };
        }
        var progressHtml = '<div class="toast__progress" style="animation-duration:' + (opts.duration || 4000) + 'ms;"></div>';

        toast.innerHTML = iconHtml +
            '<div class="toast__content">' + titleHtml + '<div class="toast__msg">' + msg + '</div>' + actionHtml + '</div>' +
            '<button class="toast__close" onclick="_removeToast(this.closest(\'.toast\'))">✕</button>' +
            progressHtml;

        container.appendChild(toast);

        var timer = setTimeout(function() { _removeToast(toast); }, opts.duration || 4000);
        toast.__toastTimer = timer;

        toast.addEventListener('mouseenter', function() { clearTimeout(timer); var p = toast.querySelector('.toast__progress'); if (p) p.style.animationPlayState = 'paused'; });
        toast.addEventListener('mouseleave', function() { var p = toast.querySelector('.toast__progress'); if (p) p.style.animationPlayState = 'running'; timer = setTimeout(function() { _removeToast(toast); }, 2000); toast.__toastTimer = timer; });
    };

    window._removeToast = function(toast) {
        if (!toast || toast.__toastRemoved) return;
        toast.__toastRemoved = true;
        if (toast.__toastTimer) clearTimeout(toast.__toastTimer);
        toast.style.animation = 'toastSlideOut 0.25s ease forwards';
        setTimeout(function() { toast.remove(); }, 260);
    };

    // ── Skeleton helpers ──────────────────────────────────────────
    window.renderSkeletonRows = function(cols, rows) {
        rows = rows || 6;
        var html = '';
        for (var i = 0; i < rows; i++) {
            html += '<tr class="skeleton--table-row">';
            for (var j = 0; j < cols; j++) {
                html += '<td><div class="skeleton"></div></td>';
            }
            html += '</tr>';
        }
        return html;
    };

    window.showSkeleton = function(containerSelector, cols, rows) {
        var el = typeof containerSelector === 'string' ? document.querySelector(containerSelector) : containerSelector;
        if (!el) return;
        el.dataset.originalContent = el.innerHTML;
        el.innerHTML = renderSkeletonRows(cols || 6, rows || 6);
    };

    window.hideSkeleton = function(containerSelector) {
        var el = typeof containerSelector === 'string' ? document.querySelector(containerSelector) : containerSelector;
        if (!el || el.dataset.originalContent === undefined) return;
        el.innerHTML = el.dataset.originalContent;
        delete el.dataset.originalContent;
    };

    // ── Loading / Empty state helpers ────────────────────────────────────────
    window.renderLoadingState = function(opts) {
        opts = opts || {};
        var message = opts.message || 'Cargando...';
        return '<div class="empty-state empty-state--loading" role="status" aria-live="polite">' +
            '<div class="empty-state__icon"><svg class="empty-state__spinner" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M21 12a9 9 0 1 1-6.219-8.56"/></svg></div>' +
            '<div class="empty-state__title">' + message + '</div>' +
            '</div>';
    };
    window.renderEmptyState = function(opts) {
        opts = opts || {};
        var icon = opts.icon || '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"><rect x="3" y="3" width="18" height="18" rx="2" ry="2"/><line x1="9" y1="9" x2="15" y2="15"/><line x1="15" y1="9" x2="9" y2="15"/></svg>';
        var title = opts.title || 'Sin datos';
        var subtitle = opts.subtitle || 'No hay información disponible en este momento.';
        var action = opts.action ? '<div class="empty-state__action">' + opts.action + '</div>' : '';
        return '<div class="empty-state">' +
            '<div class="empty-state__icon">' + icon + '</div>' +
            '<div class="empty-state__title">' + title + '</div>' +
            '<div class="empty-state__subtitle">' + subtitle + '</div>' +
            action + '</div>';
    };

    // ── Cmd+K Global Search ───────────────────────────────────────
    (function() {
        var cmdkOverlay = null, cmdkInput = null, cmdkResults = null, cmdkSelected = -1;
        var cmdkItems = [];

        function buildCmdK() {
            if (cmdkOverlay) return;
            cmdkOverlay = document.createElement('div');
            cmdkOverlay.className = 'cmdk-overlay';
            cmdkOverlay.innerHTML =
                '<div class="cmdk-modal" role="dialog" aria-label="Búsqueda global">' +
                '  <div class="cmdk-input-wrap">' +
                '    <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round" style="color:var(--color-text-muted);flex-shrink:0;"><circle cx="11" cy="11" r="8"/><line x1="21" y1="21" x2="16.65" y2="16.65"/></svg>' +
                '    <input type="text" class="cmdk-input" placeholder="Buscar módulos, productos, clientes..." autocomplete="off">' +
                '    <span class="cmdk-shortcut">ESC</span>' +
                '  </div>' +
                '  <div class="cmdk-results"></div>' +
                '  <div class="cmdk-footer"><span>↑↓ navegar · ↵ seleccionar</span><span>' + cmdkItems.length + ' resultados</span></div>' +
                '</div>';
            document.body.appendChild(cmdkOverlay);
            cmdkInput = cmdkOverlay.querySelector('.cmdk-input');
            cmdkResults = cmdkOverlay.querySelector('.cmdk-results');

            cmdkOverlay.addEventListener('click', function(e) { if (e.target === cmdkOverlay) closeCmdK(); });
            cmdkInput.addEventListener('input', function() { filterCmdK(this.value); });
            cmdkInput.addEventListener('keydown', function(e) {
                if (e.key === 'Escape') { closeCmdK(); return; }
                if (e.key === 'ArrowDown') { e.preventDefault(); moveCmdK(1); }
                if (e.key === 'ArrowUp') { e.preventDefault(); moveCmdK(-1); }
                if (e.key === 'Enter') { e.preventDefault(); activateCmdK(); }
            });
        }

        function openCmdK() {
            buildCmdK();
            cmdkOverlay.classList.add('is-open');
            cmdkInput.value = '';
            cmdkInput.focus();
            filterCmdK('');
        }

        function closeCmdK() {
            if (cmdkOverlay) cmdkOverlay.classList.remove('is-open');
        }

        function moveCmdK(dir) {
            var items = cmdkResults.querySelectorAll('.cmdk-item');
            if (!items.length) return;
            cmdkSelected += dir;
            if (cmdkSelected < 0) cmdkSelected = items.length - 1;
            if (cmdkSelected >= items.length) cmdkSelected = 0;
            items.forEach(function(it, i) { it.classList.toggle('is-selected', i === cmdkSelected); });
            var sel = items[cmdkSelected];
            if (sel) sel.scrollIntoView({ block: 'nearest' });
        }

        function activateCmdK() {
            var items = cmdkResults.querySelectorAll('.cmdk-item');
            var sel = items[cmdkSelected];
            if (sel && sel.dataset.href) { closeCmdK(); window.location.href = sel.dataset.href; }
        }

        function filterCmdK(q) {
            q = (q || '').toLowerCase().trim();
            var groups = {};
            cmdkItems.forEach(function(item) {
                if (!q || item.label.toLowerCase().indexOf(q) !== -1 || (item.keywords || '').toLowerCase().indexOf(q) !== -1) {
                    groups[item.group] = groups[item.group] || [];
                    groups[item.group].push(item);
                }
            });
            var html = '';
            var total = 0;
            Object.keys(groups).forEach(function(g) {
                html += '<div class="cmdk-group"><div class="cmdk-group__label">' + g + '</div>';
                groups[g].forEach(function(item) {
                    total++;
                    html += '<div class="cmdk-item" data-href="' + (item.href || '') + '">' +
                        '<div class="cmdk-item__icon">' + (item.icon || '→') + '</div>' +
                        '<div>' + item.label + '</div>' +
                        (item.meta ? '<div class="cmdk-item__meta">' + item.meta + '</div>' : '') +
                        '</div>';
                });
                html += '</div>';
            });
            if (!total) html = '<div style="padding:24px;text-align:center;color:var(--color-text-muted);">Sin resultados</div>';
            cmdkResults.innerHTML = html;
            cmdkSelected = 0;
            var first = cmdkResults.querySelector('.cmdk-item');
            if (first) first.classList.add('is-selected');
            var footer = cmdkOverlay.querySelector('.cmdk-footer span:last-child');
            if (footer) footer.textContent = total + ' resultados';
        }

        document.addEventListener('keydown', function(e) {
            if ((e.metaKey || e.ctrlKey) && e.key === 'k') { e.preventDefault(); openCmdK(); }
        });

        window.registerCmdKItem = function(item) {
            if (!item || !item.label) return;
            cmdkItems.push(item);
        };

        window.openCmdK = openCmdK;
        window.closeCmdK = closeCmdK;
    })();

    // ── Connection indicator helper ───────────────────────────────
    window.ConnectionStatus = {
        online: function() { return navigator.onLine; },
        render: function(containerId) {
            var el = document.getElementById(containerId);
            if (!el) return;
            function update() {
                var isOnline = navigator.onLine;
                el.className = 'connection-indicator' + (isOnline ? '' : ' connection-indicator--offline');
                el.innerHTML = '<span></span>' + (isOnline ? 'En línea' : 'Sin conexión');
            }
            update();
            window.addEventListener('online', update);
            window.addEventListener('offline', update);
        }
    };

    // ── Bulk toolbar helper ───────────────────────────────────────
    window.renderBulkToolbar = function(count, actionsHtml) {
        return '<div class="bulk-toolbar">' +
            '<div class="bulk-toolbar__count">' + count + ' seleccionado' + (count !== 1 ? 's' : '') + '</div>' +
            '<div class="bulk-toolbar__actions">' + actionsHtml + '</div>' +
            '</div>';
    };

    // ── Entrance animation helper ─────────────────────────────────
    window.animateEntrance = function(selector, animClass, stagger) {
        animClass = animClass || 'animate-fade-in-up';
        stagger = stagger || 0.05;
        var els = document.querySelectorAll(selector);
        els.forEach(function(el, i) {
            el.style.animationDelay = (i * stagger) + 's';
            el.classList.add(animClass);
        });
    };

    // ── "Próximamente" placeholder for features not yet built ──────
    window.featureProximamente = function(nombre) {
        showToast((nombre || 'Esta función') + ' estará disponible próximamente.', 'info');
    };

    // ── Table Filter Panel ────────────────────────────────────────
    // Creates a dropdown filter panel that filters visible table rows
    // client-side. Call toggleFilterPanel(buttonEl, config) where config
    // is an array of {label, column, values} describing each filter.
    //
    // Usage (from onclick):
    //   toggleFilterPanel(this, [
    //     {label: 'Marca', column: 2, values: ['BOSCH','MONROE','Todas']},
    //     {label: 'Status', column: 4, values: ['Activo','Inactivo','Todos']},
    //   ])

    var _activeFilterPanel = null;

    window.toggleFilterPanel = function(btnEl, filters) {
        // Close existing panel if open
        if (_activeFilterPanel) {
            _activeFilterPanel.remove();
            _activeFilterPanel = null;
            return;
        }

        var panel = document.createElement('div');
        panel.className = 'filter-panel';
        panel.style.cssText = 'position:absolute;top:100%;right:0;z-index:1000;' +
            'background:var(--glass-bg-strong,#1a1a1a);backdrop-filter:blur(16px);' +
            'border:1px solid var(--glass-border,#333);border-radius:var(--radius-lg,12px);' +
            'padding:16px;min-width:260px;box-shadow:0 8px 32px rgba(0,0,0,0.3);' +
            'display:flex;flex-direction:column;gap:12px;';

        var title = document.createElement('div');
        title.style.cssText = 'font-weight:700;font-size:14px;display:flex;justify-content:space-between;align-items:center;';
        title.innerHTML = 'Filtros <button onclick="closeFilterPanel()" style="background:none;border:none;color:var(--color-text-muted);cursor:pointer;font-size:18px;">✕</button>';
        panel.appendChild(title);

        filters.forEach(function(f) {
            var group = document.createElement('div');
            var label = document.createElement('label');
            label.style.cssText = 'display:block;font-size:12px;color:var(--color-text-muted);margin-bottom:4px;text-transform:uppercase;letter-spacing:0.05em;';
            label.textContent = f.label;
            group.appendChild(label);

            var select = document.createElement('select');
            select.style.cssText = 'width:100%;padding:8px 10px;background:var(--glass-bg,#222);' +
                'border:1px solid var(--glass-border,#444);border-radius:6px;' +
                'color:var(--color-text-primary,#fff);font-size:13px;';
            select.dataset.filterColumn = f.column;

            // "Todos" option always first
            var allOpt = document.createElement('option');
            allOpt.value = '';
            allOpt.textContent = f.allLabel || 'Todos';
            select.appendChild(allOpt);

            (f.values || []).forEach(function(v) {
                if (!v) return;
                var opt = document.createElement('option');
                opt.value = v;
                opt.textContent = v;
                select.appendChild(opt);
            });

            select.addEventListener('change', function() { applyFilters(panel); });
            group.appendChild(select);
            panel.appendChild(group);
        });

        // Clear all button
        var clearBtn = document.createElement('button');
        clearBtn.style.cssText = 'padding:8px;background:transparent;border:1px dashed var(--glass-border,#444);' +
            'border-radius:6px;color:var(--color-text-muted);cursor:pointer;font-size:12px;';
        clearBtn.textContent = 'Limpiar filtros';
        clearBtn.addEventListener('click', function() {
            panel.querySelectorAll('select').forEach(function(s) { s.value = ''; });
            applyFilters(panel);
        });
        panel.appendChild(clearBtn);

        // Position relative to the button
        var wrapper = btnEl.parentElement;
        if (wrapper) wrapper.style.position = 'relative';
        (wrapper || document.body).appendChild(panel);
        _activeFilterPanel = panel;

        // Close on outside click
        setTimeout(function() {
            document.addEventListener('click', function handler(e) {
                if (!panel.contains(e.target) && e.target !== btnEl) {
                    closeFilterPanel();
                    document.removeEventListener('click', handler);
                }
            });
        }, 100);
    };

    window.closeFilterPanel = function() {
        if (_activeFilterPanel) {
            _activeFilterPanel.remove();
            _activeFilterPanel = null;
        }
    };

    function applyFilters(panel) {
        var selects = panel.querySelectorAll('select[data-filter-column]');
        // Find the nearest visible table
        var tables = document.querySelectorAll('table');
        var table = null;
        for (var i = 0; i < tables.length; i++) {
            if (tables[i].offsetParent !== null) { table = tables[i]; break; }
        }
        if (!table) return;

        var rows = table.querySelectorAll('tbody tr');
        rows.forEach(function(tr) {
            var show = true;
            selects.forEach(function(sel) {
                var col = parseInt(sel.dataset.filterColumn);
                var val = sel.value.toLowerCase();
                if (!val) return; // "Todos" — no filter
                var cells = tr.querySelectorAll('td');
                if (cells[col]) {
                    var cellText = cells[col].textContent.trim().toLowerCase();
                    if (cellText.indexOf(val.toLowerCase()) === -1) show = false;
                }
            });
            tr.style.display = show ? '' : 'none';
        });

        // Update count badge if exists
        var visibleCount = 0;
        rows.forEach(function(tr) { if (tr.style.display !== 'none') visibleCount++; });
        var badge = document.querySelector('.filter-count-badge');
        if (badge) badge.textContent = visibleCount + ' resultados';
    }

    // ── Auto-extract unique values from a table column ──────────
    // Useful for building filter options dynamically from data.
    window.getUniqueColumnValues = function(tableEl, colIndex, maxValues) {
        maxValues = maxValues || 30;
        var values = {};
        if (!tableEl) return [];
        tableEl.querySelectorAll('tbody tr').forEach(function(tr) {
            var cells = tr.querySelectorAll('td');
            if (cells[colIndex]) {
                var v = cells[colIndex].textContent.trim();
                if (v && v !== '-' && v !== '') values[v] = (values[v] || 0) + 1;
            }
        });
        // Sort by frequency (most common first)
        return Object.keys(values)
            .sort(function(a, b) { return values[b] - values[a]; })
            .slice(0, maxValues);
    };

    // ── Auto-print polling for WhatsApp quotations ───────────────
    // Polls /quotations/print-queue every 15s. When a confirmed WA quote
    // is found, it fetches the ESC/POS bytes and sends to the connected
    // thermal printer. Falls back to browser print if no thermal is connected.

    var _autoPrintTimer = null;
    var _autoPrintEnabled = false;

    window.startAutoPrint = function() {
        if (_autoPrintTimer) return;
        _autoPrintEnabled = true;
        var token = localStorage.getItem('pos_token');
        if (!token) return;

        _autoPrintTimer = setInterval(function() {
            fetch('/pos/api/quotations/print-queue', {
                headers: { 'Authorization': 'Bearer ' + token }
            })
            .then(function(r) { return r.json(); })
            .then(function(d) {
                if (!d.data || !d.data.length) return;
                d.data.forEach(function(q) {
                    console.log('[auto-print] Cotización #' + q.id + ' confirmada por WhatsApp — imprimiendo...');
                    showToast('🖨️ Imprimiendo cotización #' + q.id + ' (WhatsApp)', 'ok');
                    autoPrintQuote(q.id, token);
                });
            })
            .catch(function() {}); // silent on errors
        }, 15000); // every 15 seconds

        console.log('[auto-print] Enabled — polling every 15s');
    };

    window.stopAutoPrint = function() {
        if (_autoPrintTimer) {
            clearInterval(_autoPrintTimer);
            _autoPrintTimer = null;
        }
        _autoPrintEnabled = false;
    };

    function autoPrintQuote(quoteId, token) {
        // Try thermal printer first (via NexusPrinter if loaded)
        if (typeof NexusPrinter !== 'undefined' && NexusPrinter.isConnected && NexusPrinter.isConnected()) {
            fetch('/pos/api/quotations/' + quoteId + '/print', {
                method: 'POST',
                headers: { 'Authorization': 'Bearer ' + token, 'Content-Type': 'application/json' },
                body: JSON.stringify({ printer_type: 'escpos_raw', width: 80 }),
            })
            .then(function(r) { return r.arrayBuffer(); })
            .then(function(buf) {
                NexusPrinter.sendRaw(new Uint8Array(buf));
                markPrinted(quoteId, token);
            })
            .catch(function(e) {
                console.error('[auto-print] Thermal print failed:', e);
                browserPrintQuote(quoteId, token);
            });
        } else {
            browserPrintQuote(quoteId, token);
        }
    }

    function browserPrintQuote(quoteId, token) {
        // Fallback: open a print-friendly window
        fetch('/pos/api/quotations/' + quoteId + '/print', {
            method: 'POST',
            headers: { 'Authorization': 'Bearer ' + token, 'Content-Type': 'application/json' },
            body: JSON.stringify({ printer_type: 'browser' }),
        })
        .then(function(r) { return r.json(); })
        .then(function(q) {
            var html = '<html><head><title>Cotización #' + q.id + '</title>';
            html += '<style>body{font-family:monospace;font-size:12px;width:80mm;margin:0 auto;padding:10px;}';
            html += 'h1{font-size:18px;text-align:center;margin:0;}';
            html += '.center{text-align:center;}.right{text-align:right;}';
            html += 'hr{border:none;border-top:1px dashed #000;}';
            html += 'table{width:100%;border-collapse:collapse;}td{padding:2px 4px;}</style></head><body>';
            html += '<h1>COTIZACIÓN</h1>';
            html += '<p class="center">COT-' + q.id + '</p>';
            html += '<p>Fecha: ' + (q.created_at || '').substring(0, 10) + '</p>';
            if (q.customer_name) html += '<p>Cliente: ' + q.customer_name + '</p>';
            if (q.wa_phone) html += '<p>WhatsApp: ' + q.wa_phone + '</p>';
            html += '<hr><table>';
            (q.items || []).forEach(function(it) {
                html += '<tr><td>' + it.quantity + 'x ' + it.name + '</td><td class="right">$' + it.subtotal.toFixed(2) + '</td></tr>';
                if (it.part_number) html += '<tr><td colspan="2" style="font-size:10px;color:#666;">  #' + it.part_number + '</td></tr>';
            });
            html += '</table><hr>';
            html += '<p class="right">Subtotal: $' + q.subtotal.toFixed(2) + '</p>';
            html += '<p class="right">IVA: $' + q.tax_total.toFixed(2) + '</p>';
            html += '<p class="right" style="font-size:16px;font-weight:bold;">TOTAL: $' + q.total.toFixed(2) + '</p>';
            html += '<hr><p class="center" style="font-size:10px;">Esta cotización no es comprobante fiscal<br>Precios sujetos a disponibilidad</p>';
            html += '</body></html>';

            var w = window.open('', '_blank', 'width=400,height=600');
            w.document.write(html);
            w.document.close();
            setTimeout(function() { w.print(); }, 500);
            markPrinted(quoteId, token);
        })
        .catch(function(e) {
            console.error('[auto-print] Browser print failed:', e);
        });
    }

    function markPrinted(quoteId, token) {
        fetch('/pos/api/quotations/' + quoteId + '/mark-printed', {
            method: 'POST',
            headers: { 'Authorization': 'Bearer ' + token },
        }).catch(function() {});
    }

    // Auto-start polling on pages that are likely to have a printer
    // (POS sale page and quotations page)
    if (window.location.pathname.indexOf('/pos/sale') !== -1 ||
        window.location.pathname.indexOf('/pos/quotation') !== -1 ||
        window.location.pathname.indexOf('/pos/dashboard') !== -1) {
        var _initToken = localStorage.getItem('pos_token');
        if (_initToken) {
            setTimeout(function() { startAutoPrint(); }, 3000);
        }
    }

    // ── Barcode Scanner Feedback ──────────────────────────────────
    window.BarcodeFeedback = {
        _audioCtx: null,
        success: function() {
            this._beep(800, 0.1, 'sine');
            this._flash('#22c55e');
            if (navigator.vibrate) navigator.vibrate(50);
        },
        error: function() {
            this._beep(200, 0.15, 'square');
            this._flash('#ef4444');
            if (navigator.vibrate) navigator.vibrate([80, 50, 80]);
        },
        _beep: function(freq, duration, type) {
            try {
                if (!this._audioCtx) this._audioCtx = new (window.AudioContext || window.webkitAudioContext)();
                var osc = this._audioCtx.createOscillator();
                var gain = this._audioCtx.createGain();
                osc.type = type || 'sine';
                osc.frequency.value = freq;
                gain.gain.value = 0.1;
                osc.connect(gain);
                gain.connect(this._audioCtx.destination);
                osc.start();
                osc.stop(this._audioCtx.currentTime + duration);
            } catch(e) {}
        },
        _flash: function(color) {
            var el = document.createElement('div');
            el.style.cssText = 'position:fixed;inset:0;z-index:99999;opacity:0.3;background:' + color + ';pointer-events:none;transition:opacity 0.2s;';
            document.body.appendChild(el);
            setTimeout(function() { el.style.opacity = '0'; }, 50);
            setTimeout(function() { el.remove(); }, 300);
        }
    };

    // ── Saved Filters ─────────────────────────────────────────────
    window.SavedFilters = {
        _key: function(page) { return 'pos_filters_' + (page || window.location.pathname); },
        save: function(name, filters) {
            var key = this._key();
            var saved = this.list();
            saved.push({ name: name, filters: filters, created: Date.now() });
            localStorage.setItem(key, JSON.stringify(saved));
        },
        list: function() {
            try { return JSON.parse(localStorage.getItem(this._key()) || '[]'); } catch(e) { return []; }
        },
        remove: function(name) {
            var saved = this.list().filter(function(f) { return f.name !== name; });
            localStorage.setItem(this._key(), JSON.stringify(saved));
        },
        renderChips: function(containerId, onApply) {
            var container = document.getElementById(containerId);
            if (!container) return;
            var saved = this.list();
            if (!saved.length) { container.innerHTML = ''; return; }
            var html = '';
            saved.forEach(function(f) {
                html += '<span class="filter-chip">' + esc(f.name) +
                    '<button class="filter-chip__remove" onclick="SavedFilters.remove(\'' + esc(f.name) + '\');SavedFilters.renderChips(\'' + containerId + '\');">&times;</button></span>';
            });
            container.innerHTML = html;
            container.querySelectorAll('.filter-chip').forEach(function(chip, i) {
                chip.addEventListener('click', function(e) {
                    if (e.target.classList.contains('filter-chip__remove')) return;
                    if (onApply) onApply(saved[i].filters);
                });
            });
        }
    };

    // ── Resizable Columns ─────────────────────────────────────────
    window.makeTableResizable = function(tableSelector) {
        var table = document.querySelector(tableSelector);
        if (!table) return;
        var ths = table.querySelectorAll('thead th');
        ths.forEach(function(th, i) {
            if (i >= ths.length - 1) return; // skip last column
            var handle = document.createElement('div');
            handle.className = 'resize-handle';
            th.appendChild(handle);

            handle.addEventListener('mousedown', function(e) {
                e.preventDefault();
                var startX = e.pageX;
                var startW = th.offsetWidth;
                th.classList.add('is-resizing');

                function onMove(ev) {
                    var newW = Math.max(60, startW + (ev.pageX - startX));
                    th.style.width = newW + 'px';
                    th.style.minWidth = newW + 'px';
                }
                function onUp() {
                    th.classList.remove('is-resizing');
                    document.removeEventListener('mousemove', onMove);
                    document.removeEventListener('mouseup', onUp);
                }
                document.addEventListener('mousemove', onMove);
                document.addEventListener('mouseup', onUp);
            });
        });
    };

    // ── Density / Touch Mode Toggles ──────────────────────────────
    window.DensityToggle = {
        set: function(density) {
            document.documentElement.setAttribute('data-density', density);
            localStorage.setItem('pos_density', density);
        },
        init: function() {
            var saved = localStorage.getItem('pos_density') || 'normal';
            document.documentElement.setAttribute('data-density', saved);
        }
    };
    window.TouchModeToggle = {
        set: function(enabled) {
            document.documentElement.setAttribute('data-touch', enabled ? 'true' : 'false');
            localStorage.setItem('pos_touch_mode', enabled ? 'true' : 'false');
        },
        init: function() {
            var saved = localStorage.getItem('pos_touch_mode') === 'true';
            document.documentElement.setAttribute('data-touch', saved ? 'true' : 'false');
        }
    };
    DensityToggle.init();
    TouchModeToggle.init();

    // ── Notifications Dropdown (functional) ───────────────────────
    window.NotificationsDropdown = {
        _visible: false,
        _el: null,
        toggle: function() {
            if (this._visible) { this.hide(); return; }
            this.show();
        },
        show: function() {
            if (this._el) this._el.remove();
            var btn = document.getElementById('notifDropdownBtn');
            var el = document.createElement('div');
            el.className = 'notif-dropdown';
            el.innerHTML = '<div class="notif-dropdown__header">Notificaciones <button onclick="NotificationsDropdown.hide()" style="background:none;border:none;color:var(--color-text-muted);cursor:pointer;font-size:16px;">✕</button></div>' +
                '<div class="notif-dropdown__list" id="notifDropdownList"><div class="notif-dropdown__empty">Sin notificaciones nuevas</div></div>';
            if (btn) {
                btn.parentElement.style.position = 'relative';
                btn.parentElement.appendChild(el);
            } else {
                document.body.appendChild(el);
            }
            this._el = el;
            this._visible = true;
            this._load();
            setTimeout(function() {
                document.addEventListener('click', function handler(e) {
                    if (!el.contains(e.target) && e.target !== btn) {
                        NotificationsDropdown.hide();
                        document.removeEventListener('click', handler);
                    }
                });
            }, 100);
        },
        hide: function() { if (this._el) { this._el.remove(); this._el = null; } this._visible = false; },
        _load: function() {
            // Stub: can be wired to /api/notifications endpoint
            var list = document.getElementById('notifDropdownList');
            if (!list) return;
            // Example: show inventory alerts count if available
            var token = localStorage.getItem('pos_token');
            if (!token) return;
            fetch('/pos/api/inventory/alerts', { headers: { 'Authorization': 'Bearer ' + token } })
                .then(function(r) { return r.json(); })
                .then(function(d) {
                    var count = (d.counts || {}).critical || 0;
                    if (count > 0) {
                        list.innerHTML = '<div class="notif-dropdown__item notif-dropdown__item--unread" onclick="window.location.href=\'/pos/inventory#alertas\'">' +
                            '<div class="notif-dropdown__icon">⚠️</div>' +
                            '<div class="notif-dropdown__content"><div class="notif-dropdown__title">' + count + ' producto' + (count > 1 ? 's' : '') + ' sin stock</div>' +
                            '<div class="notif-dropdown__time">Stock crítico</div></div></div>';
                    }
                }).catch(function() {});
        }
    };

    // ── Ticket Preview Helper ─────────────────────────────────────
    window.previewTicket = function(ticketData) {
        var data = ticketData || {};
        var items = (data.items || []).map(function(it) {
            return '<div class="ticket-preview__row"><span>' + (it.quantity || 1) + 'x ' + esc(it.name) + '</span><span>$' + (it.subtotal || 0).toFixed(2) + '</span></div>';
        }).join('');
        var html = '<div class="ticket-preview">' +
            '<div class="ticket-preview__header"><div class="ticket-preview__title">Nexus Autoparts</div><div class="ticket-preview__meta">' + (data.store || 'Sucursal Centro') + '</div></div>' +
            '<div class="ticket-preview__meta" style="text-align:center;margin-bottom:8px;">' + new Date().toLocaleString('es-MX') + '</div>' +
            '<div class="ticket-preview__row"><span>Ticket #' + (data.id || '---') + '</span></div>' +
            '<hr style="border:none;border-top:1px dashed #ccc;margin:8px 0;">' +
            items +
            '<hr style="border:none;border-top:1px dashed #ccc;margin:8px 0;">' +
            '<div class="ticket-preview__row"><span>Subtotal</span><span>$' + (data.subtotal || 0).toFixed(2) + '</span></div>' +
            '<div class="ticket-preview__row"><span>IVA</span><span>$' + (data.tax || 0).toFixed(2) + '</span></div>' +
            '<div class="ticket-preview__total"><span>TOTAL</span><span>$' + (data.total || 0).toFixed(2) + '</span></div>' +
            '<div style="text-align:center;font-size:10px;color:#666;margin-top:12px;">Gracias por su compra</div>' +
            '</div>';
        return html;
    };

    // ── Image Comparator Helper ───────────────────────────────────
    window.initImageComparator = function(containerSelector) {
        var container = document.querySelector(containerSelector);
        if (!container) return;
        var overlay = container.querySelector('.img-compare__overlay');
        var handle = container.querySelector('.img-compare__handle');
        if (!overlay || !handle) return;

        function move(x) {
            var rect = container.getBoundingClientRect();
            var pct = Math.max(0, Math.min(100, ((x - rect.left) / rect.width) * 100));
            overlay.style.width = pct + '%';
            handle.style.left = pct + '%';
        }
        handle.addEventListener('mousedown', function(e) {
            e.preventDefault();
            function onMove(ev) { move(ev.pageX); }
            function onUp() { document.removeEventListener('mousemove', onMove); document.removeEventListener('mouseup', onUp); }
            document.addEventListener('mousemove', onMove);
            document.addEventListener('mouseup', onUp);
        });
        handle.addEventListener('touchstart', function(e) {
            function onMove(ev) { move(ev.touches[0].pageX); }
            function onUp() { document.removeEventListener('touchmove', onMove); document.removeEventListener('touchend', onUp); }
            document.addEventListener('touchmove', onMove);
            document.addEventListener('touchend', onUp);
        });
    };

    // ── Infinite Scroll Helper ────────────────────────────────────
    window.InfiniteScroll = function(opts) {
        opts = opts || {};
        var container = opts.container || window;
        var threshold = opts.threshold || 200;
        var loading = false;
        var observer = new IntersectionObserver(function(entries) {
            if (entries[0].isIntersecting && !loading && opts.onLoad) {
                loading = true;
                opts.onLoad(function() { loading = false; });
            }
        }, { root: container === window ? null : container, rootMargin: threshold + 'px' });
        var sentinel = document.createElement('div');
        sentinel.style.cssText = 'height:1px;';
        (opts.sentinelParent || document.body).appendChild(sentinel);
        observer.observe(sentinel);
        return { disconnect: function() { observer.disconnect(); sentinel.remove(); } };
    };

    // ── Sparkline Renderer ────────────────────────────────────────
    window.renderSparkline = function(containerSelector, values, opts) {
        opts = opts || {};
        var container = typeof containerSelector === 'string' ? document.querySelector(containerSelector) : containerSelector;
        if (!container || !values || !values.length) return;
        var max = Math.max.apply(null, values) || 1;
        var min = Math.min.apply(null, values);
        var range = max - min || 1;
        var cls = opts.trend === 'up' ? 'sparkline--up' : (opts.trend === 'down' ? 'sparkline--down' : '');
        var html = '<div class="sparkline ' + cls + '">';
        values.forEach(function(v) {
            var h = Math.max(4, Math.round(((v - min) / range) * 100));
            html += '<div class="sparkline__bar" style="height:' + h + '%" title="' + (opts.prefix || '') + v + '"></div>';
        });
        html += '</div>';
        container.innerHTML = html;
    };

})();
