(function (window, document) {
    'use strict';

    var productsUrlSyncTimer = null;

    function zertiPersianToLatinDigits(str) {
        return window.zertiToLatinDigits(str);
    }

    function zertiLatinToPersianDigits(str) {
        return window.zertiLocalizeDigits(str);
    }

    function zertiArabicToPersianDigits(str) {
        if (window.zertiDigitLocale && window.zertiDigitLocale.script === 'persian') {
            var arabic = '٠١٢٣٤٥٦٧٨٩';
            var persian = '۰۱۲۳۴۵۶۷۸۹';
            return String(str).replace(/[٠-٩]/g, function (ch) {
                var idx = arabic.indexOf(ch);
                return idx >= 0 ? persian.charAt(idx) : ch;
            });
        }

        return String(str);
    }

    /**
     * Keep only numeric characters; show Persian digits in the input.
     *
     * @param {HTMLInputElement} input
     * @param {string} field price | sale_price | stock
     */
    function normalizeInlineNumericInput(input, field) {
        if (!input) {
            return;
        }
        var allowDecimal = field === 'price' || field === 'sale_price';
        var latin = zertiPersianToLatinDigits(zertiArabicToPersianDigits(String(input.value || '')));
        var normalized = '';
        var hasSeparator = false;
        var i;
        for (i = 0; i < latin.length; i++) {
            var ch = latin.charAt(i);
            if (ch >= '0' && ch <= '9') {
                normalized += ch;
            } else if (allowDecimal && !hasSeparator && (ch === '.' || ch === ',' || ch === '/' || ch === '٫')) {
                normalized += '.';
                hasSeparator = true;
            }
        }
        input.value = zertiLatinToPersianDigits(normalized);
    }

    function readGridCfg() {
        return typeof window.zertiProductsGrid === 'object' && window.zertiProductsGrid !== null
            ? window.zertiProductsGrid
            : {};
    }

    function gridI18n() {
        var cfg = readGridCfg();
        return (cfg && cfg.i18n) || {};
    }

    function isValidProductsStockFilter(slug) {
        return slug === 'zero' || slug === 'one' || slug === 'available';
    }

    function productsStockFilterLabel(cfg, stockFilter) {
        var i18n = (cfg && cfg.i18n) || {};
        if (stockFilter === 'one') {
            return i18n.stockFilterOne || '';
        }
        if (stockFilter === 'available') {
            return i18n.stockFilterAvailable || '';
        }
        return i18n.stockFilterZero || '';
    }

    function readProductsStockFilter(cfg) {
        try {
            var sp = new URLSearchParams(window.location.search);
            var pStock = cfg.paramProductsStock || 'zerti_products_stock';
            var raw = sp.get(pStock);
            return raw && isValidProductsStockFilter(raw) ? raw : '';
        } catch (eStock) {
            return '';
        }
    }

    function readUrlParams(cfg) {
        var out = { search: '', orderCol: NaN, orderDir: 'desc', stockFilter: '' };
        try {
            var sp = new URLSearchParams(window.location.search);
            var ps = cfg.paramSearch || 'zerti_products_search';
            var pc = cfg.paramOrderCol || 'zerti_products_order_col';
            var pd = cfg.paramOrderDir || 'zerti_products_order_dir';
            out.search = sp.get(ps) ? String(sp.get(ps)) : '';
            out.stockFilter = readProductsStockFilter(cfg);
            var colRaw = sp.get(pc);
            if (colRaw !== null && colRaw !== '') {
                out.orderCol = parseInt(colRaw, 10);
            }
            var dirRaw = (sp.get(pd) || 'desc').toLowerCase();
            out.orderDir = dirRaw === 'asc' ? 'asc' : 'desc';
        } catch (e1) {
            return out;
        }
        return out;
    }

    function replaceProductsGridUrlParams(api, cfg, searchInput) {
        if (productsUrlSyncTimer) {
            window.clearTimeout(productsUrlSyncTimer);
        }
        productsUrlSyncTimer = window.setTimeout(function () {
            productsUrlSyncTimer = null;
            try {
                var u = new URL(window.location.href);
                var ps = cfg.paramSearch || 'zerti_products_search';
                var pc = cfg.paramOrderCol || 'zerti_products_order_col';
                var pd = cfg.paramOrderDir || 'zerti_products_order_dir';
                var pStock = cfg.paramProductsStock || 'zerti_products_stock';
                var stockFilter = readProductsStockFilter(cfg);
                var q = searchInput && searchInput.value ? zertiPersianToLatinDigits(searchInput.value).trim() : '';
                if (q) {
                    u.searchParams.set(ps, q);
                } else {
                    u.searchParams.delete(ps);
                }
                var state = api.getColumnState ? api.getColumnState() : [];
                var sorted = state.filter(function (col) {
                    return col.sort;
                })[0];
                if (sorted && typeof sorted.colId === 'string' && /^c[0-9]+$/.test(sorted.colId)) {
                    u.searchParams.set(pc, sorted.colId.slice(1));
                    u.searchParams.set(pd, sorted.sort === 'asc' ? 'asc' : 'desc');
                }
                if (stockFilter && isValidProductsStockFilter(stockFilter)) {
                    u.searchParams.set(pStock, stockFilter);
                } else {
                    u.searchParams.delete(pStock);
                }
                var next = u.pathname + u.search + window.location.hash;
                var cur = window.location.pathname + window.location.search + window.location.hash;
                if (next !== cur) {
                    window.history.replaceState(null, '', next);
                }
            } catch (e2) {}
        }, 280);
    }

    function htmlRenderer(params) {
        return params.value || '';
    }

    function emptyProductImageHtml() {
        var noImage = gridI18n().noImage || '';
        return (
            '<div class="zerti-products-grid-image-wrap zerti-products-grid-image-wrap--empty" role="img" aria-label="' +
            escapeAttr(noImage) +
            '">' +
            '<i class="fa-solid fa-image zerti-products-grid-image-placeholder" aria-hidden="true"></i>' +
            '</div>'
        );
    }

    function showEmptyProductImage(host) {
        host.innerHTML = emptyProductImageHtml();
    }

    function bindProductImageLoad(host, img) {
        function useEmptyIfBroken() {
            if (!img.naturalWidth || !img.naturalHeight || img.naturalWidth < 2 || img.naturalHeight < 2) {
                showEmptyProductImage(host);
            }
        }
        img.addEventListener('error', function () {
            showEmptyProductImage(host);
        });
        if (img.complete) {
            useEmptyIfBroken();
        } else {
            img.addEventListener('load', useEmptyIfBroken, { once: true });
        }
    }

    function productImageCellRenderer(params) {
        var host = document.createElement('div');
        host.className = 'zerti-products-grid-image-cell-host';
        var html = params.value;
        if (!html || String(html).trim() === '') {
            showEmptyProductImage(host);
            return host;
        }
        host.innerHTML = html;
        var wrap = host.querySelector('.zerti-products-grid-image-wrap');
        if (!wrap) {
            showEmptyProductImage(host);
            return host;
        }
        if (wrap.classList.contains('zerti-products-grid-image-wrap--empty')) {
            if (!wrap.querySelector('.zerti-products-grid-image-placeholder')) {
                wrap.insertAdjacentHTML(
                    'beforeend',
                    '<i class="fa-solid fa-image zerti-products-grid-image-placeholder" aria-hidden="true"></i>'
                );
            }
            return host;
        }
        var img = wrap.querySelector('img.zerti-products-grid-image');
        if (!img) {
            showEmptyProductImage(host);
            return host;
        }
        var src = img.getAttribute('src') || '';
        if (src === '') {
            showEmptyProductImage(host);
            return host;
        }
        bindProductImageLoad(host, img);
        return host;
    }

    function productNameCellRenderer(params) {
        var host = document.createElement('div');
        host.className = 'zerti-products-grid-name-cell-host';
        var name = params && params.value != null ? String(params.value) : '';
        if (!name && params && params.data && params.data.name != null) {
            name = String(params.data.name);
        }
        var label = document.createElement('span');
        label.className = 'zerti-products-grid-name-cell-label';
        label.textContent = name || '—';
        if (name) {
            label.setAttribute('title', name);
            host.setAttribute('title', name);
        }
        host.appendChild(label);
        return host;
    }

    var PRODUCTS_DETAIL_COL_IDS = { c0: true, c1: true, c2: true, c3: true };

    function openProductDetail(productId) {
        var pid = Number(productId);
        if (!(pid > 0)) {
            return;
        }
        if (typeof window.zertiOpenInventoryProductEdit === 'function') {
            window.zertiOpenInventoryProductEdit(pid);
            return;
        }
        if (typeof window.zertiAdminRouteUrl === 'function') {
            var url = window.zertiAdminRouteUrl({ tab: 'inventory', inv: 'warehouse', product: pid });
            if (url) {
                window.location.href = url;
            }
        }
    }

    function handleProductsDetailCellClicked(params) {
        if (!params || !params.data) {
            return;
        }
        var colId =
            params.column && typeof params.column.getColId === 'function'
                ? params.column.getColId()
                : params.colDef && params.colDef.colId
                  ? String(params.colDef.colId)
                  : '';
        if (!PRODUCTS_DETAIL_COL_IDS[colId]) {
            return;
        }
        openProductDetail(params.data.idRaw);
    }

    function escapeHtml(str) {
        return String(str)
            .replace(/&/g, '&amp;')
            .replace(/</g, '&lt;')
            .replace(/>/g, '&gt;')
            .replace(/"/g, '&quot;');
    }

    function escapeAttr(str) {
        return escapeHtml(str).replace(/'/g, '&#39;');
    }

    function getInlineEditor(gridEl) {
        return gridEl && gridEl.__zertiInlineEditor ? gridEl.__zertiInlineEditor : null;
    }

    function setInlineEditor(gridEl, editor) {
        if (gridEl) {
            gridEl.__zertiInlineEditor = editor || null;
        }
    }

    function refreshEditableCells(api, rowId) {
        if (!api || !api.refreshCells) {
            return;
        }
        var columns = ['c4', 'c5', 'c6'];
        if (rowId) {
            var node = api.getRowNode ? api.getRowNode(String(rowId)) : null;
            if (node) {
                api.refreshCells({ rowNodes: [node], columns: columns, force: true });
                return;
            }
        }
        api.refreshCells({ columns: columns, force: true });
    }

    function buildInlineEditorHtml(data, field) {
        var i18n = gridI18n();
        var saveLabel = i18n.save || '';
        var cancelLabel = i18n.cancel || '';
        var value = '';
        if (field === 'price') {
            value = data.priceValue || '';
        } else if (field === 'sale_price') {
            value = data.salePriceValue || '';
        } else if (field === 'stock' && data.stockIsNumeric) {
            value = data.stockValue || '';
        }
        var displayValue = value !== '' ? zertiLatinToPersianDigits(String(value)) : '';
        var inputMode = field === 'stock' ? 'numeric' : 'decimal';
        return (
            '<div class="zerti-products-grid-inline-editor d-inline-flex align-items-center gap-1">' +
            '<input type="text" class="form-control form-control-sm zerti-products-grid-inline-input" value="' +
            escapeAttr(displayValue) +
            '" dir="ltr" lang="fa" inputmode="' +
            inputMode +
            '" pattern="[۰-۹٠-٩.,/٫]*" autocomplete="off" autocorrect="off" spellcheck="false" data-zerti-field="' +
            escapeAttr(field) +
            '" data-zerti-product="' +
            String(data.idRaw) +
            '">' +
            '<div class="zerti-products-grid-inline-actions d-flex flex-column gap-1">' +
            '<button type="button" class="btn btn-success zerti-products-grid-inline-save" title="' +
            escapeAttr(saveLabel) +
            '" aria-label="' +
            escapeAttr(saveLabel) +
            '" data-zerti-field="' +
            escapeAttr(field) +
            '" data-zerti-product="' +
            String(data.idRaw) +
            '"><i class="fa-solid fa-check" aria-hidden="true"></i></button>' +
            '<button type="button" class="btn btn-outline-secondary zerti-products-grid-inline-cancel" title="' +
            escapeAttr(cancelLabel) +
            '" aria-label="' +
            escapeAttr(cancelLabel) +
            '" data-zerti-field="' +
            escapeAttr(field) +
            '" data-zerti-product="' +
            String(data.idRaw) +
            '"><i class="fa-solid fa-xmark" aria-hidden="true"></i></button>' +
            '</div></div>'
        );
    }

    function productsEditableCellRenderer(field) {
        return function (params) {
            if (!params.data) {
                return '';
            }
            var editableKey =
                field === 'price'
                    ? 'priceEditable'
                    : field === 'sale_price'
                      ? 'salePriceEditable'
                      : 'stockEditable';
            var displayHtml =
                field === 'price'
                    ? params.data.price || '—'
                    : field === 'sale_price'
                      ? params.data.salePrice || '—'
                      : escapeHtml(params.data.stock || '—');
            if (!params.data[editableKey]) {
                return field === 'stock' ? displayHtml : displayHtml;
            }
            var gridEl =
                params.eGridCell && params.eGridCell.closest
                    ? params.eGridCell.closest('.zerti-products-grid')
                    : document.getElementById('zertiProductsGrid');
            var editor = gridEl ? getInlineEditor(gridEl) : null;
            if (editor && String(editor.rowId) === String(params.data.idRaw) && editor.field === field) {
                return buildInlineEditorHtml(params.data, field);
            }
            var triggerClass =
                field === 'price'
                    ? ' zerti-products-grid-editable-trigger--price'
                    : field === 'sale_price'
                      ? ' zerti-products-grid-editable-trigger--sale-price'
                      : '';
            return (
                '<button type="button" class="btn btn-link btn-sm p-0 zerti-products-grid-editable-trigger' +
                triggerClass +
                '" data-zerti-field="' +
                escapeAttr(field) +
                '" data-zerti-product="' +
                String(params.data.idRaw) +
                '">' +
                (field === 'stock' ? escapeHtml(params.data.stock || '—') : displayHtml) +
                '</button>'
            );
        };
    }

    function getProductsPanel(gridEl) {
        return gridEl && gridEl.closest ? gridEl.closest('.zerti-products-panel') : null;
    }

    function getZertiAdminRoot() {
        return document.getElementById('zertiAdmin');
    }

    function getProductsAlertOverlay() {
        var root = getZertiAdminRoot();
        if (!root) {
            return null;
        }
        var ov = root.querySelector('.zerti-products-grid-alert-overlay');
        if (!ov) {
            ov = document.createElement('div');
            ov.className = 'zerti-products-grid-alert-overlay';
            ov.setAttribute('role', 'dialog');
            ov.setAttribute('aria-modal', 'true');
            ov.setAttribute('aria-labelledby', 'zertiProductsGridAlertMessage');
            ov.hidden = true;
            ov.innerHTML =
                '<div class="zerti-products-grid-alert-dialog alert alert-danger">' +
                '<p id="zertiProductsGridAlertMessage" class="zerti-products-grid-alert-message mb-0"></p>' +
                '<button type="button" class="btn btn-light zerti-products-grid-alert-ok"></button>' +
                '</div>';
            root.appendChild(ov);
            var okBtn = ov.querySelector('.zerti-products-grid-alert-ok');
            if (okBtn) {
                okBtn.addEventListener('click', function (event) {
                    event.preventDefault();
                    event.stopPropagation();
                    var gridEl = document.getElementById('zertiProductsGrid');
                    if (gridEl && gridEl.__zertiAgGridApi && getInlineEditor(gridEl)) {
                        closeInlineEditor(gridEl, gridEl.__zertiAgGridApi);
                    }
                    hideProductsGridAlert();
                });
            }
        }
        return ov;
    }

    function hideProductsGridAlert() {
        var ov = getProductsAlertOverlay();
        if (ov) {
            ov.hidden = true;
        }
    }

    function showProductsGridAlert(gridEl, message) {
        if (!message) {
            return;
        }
        var ov = getProductsAlertOverlay();
        if (!ov) {
            return;
        }
        var msgEl = ov.querySelector('.zerti-products-grid-alert-message');
        if (!ov.hidden && msgEl && msgEl.textContent === message) {
            return;
        }
        var cfg = readGridCfg();
        var i18n = cfg.i18n || {};
        var okBtn = ov.querySelector('.zerti-products-grid-alert-ok');
        if (msgEl) {
            msgEl.textContent = message;
        }
        if (okBtn) {
            okBtn.textContent = i18n.alertOk || '';
        }
        ov.hidden = false;
        if (okBtn && typeof okBtn.focus === 'function') {
            okBtn.focus();
        }
    }

    function getProductGridRowElement(gridEl, api, productId) {
        if (!gridEl || !api || typeof api.getRowNode !== 'function') {
            return null;
        }
        var id = String(productId);
        var node = api.getRowNode(id);
        if (!node) {
            return null;
        }
        var escapedId =
            typeof CSS !== 'undefined' && typeof CSS.escape === 'function' ? CSS.escape(id) : id.replace(/"/g, '\\"');
        var rowEl = gridEl.querySelector('.ag-row[row-id="' + escapedId + '"]');
        if (!rowEl && node.rowIndex != null) {
            rowEl = gridEl.querySelector('.ag-row[row-index="' + String(node.rowIndex) + '"]');
        }

        return rowEl;
    }

    function showProductsSaveOverlay(gridEl, api, productId, message) {
        var rowEl = getProductGridRowElement(gridEl, api, productId);
        if (!rowEl) {
            return null;
        }
        var ov = rowEl.querySelector('.zerti-products-grid-row-save-overlay');
        if (!ov) {
            ov = document.createElement('div');
            ov.className = 'zerti-products-grid-row-save-overlay';
            ov.setAttribute('role', 'status');
            ov.setAttribute('aria-live', 'polite');
            rowEl.appendChild(ov);
        }
        ov.innerHTML =
            '<span class="spinner-border spinner-border-sm" aria-hidden="true"></span>' +
            (message
                ? '<span class="zerti-products-grid-row-save-overlay__label">' + escapeHtml(message) + '</span>'
                : '');
        rowEl.classList.add('zerti-products-grid-row--saving');
        ov.hidden = false;

        return ov;
    }

    function hideProductsSaveOverlay(gridEl, api, productId) {
        var rowEl = getProductGridRowElement(gridEl, api, productId);
        if (!rowEl) {
            return;
        }
        rowEl.classList.remove('zerti-products-grid-row--saving');
        var ov = rowEl.querySelector('.zerti-products-grid-row-save-overlay');
        if (ov) {
            ov.hidden = true;
        }
    }

    function openInlineEditor(gridEl, api, productId, field) {
        if (getInlineEditor(gridEl)) {
            closeInlineEditor(gridEl, api);
        }
        setInlineEditor(gridEl, { rowId: String(productId), field: field });
        refreshEditableCells(api, productId);
        window.requestAnimationFrame(function () {
            var input = gridEl.querySelector(
                '.zerti-products-grid-inline-input[data-zerti-product="' + String(productId) + '"][data-zerti-field="' + field + '"]'
            );
            if (input) {
                normalizeInlineNumericInput(input, field);
                bindInlineInputKeydown(input, gridEl, api);
                input.focus();
                if (typeof input.select === 'function') {
                    input.select();
                }
            }
        });
    }

    function closeInlineEditor(gridEl, api) {
        var editor = getInlineEditor(gridEl);
        if (editor && gridEl) {
            var activeInput = gridEl.querySelector(
                '.zerti-products-grid-inline-input[data-zerti-product="' +
                    String(editor.rowId) +
                    '"][data-zerti-field="' +
                    editor.field +
                    '"]'
            );
            unbindInlineInputKeydown(activeInput);
        }
        setInlineEditor(gridEl, null);
        refreshEditableCells(api, null);
    }

    function commitInlineEditorFromInput(gridEl, api, input) {
        if (!input) {
            return;
        }
        var productId = Number(input.getAttribute('data-zerti-product'));
        var field = input.getAttribute('data-zerti-field') || '';
        if (!(productId > 0) || (field !== 'price' && field !== 'sale_price' && field !== 'stock')) {
            return;
        }
        var rawValue = input.value ? zertiPersianToLatinDigits(input.value).trim() : '';
        if (rawValue === '' && field !== 'sale_price') {
            return;
        }
        if (!canSaveSalePrice(api, productId, field, rawValue)) {
            closeInlineEditor(gridEl, api);
            alertSalePriceNeedsRegularPrice(gridEl, readGridCfg());
            return;
        }
        saveProductInlineEdit(gridEl, api, productId, field, rawValue);
    }

    function handleInlineEditorKeydown(event, gridEl, api) {
        var input =
            event.target && event.target.closest
                ? event.target.closest('.zerti-products-grid-inline-input')
                : null;
        if (!input || !gridEl.contains(input)) {
            return;
        }
        var key = event.key;
        if (key === 'Escape' || key === 'Esc') {
            event.preventDefault();
            event.stopPropagation();
            closeInlineEditor(gridEl, api);
            return;
        }
        if (key === 'Enter' || key === 'NumpadEnter') {
            event.preventDefault();
            event.stopPropagation();
            commitInlineEditorFromInput(gridEl, api, input);
        }
    }

    function unbindInlineInputKeydown(input) {
        if (!input || !input.__zertiInlineKeydownHandler) {
            return;
        }
        input.removeEventListener('keydown', input.__zertiInlineKeydownHandler, true);
        input.__zertiInlineKeydownHandler = null;
        input.__zertiInlineKeydownBound = false;
    }

    function bindInlineInputKeydown(input, gridEl, api) {
        if (!input) {
            return;
        }
        unbindInlineInputKeydown(input);
        var handler = function (event) {
            handleInlineEditorKeydown(event, gridEl, api);
        };
        input.__zertiInlineKeydownHandler = handler;
        input.__zertiInlineKeydownBound = true;
        input.addEventListener('keydown', handler, true);
    }

    function getProductRowData(api, productId) {
        if (!api || typeof api.getRowNode !== 'function') {
            return null;
        }
        var node = api.getRowNode(String(productId));
        return node && node.data ? node.data : null;
    }

    function productHasRegularPrice(api, productId) {
        var data = getProductRowData(api, productId);
        if (!data) {
            return true;
        }
        if (data.hasRegularPrice === true) {
            return true;
        }
        if (Number(data.priceSort || 0) > 0) {
            return true;
        }
        var priceValue = String(data.priceValue || '').trim();
        if (priceValue !== '') {
            var parsed = parseFloat(zertiPersianToLatinDigits(priceValue).replace(/,/g, ''));
            if (!isNaN(parsed) && parsed > 0) {
                return true;
            }
        }
        return false;
    }

    function alertSalePriceNeedsRegularPrice(gridEl, cfg) {
        var i18n = (cfg && cfg.i18n) || readGridCfg().i18n || {};
        var msg = i18n.salePriceNeedsRegularPrice || '';
        showProductsGridAlert(gridEl, msg);
    }

    function canSaveSalePrice(api, productId, field, rawValue) {
        if (field !== 'sale_price' || rawValue === '') {
            return true;
        }
        return productHasRegularPrice(api, productId);
    }

    function applyProductRowUpdate(api, productId, rowData) {
        if (!api || !rowData || typeof api.getRowNode !== 'function') {
            return false;
        }
        var node = api.getRowNode(String(productId));
        if (!node || !node.data) {
            return false;
        }
        Object.keys(rowData).forEach(function (key) {
            if (Object.prototype.hasOwnProperty.call(rowData, key)) {
                node.data[key] = rowData[key];
            }
        });
        if (typeof node.setData === 'function') {
            node.setData(node.data);
        }
        refreshEditableCells(api, productId);
        return true;
    }

    function saveProductInlineEdit(gridEl, api, productId, field, value) {
        var cfg = readGridCfg();
        var i18n = cfg.i18n || {};
        var saveBtn = gridEl.querySelector(
            '.zerti-products-grid-inline-save[data-zerti-product="' + String(productId) + '"][data-zerti-field="' + field + '"]'
        );
        var cancelBtn = gridEl.querySelector(
            '.zerti-products-grid-inline-cancel[data-zerti-product="' + String(productId) + '"][data-zerti-field="' + field + '"]'
        );
        var input = gridEl.querySelector(
            '.zerti-products-grid-inline-input[data-zerti-product="' + String(productId) + '"][data-zerti-field="' + field + '"]'
        );
        if (saveBtn) {
            saveBtn.disabled = true;
        }
        if (cancelBtn) {
            cancelBtn.disabled = true;
        }
        if (input) {
            input.readOnly = true;
        }
        showProductsSaveOverlay(gridEl, api, productId, i18n.working || i18n.saving || '');

        var body = new URLSearchParams({
            action: cfg.updateAction || 'zerti_product_quick_update',
            nonce: cfg.nonce || '',
            product_id: String(productId),
            field: field,
            value: value,
        });

        return window
            .fetch(cfg.ajaxUrl || window.ajaxurl || '', {
                method: 'POST',
                credentials: 'same-origin',
                headers: {
                    'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8',
                },
                body: body.toString(),
            })
            .then(function (response) {
                return response.json();
            })
            .then(function (payload) {
                if (!payload || !payload.success) {
                    var err = new Error('save_failed');
                    err.payload = payload;
                    throw err;
                }
                closeInlineEditor(gridEl, api);
                var rowData = payload.data && payload.data.row ? payload.data.row : null;
                if (!applyProductRowUpdate(api, productId, rowData)) {
                    if (api && api.refreshInfiniteCache) {
                        api.refreshInfiniteCache();
                    } else if (api && api.purgeInfiniteCache) {
                        api.purgeInfiniteCache();
                    }
                }
                if (typeof window.zertiRefreshDashboard === 'function') {
                    window.zertiRefreshDashboard();
                }
                return payload;
            })
            .catch(function (err) {
                if (saveBtn) {
                    saveBtn.disabled = false;
                }
                if (cancelBtn) {
                    cancelBtn.disabled = false;
                }
                if (input) {
                    input.readOnly = false;
                }
                var msg =
                    err && err.payload && err.payload.data && err.payload.data.message
                        ? String(err.payload.data.message)
                        : '';
                if (msg) {
                    showProductsGridAlert(gridEl, msg);
                }
            })
            .finally(function () {
                hideProductsSaveOverlay(gridEl, api, productId);
            });
    }

    function bindProductsGridOutsideClickDismiss(gridEl, api) {
        if (!gridEl || gridEl.__zertiOutsideClickBound) {
            return;
        }
        gridEl.__zertiOutsideClickBound = true;

        function onDocumentPointerDown(event) {
            var editor = getInlineEditor(gridEl);
            if (!editor) {
                return;
            }
            var savingBtn = gridEl.querySelector(
                '.zerti-products-grid-inline-save[data-zerti-product="' +
                    String(editor.rowId) +
                    '"][data-zerti-field="' +
                    editor.field +
                    '"]:disabled'
            );
            if (savingBtn) {
                return;
            }
            var target = event.target;
            if (!target || !target.closest) {
                return;
            }
            if (target.closest('.zerti-products-grid-inline-editor')) {
                return;
            }
            if (target.closest('.zerti-products-grid-editable-trigger')) {
                return;
            }
            if (target.closest('.zerti-products-grid-alert-overlay')) {
                return;
            }
            closeInlineEditor(gridEl, api);
        }

        document.addEventListener('pointerdown', onDocumentPointerDown, true);
        gridEl.__zertiOutsideClickCleanup = function () {
            document.removeEventListener('pointerdown', onDocumentPointerDown, true);
            gridEl.__zertiOutsideClickBound = false;
            gridEl.__zertiOutsideClickCleanup = null;
        };
    }

    function bindProductsGridInteractions(gridEl, api) {
        if (!gridEl) {
            return;
        }
        if (typeof gridEl.__zertiGridInteractionsCleanup === 'function') {
            gridEl.__zertiGridInteractionsCleanup();
        }

        function onGridClick(event) {
            var target = event.target;
            if (!target || !target.closest) {
                return;
            }
            var cancelBtn = target.closest('.zerti-products-grid-inline-cancel');
            if (cancelBtn) {
                event.preventDefault();
                event.stopPropagation();
                closeInlineEditor(gridEl, api);
                return;
            }
            var saveBtn = target.closest('.zerti-products-grid-inline-save');
            if (saveBtn) {
                if (saveBtn.disabled) {
                    return;
                }
                event.preventDefault();
                event.stopPropagation();
                var productId = Number(saveBtn.getAttribute('data-zerti-product'));
                var field = saveBtn.getAttribute('data-zerti-field') || '';
                var input = gridEl.querySelector(
                    '.zerti-products-grid-inline-input[data-zerti-product="' + String(productId) + '"][data-zerti-field="' + field + '"]'
                );
                commitInlineEditorFromInput(gridEl, api, input);
                return;
            }
            var trigger = target.closest('.zerti-products-grid-editable-trigger');
            if (trigger) {
                event.preventDefault();
                event.stopPropagation();
                var pid = Number(trigger.getAttribute('data-zerti-product'));
                var fld = trigger.getAttribute('data-zerti-field') || '';
                if (!(pid > 0) || (fld !== 'price' && fld !== 'sale_price' && fld !== 'stock')) {
                    return;
                }
                openInlineEditor(gridEl, api, pid, fld);
            }
        }

        function onGridInput(event) {
            var inputEl = event.target && event.target.closest ? event.target.closest('.zerti-products-grid-inline-input') : null;
            if (!inputEl || !gridEl.contains(inputEl)) {
                return;
            }
            normalizeInlineNumericInput(inputEl, inputEl.getAttribute('data-zerti-field') || '');
        }

        function onGridPaste(event) {
            var inputEl = event.target && event.target.closest ? event.target.closest('.zerti-products-grid-inline-input') : null;
            if (!inputEl || !gridEl.contains(inputEl)) {
                return;
            }
            window.setTimeout(function () {
                normalizeInlineNumericInput(inputEl, inputEl.getAttribute('data-zerti-field') || '');
            }, 0);
        }

        function onGridKeydown(event) {
            handleInlineEditorKeydown(event, gridEl, api);
        }

        bindProductsGridOutsideClickDismiss(gridEl, api);
        gridEl.addEventListener('click', onGridClick);
        gridEl.addEventListener('input', onGridInput);
        gridEl.addEventListener('paste', onGridPaste);
        gridEl.addEventListener('keydown', onGridKeydown, true);
        gridEl.__zertiGridClickBound = true;
        gridEl.__zertiGridInteractionsCleanup = function () {
            gridEl.removeEventListener('click', onGridClick);
            gridEl.removeEventListener('input', onGridInput);
            gridEl.removeEventListener('paste', onGridPaste);
            gridEl.removeEventListener('keydown', onGridKeydown, true);
            if (typeof gridEl.__zertiOutsideClickCleanup === 'function') {
                gridEl.__zertiOutsideClickCleanup();
            }
            gridEl.__zertiGridClickBound = false;
            gridEl.__zertiGridInteractionsCleanup = null;
        };
    }

    function productsGridSuppressKeyboardEvent(params) {
        var e = params && params.event;
        if (!e) {
            return false;
        }
        var key = e.key;
        if (key !== 'Enter' && key !== 'Escape' && key !== 'Esc' && key !== 'NumpadEnter') {
            return false;
        }
        var target = e.target;
        if (
            target &&
            typeof target.closest === 'function' &&
            target.closest('.zerti-products-grid-inline-input')
        ) {
            return true;
        }
        return false;
    }

    function numericComparator(key) {
        return function (a, b, nodeA, nodeB) {
            var av = nodeA && nodeA.data ? Number(nodeA.data[key] || 0) : 0;
            var bv = nodeB && nodeB.data ? Number(nodeB.data[key] || 0) : 0;
            return av - bv;
        };
    }

    function textComparator(a, b) {
        return String(a || '').localeCompare(String(b || ''), 'fa');
    }

    function zertiEffectiveProductSearchTerm(input) {
        var value = input && input.value ? zertiPersianToLatinDigits(input.value).trim() : '';
        var length = typeof Array.from === 'function' ? Array.from(value).length : value.length;
        return length >= 3 ? value : '';
    }

    function scrollProductsPageToTop() {
        if (typeof window.scrollTo === 'function') {
            window.scrollTo({ top: 0, behavior: 'smooth' });
        }
    }

    function installSearchToolbar(wrap, cfg, initialSearch) {
        if (!wrap) {
            return null;
        }

        var toolbar = wrap.querySelector('.zerti-products-grid-toolbar');
        if (!toolbar) {
            toolbar = document.createElement('div');
            toolbar.className = 'zerti-products-grid-toolbar';
            toolbar.setAttribute('role', 'toolbar');
            var searchSlot = document.createElement('div');
            searchSlot.className = 'zerti-products-grid-toolbar-search';
            toolbar.appendChild(searchSlot);
            wrap.insertBefore(toolbar, wrap.firstChild);
        }

        var searchSlot = toolbar.querySelector('.zerti-products-grid-toolbar-search');
        if (!searchSlot) {
            searchSlot = document.createElement('div');
            searchSlot.className = 'zerti-products-grid-toolbar-search';
            toolbar.insertBefore(searchSlot, toolbar.firstChild);
        }
        searchSlot.innerHTML = '';

        var label = document.createElement('label');
        label.className = 'zerti-products-grid-search-label form-label mb-0';
        label.setAttribute('for', 'zertiProductsGridSearch');

        var icon = document.createElement('i');
        icon.className = 'fa-solid fa-magnifying-glass zerti-products-grid-search-icon';
        icon.setAttribute('aria-hidden', 'true');

        var input = document.createElement('input');
        input.type = 'search';
        input.id = 'zertiProductsGridSearch';
        input.className = 'form-control form-control-sm zerti-products-grid-search-input';
        input.setAttribute('autocomplete', 'off');
        input.setAttribute('dir', window.zertiAdminTextDirection ? window.zertiAdminTextDirection() : 'rtl');
        input.setAttribute(
            'aria-label',
            cfg.searchAriaLabel || ''
        );
        input.placeholder = cfg.searchPlaceholder || '';
        input.value = initialSearch ? zertiLatinToPersianDigits(initialSearch) : '';

        label.appendChild(icon);
        label.appendChild(input);
        searchSlot.appendChild(label);

        return input;
    }

    function productsSortFromApi(api, fallbackCol, fallbackDir) {
        var orderCol = fallbackCol;
        var orderDir = fallbackDir === 'asc' ? 'asc' : 'desc';
        if (api && api.getColumnState) {
            var sorted = (api.getColumnState() || []).filter(function (col) {
                return col && col.sort;
            })[0];
            if (sorted && sorted.colId) {
                var match = /^c(\d+)$/.exec(String(sorted.colId));
                if (match) {
                    orderCol = parseInt(match[1], 10);
                    orderDir = sorted.sort === 'asc' ? 'asc' : 'desc';
                }
            }
        }
        return { orderCol: orderCol, orderDir: orderDir };
    }

    function productsGridInnerWidth(gridEl) {
        if (!gridEl) {
            return 0;
        }
        var wrap = gridEl.closest('.zerti-products-wrap');
        return Math.max(
            wrap && wrap.clientWidth ? wrap.clientWidth : 0,
            gridEl.clientWidth || 0,
            gridEl.offsetWidth || 0
        );
    }

    function bindProductsGridFillResize(gridEl, api) {
        if (!gridEl || !api || typeof window.ResizeObserver !== 'function') {
            return function () {};
        }
        var wrap = gridEl.closest('.zerti-products-wrap');
        if (!wrap) {
            return function () {};
        }
        var layoutTimer = null;
        var observer = new window.ResizeObserver(function () {
            if (layoutTimer) {
                window.clearTimeout(layoutTimer);
            }
            layoutTimer = window.setTimeout(function () {
                layoutTimer = null;
                stabilizeProductsLayout(api, gridEl);
            }, 80);
        });
        observer.observe(wrap);
        return function () {
            if (layoutTimer) {
                window.clearTimeout(layoutTimer);
                layoutTimer = null;
            }
            observer.disconnect();
        };
    }

    function stabilizeProductsLayout(api, gridEl) {
        if (!api || !gridEl) {
            return;
        }
        if (api.setGridOption) {
            api.setGridOption('getRowHeight', undefined);
            api.setGridOption('suppressHorizontalScroll', isProductsGridCompact(productsGridInnerWidth(gridEl)));
        }
        if (api.resetRowHeights) {
            api.resetRowHeights();
        }
        try {
            if (api.onGridSizeChanged) {
                api.onGridSizeChanged();
            }
        } catch (errSize) {}
        window.requestAnimationFrame(function () {
            try {
                if (api.autoSizeColumns) {
                    api.autoSizeColumns({
                        columns: ['c0', 'c6'],
                        skipHeader: false,
                    });
                }
            } catch (errAuto) {}
            try {
                if (api.sizeColumnsToFit) {
                    api.sizeColumnsToFit();
                }
            } catch (errFit) {}
        });
    }

    function buildColumnDefs(orderCol, orderDir, viewport, api) {
        var i18n = gridI18n();
        var isCompact = viewport && viewport.isCompact;
        var sortPrefs = productsSortFromApi(api, orderCol, orderDir);
        orderCol = sortPrefs.orderCol;
        orderDir = sortPrefs.orderDir;
        var defs = [
            {
                colId: 'c0',
                headerName: i18n.colId || '',
                field: 'id',
                comparator: numericComparator('idRaw'),
                cellClass: 'text-center zerti-products-grid-detail-cell zerti-products-grid-id-cell',
                flex: 0,
                minWidth: 48,
                maxWidth: 120,
                suppressSizeToFit: true,
                resizable: false,
                filter: false,
            },
            {
                colId: 'c1',
                headerName: i18n.colImage || '',
                field: 'image',
                cellRenderer: productImageCellRenderer,
                sortable: false,
                filter: false,
                flex: 0,
                width: 56,
                minWidth: 52,
                maxWidth: 60,
                suppressSizeToFit: true,
                resizable: false,
                cellClass: 'text-center zerti-products-grid-image-cell zerti-products-grid-detail-cell',
            },
            {
                colId: 'c2',
                headerName: i18n.colName || '',
                field: 'name',
                cellRenderer: productNameCellRenderer,
                comparator: textComparator,
                minWidth: isCompact ? 100 : 135,
                flex: isCompact ? 1 : 2.5,
                suppressSizeToFit: false,
                cellClass: 'zerti-products-grid-detail-cell zerti-products-grid-name-cell',
                filter: false,
            },
            {
                colId: 'c7',
                headerName: isCompact ? i18n.colTypeShort || i18n.colType || '' : i18n.colType || '',
                field: 'type',
                comparator: textComparator,
                cellClass: 'text-center zerti-products-grid-type-cell',
                flex: isCompact ? 0.55 : 0,
                width: isCompact ? undefined : 104,
                minWidth: isCompact ? 72 : 96,
                maxWidth: isCompact ? 108 : 128,
                suppressSizeToFit: isCompact ? false : true,
                sortable: false,
                filter: false,
            },
            {
                colId: 'c8',
                headerName: isCompact ? i18n.colStatusShort || i18n.colStatus || '' : i18n.colStatus || '',
                field: 'status',
                comparator: textComparator,
                cellClass: 'text-center zerti-products-grid-status-cell',
                flex: isCompact ? 0.55 : 0,
                width: isCompact ? undefined : 112,
                minWidth: isCompact ? 72 : 100,
                maxWidth: isCompact ? 116 : 140,
                suppressSizeToFit: isCompact ? false : true,
                filter: false,
            },
            {
                colId: 'c3',
                headerName: isCompact ? i18n.colSkuShort || '' : i18n.colSku || '',
                field: 'sku',
                comparator: textComparator,
                cellClass: 'text-center zerti-products-grid-detail-cell',
                flex: isCompact ? 0.65 : 0,
                width: isCompact ? undefined : 118,
                minWidth: isCompact ? 72 : 100,
                maxWidth: isCompact ? 110 : 150,
                suppressSizeToFit: isCompact ? false : true,
                filter: false,
            },
            {
                colId: 'c4',
                headerName: i18n.colPrice || '',
                field: 'price',
                cellRenderer: productsEditableCellRenderer('price'),
                comparator: numericComparator('priceSort'),
                cellClass: 'text-center zerti-products-grid-editable-cell zerti-products-grid-price-cell',
                flex: 0.375,
                minWidth: isCompact ? 108 : 117,
                maxWidth: isCompact ? 129 : 141,
                suppressSizeToFit: false,
                filter: false,
            },
            {
                colId: 'c5',
                headerName: i18n.colSalePrice || '',
                field: 'salePrice',
                cellRenderer: productsEditableCellRenderer('sale_price'),
                comparator: numericComparator('salePriceSort'),
                cellClass: 'text-center zerti-products-grid-editable-cell zerti-products-grid-price-cell',
                flex: 0.375,
                minWidth: isCompact ? 108 : 117,
                maxWidth: isCompact ? 129 : 141,
                suppressSizeToFit: false,
                filter: false,
            },
            {
                colId: 'c6',
                headerName: i18n.colStock || '',
                field: 'stock',
                cellRenderer: productsEditableCellRenderer('stock'),
                comparator: numericComparator('stockSort'),
                cellClass: 'text-center zerti-products-grid-stock-cell zerti-products-grid-editable-cell',
                flex: 0,
                minWidth: 56,
                maxWidth: 96,
                suppressSizeToFit: true,
                resizable: false,
                filter: false,
            },
            {
                colId: 'searchText',
                field: 'searchText',
                filter: false,
                hide: true,
                suppressColumnsToolPanel: true,
            },
        ];
        var sortColId = 'c0';
        if (api) {
            var sortedFromApi = (api.getColumnState ? api.getColumnState() : []).filter(function (col) {
                return col && col.sort;
            })[0];
            if (sortedFromApi && sortedFromApi.colId && /^c[0-9]+$/.test(String(sortedFromApi.colId))) {
                sortColId = String(sortedFromApi.colId);
            }
        } else if (!isNaN(orderCol) && orderCol >= 0) {
            sortColId = 'c' + orderCol;
        }
        defs.forEach(function (def) {
            if (def.colId === sortColId && def.sortable !== false) {
                def.sort = orderDir === 'asc' ? 'asc' : 'desc';
            }
        });
        return defs;
    }

    function createProductsLoadingState(gridEl) {
        var activeRequests = 0;
        var el = document.createElement('div');
        el.className = 'zerti-products-grid-loading';
        el.setAttribute('role', 'status');
        el.setAttribute('aria-live', 'polite');
        el.hidden = true;
        el.textContent = gridI18n().loadingData || '';
        gridEl.appendChild(el);

        return {
            show: function () {
                activeRequests += 1;
                el.hidden = false;
            },
            hide: function () {
                activeRequests = Math.max(0, activeRequests - 1);
                if (activeRequests === 0) {
                    el.hidden = true;
                }
            },
            destroy: function () {
                activeRequests = 0;
                if (el.parentNode) {
                    el.parentNode.removeChild(el);
                }
            },
        };
    }

    function refreshProductsInfiniteCache(api, resetPage, dataSourceFactory) {
        if (resetPage && api && api.paginationGoToFirstPage) {
            api.paginationGoToFirstPage();
        }
        if (dataSourceFactory && api) {
            if (api.setGridOption) {
                api.setGridOption('datasource', dataSourceFactory());
                return;
            }
            if (api.setDatasource) {
                api.setDatasource(dataSourceFactory());
                return;
            }
        }
        if (api && api.purgeInfiniteCache) {
            api.purgeInfiniteCache();
        } else if (api && api.refreshInfiniteCache) {
            api.refreshInfiniteCache();
        }
    }

    function zertiGridFetchRows(cfg, query, ajaxBody) {
        if (cfg.restUrl) {
            var restQuery = new URLSearchParams(query);
            var restUrl = cfg.restUrl + (restQuery.toString() ? '?' + restQuery.toString() : '');
            return window
                .fetch(restUrl, {
                    method: 'GET',
                    credentials: 'same-origin',
                    headers: {
                        'X-WP-Nonce': cfg.restNonce || '',
                    },
                })
                .then(function (response) {
                    return response.json();
                })
                .then(function (payload) {
                    if (!payload || !Array.isArray(payload.rows)) {
                        throw new Error('invalid_grid_response');
                    }
                    return {
                        rows: payload.rows,
                        total: payload.total,
                    };
                });
        }

        var body = new URLSearchParams(ajaxBody);
        return window
            .fetch(cfg.ajaxUrl || window.ajaxurl || '', {
                method: 'POST',
                credentials: 'same-origin',
                headers: {
                    'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8',
                },
                body: body.toString(),
            })
            .then(function (response) {
                return response.json();
            })
            .then(function (payload) {
                if (!payload || !payload.success || !payload.data) {
                    throw new Error('invalid_grid_response');
                }
                return {
                    rows: Array.isArray(payload.data.rows) ? payload.data.rows : [],
                    total: payload.data.total,
                };
            });
    }

    function createProductsDataSource(cfg, searchInput, loadingState, api, gridEl) {
        return {
            getRows: function (params) {
                var pageSize = Number(cfg.pageSize || 50);
                var sortModel = params.sortModel || [];
                if (loadingState && loadingState.show) {
                    loadingState.show();
                }
                var stockFilter = readProductsStockFilter(cfg);
                var gridQuery = {
                    start: String(params.startRow || 0),
                    limit: String(pageSize),
                    search: zertiEffectiveProductSearchTerm(searchInput),
                    sort_model: JSON.stringify(sortModel),
                    stock_filter: stockFilter,
                };
                var ajaxBody = Object.assign({}, gridQuery, {
                    action: cfg.action || 'zerti_products_grid',
                    nonce: cfg.nonce || '',
                });

                zertiGridFetchRows(cfg, gridQuery, ajaxBody)
                    .then(function (data) {
                        var rows = data.rows;
                        var startRow = Number(params.startRow || 0);
                        var totalRaw = Number(data.total);
                        var serverTotalKnown = Number.isFinite(totalRaw) && totalRaw >= 0;
                        var isLastBlock = rows.length < pageSize;
                        var rowCount;
                        if (serverTotalKnown) {
                            gridEl.__zertiProductsTotal = totalRaw;
                        } else if (isLastBlock) {
                            gridEl.__zertiProductsTotal = startRow + rows.length;
                        }
                        if (isLastBlock) {
                            rowCount = startRow + rows.length;
                        } else if (serverTotalKnown) {
                            rowCount = totalRaw;
                        } else {
                            rowCount = -1;
                        }
                        if (params.success) {
                            params.success({
                                rowData: rows,
                                rowCount: rowCount >= 0 ? rowCount : undefined,
                            });
                        } else {
                            params.successCallback(rows, rowCount);
                        }
                        if (loadingState && loadingState.hide) {
                            loadingState.hide();
                        }
                        window.requestAnimationFrame(function () {
                            stabilizeProductsLayout(api, gridEl);
                        });
                        scrollProductsPageToTop();
                    })
                    .catch(function () {
                        if (params.fail) {
                            params.fail();
                        } else if (params.failCallback) {
                            params.failCallback();
                        }
                        if (loadingState && loadingState.hide) {
                            loadingState.hide();
                        }
                    });
            },
        };
    }

    function isProductsGridCompact(width) {
        return width < 992;
    }

    function applyProductsResponsiveColumns(api, gridEl) {
        if (!api || !gridEl) {
            return;
        }
        var width = productsGridInnerWidth(gridEl);
        var isCompact = isProductsGridCompact(width);
        var viewport = { isCompact: isCompact };
        var urlP = readUrlParams(readGridCfg());
        var nextMode = isCompact ? 'compact' : 'desktop';

        if (gridEl.__zertiProductsViewportMode !== nextMode) {
            gridEl.__zertiProductsViewportMode = nextMode;
            var columnDefs = buildColumnDefs(urlP.orderCol, urlP.orderDir, viewport, api);
            if (api.setGridOption) {
                api.setGridOption('columnDefs', columnDefs);
            } else if (api.setColumnDefs) {
                api.setColumnDefs(columnDefs);
            }
        }

        var visibility = {
            c0: !isCompact,
            c1: true,
            c2: true,
            c7: !isCompact,
            c8: !isCompact,
            c3: true,
            c4: !isCompact,
            c5: !isCompact,
            c6: !isCompact,
            searchText: false,
        };
        var state = Object.keys(visibility).map(function (colId) {
            return {
                colId: colId,
                hide: !visibility[colId],
            };
        });

        if (api.applyColumnState) {
            api.applyColumnState({ state: state, applyOrder: false });
        } else if (api.setColumnsVisible) {
            Object.keys(visibility).forEach(function (colId) {
                api.setColumnsVisible([colId], visibility[colId]);
            });
        }
        gridEl.classList.toggle('zerti-products-grid--compact', isCompact);
        stabilizeProductsLayout(api, gridEl);
    }

    function submitProductsExport(cfg) {
        var ajaxUrl = cfg.ajaxUrl || window.ajaxurl || '';
        if (!ajaxUrl) {
            return;
        }

        var form = document.createElement('form');
        form.method = 'POST';
        form.action = ajaxUrl;
        form.target = '_self';
        form.style.display = 'none';

        var fields = {
            action: cfg.exportAction || 'zerti_products_export',
            nonce: cfg.exportNonce || '',
        };

        Object.keys(fields).forEach(function (name) {
            var input = document.createElement('input');
            input.type = 'hidden';
            input.name = name;
            input.value = fields[name];
            form.appendChild(input);
        });

        document.body.appendChild(form);
        form.submit();
        window.setTimeout(function () {
            if (form.parentNode) {
                form.parentNode.removeChild(form);
            }
        }, 0);
    }

    function fetchProductsTotal(cfg, callback) {
        var ajaxUrl = cfg.ajaxUrl || window.ajaxurl || '';
        if (!ajaxUrl || typeof callback !== 'function') {
            callback(-1);
            return;
        }
        var stockFilter = readProductsStockFilter(cfg);
        var body = new URLSearchParams({
            action: cfg.action || 'zerti_products_grid',
            nonce: cfg.nonce || '',
            start: '0',
            limit: '1',
            search: '',
            sort_model: '[]',
            stock_filter: stockFilter,
        });
        fetch(ajaxUrl, {
            method: 'POST',
            credentials: 'same-origin',
            headers: { 'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8' },
            body: body.toString(),
        })
            .then(function (res) {
                return res.json();
            })
            .then(function (json) {
                if (!json || !json.success || !json.data) {
                    callback(-1);
                    return;
                }
                var total = Number(json.data.total);
                callback(Number.isFinite(total) && total >= 0 ? total : -1);
            })
            .catch(function () {
                callback(-1);
            });
    }

    function resetProductsExportButton(btn, defaultLabelEl, defaultLabel) {
        btn.disabled = false;
        btn.removeAttribute('aria-busy');
        if (defaultLabelEl) {
            defaultLabelEl.textContent = defaultLabel;
        }
    }

    function runProductsExport(cfg, gridEl, searchInput, btn, defaultLabelEl, defaultLabel) {
        var i18n = (cfg && cfg.i18n) || {};
        var search = zertiEffectiveProductSearchTerm(searchInput);

        function startDownload() {
            if (defaultLabelEl && i18n.exportWorking) {
                defaultLabelEl.textContent = i18n.exportWorking;
            }
            try {
                submitProductsExport(cfg);
            } catch (e) {}
            window.setTimeout(function () {
                resetProductsExportButton(btn, defaultLabelEl, defaultLabel);
            }, 1500);
        }

        function onTotalKnown(total) {
            if (total === 0) {
                showProductsGridAlert(
                    gridEl,
                    i18n.exportEmpty || ''
                );
                resetProductsExportButton(btn, defaultLabelEl, defaultLabel);
                return;
            }
            startDownload();
        }

        if (search !== '') {
            startDownload();
            return;
        }

        if (gridEl && typeof gridEl.__zertiProductsTotal === 'number') {
            onTotalKnown(gridEl.__zertiProductsTotal);
            return;
        }

        fetchProductsTotal(cfg, onTotalKnown);
    }

    function installProductsStockFilterBar(wrap, cfg, gridEl, api, dataSourceFactory) {
        if (!wrap) {
            return null;
        }
        var host = wrap.querySelector('.zerti-products-grid-toolbar');
        if (!host) {
            return null;
        }
        var existing = host.querySelector('.zerti-products-stock-filter-bar');
        if (existing) {
            existing.remove();
        }
        var stockFilter = readProductsStockFilter(cfg);
        if (!stockFilter || !isValidProductsStockFilter(stockFilter)) {
            return null;
        }
        var i18n = (cfg && cfg.i18n) || {};
        var bar = document.createElement('div');
        bar.className =
            'zerti-products-stock-filter-bar alert alert-secondary py-2 px-3 mb-0 small d-flex flex-wrap align-items-center justify-content-center gap-2';
        bar.setAttribute('role', 'status');
        var label = document.createElement('span');
        label.className = 'zerti-grid-list-filter-label';
        label.textContent = productsStockFilterLabel(cfg, stockFilter);
        var clearBtn = document.createElement('button');
        clearBtn.type = 'button';
        clearBtn.className =
            'btn btn-outline-secondary d-inline-flex align-items-center zerti-grid-list-filter-clear-btn';
        clearBtn.textContent = i18n.stockFilterClear || '';
        clearBtn.addEventListener('click', function () {
            try {
                var u = new URL(window.location.href);
                u.searchParams.delete(cfg.paramProductsStock || 'zerti_products_stock');
                var next = u.pathname + u.search + window.location.hash;
                window.history.replaceState(null, '', next);
            } catch (eClear) {}
            bar.remove();
            gridEl.__zertiProductsTotal = undefined;
            refreshProductsInfiniteCache(api, true, dataSourceFactory);
        });
        bar.appendChild(clearBtn);
        bar.appendChild(label);
        host.insertBefore(bar, host.firstChild);
        return function () {
            if (bar.parentNode) {
                bar.parentNode.removeChild(bar);
            }
        };
    }

    function installProductsExportButton(wrap, cfg, gridEl, searchInput) {
        if (!wrap) {
            return null;
        }
        var btn = wrap.querySelector('.zerti-products-export-btn');
        if (!btn) {
            return null;
        }
        var defaultLabelEl = btn.querySelector('.zerti-products-export-label');
        var defaultLabel = defaultLabelEl ? defaultLabelEl.textContent : '';

        var onClick = function (event) {
            event.preventDefault();
            if (btn.disabled) {
                return;
            }
            btn.disabled = true;
            btn.setAttribute('aria-busy', 'true');
            runProductsExport(cfg, gridEl, searchInput, btn, defaultLabelEl, defaultLabel);
        };

        btn.addEventListener('click', onClick);
        return function () {
            btn.removeEventListener('click', onClick);
        };
    }

    function zertiInitProductsTable(root) {
        if (!root || !window.agGrid || typeof window.agGrid.createGrid !== 'function') {
            return;
        }
        var gridEl = root.querySelector('#zertiProductsGrid');
        if (!gridEl) {
            return;
        }
        if (typeof gridEl.__zertiAgGridCleanup === 'function') {
            gridEl.__zertiAgGridCleanup();
            gridEl.__zertiAgGridCleanup = null;
        }
        if (gridEl.__zertiAgGridApi && typeof gridEl.__zertiAgGridApi.destroy === 'function') {
            gridEl.__zertiAgGridApi.destroy();
            gridEl.__zertiAgGridApi = null;
        }

        var wrap = gridEl.closest('.zerti-products-wrap');
        var cfg = readGridCfg();
        var urlP = readUrlParams(cfg);
        var initialSearch = zertiPersianToLatinDigits(urlP.search).trim();
        var searchInput = installSearchToolbar(wrap, cfg, initialSearch);
        var exportCleanup = installProductsExportButton(wrap, cfg, gridEl, searchInput);
        var pageSize = Number(cfg.pageSize || 50);
        var stockFilterCleanup = null;
        var gridWidth = productsGridInnerWidth(gridEl);
        var initialViewport = {
            isCompact: isProductsGridCompact(gridWidth),
        };
        gridEl.__zertiProductsViewportMode = initialViewport.isCompact ? 'compact' : 'desktop';

        var gridOptions = {
            theme: 'legacy',
            rowModelType: 'infinite',
            columnDefs: buildColumnDefs(urlP.orderCol, urlP.orderDir, initialViewport, null),
            defaultColDef: {
                flex: 1,
                sortable: true,
                resizable: true,
                filter: false,
                floatingFilter: false,
                cellClass: 'zerti-products-grid-cell',
            },
            enableRtl: window.zertiIsAdminRtl ? window.zertiIsAdminRtl() : true,
            animateRows: true,
            rowHeight: 48,
            pagination: true,
            paginationPageSize: pageSize,
            paginationPageSizeSelector: false,
            paginationPanels: ['pageSummary', 'rowSummary'],
            cacheBlockSize: pageSize,
            maxBlocksInCache: 4,
            paginationNumberFormatter: function (params) {
                return zertiLatinToPersianDigits(params.value);
            },
            localeText: window.zertiAgGridLocaleText(gridI18n(), 'noRowsToShow'),
            getRowId: function (params) {
                return String(params.data.idRaw);
            },
            suppressRowClickSelection: true,
            suppressKeyboardEvent: productsGridSuppressKeyboardEvent,
            onCellClicked: handleProductsDetailCellClicked,
            onSortChanged: function (event) {
                gridEl.__zertiProductsTotal = undefined;
                replaceProductsGridUrlParams(event.api, cfg, searchInput);
                refreshProductsInfiniteCache(event.api, true, gridEl.__zertiAgGridDataSourceFactory);
                window.requestAnimationFrame(function () {
                    stabilizeProductsLayout(event.api, gridEl);
                });
            },
        };

        var api = window.agGrid.createGrid(gridEl, gridOptions);
        var loadingState = createProductsLoadingState(gridEl);
        var dataSourceFactory = function () {
            return createProductsDataSource(cfg, searchInput, loadingState, api, gridEl);
        };
        gridEl.__zertiAgGridDataSourceFactory = dataSourceFactory;
        var dataSource = dataSourceFactory();
        if (api.setGridOption) {
            api.setGridOption('datasource', dataSource);
        } else if (api.setDatasource) {
            api.setDatasource(dataSource);
        }
        gridEl.__zertiAgGridApi = api;
        stockFilterCleanup = installProductsStockFilterBar(wrap, cfg, gridEl, api, dataSourceFactory);
        bindProductsGridInteractions(gridEl, api);
        applyProductsResponsiveColumns(api, gridEl);
        window.requestAnimationFrame(function () {
            stabilizeProductsLayout(api, gridEl);
        });
        var disconnectFillResize = bindProductsGridFillResize(gridEl, api);

        var resizeTimer = null;
        var onResize = function () {
            if (resizeTimer) {
                window.clearTimeout(resizeTimer);
            }
            resizeTimer = window.setTimeout(function () {
                resizeTimer = null;
                applyProductsResponsiveColumns(api, gridEl);
            }, 120);
        };
        window.addEventListener('resize', onResize);
        gridEl.__zertiAgGridCleanup = function () {
            if (resizeTimer) {
                window.clearTimeout(resizeTimer);
                resizeTimer = null;
            }
            if (typeof exportCleanup === 'function') {
                exportCleanup();
            }
            if (typeof stockFilterCleanup === 'function') {
                stockFilterCleanup();
            }
            if (typeof disconnectFillResize === 'function') {
                disconnectFillResize();
            }
            if (typeof gridEl.__zertiGridInteractionsCleanup === 'function') {
                gridEl.__zertiGridInteractionsCleanup();
            }
            window.removeEventListener('resize', onResize);
            closeInlineEditor(gridEl, api);
            gridEl.__zertiAgGridDataSourceFactory = null;
            gridEl.__zertiProductsViewportMode = null;
            gridEl.__zertiProductsTotal = undefined;
            loadingState.destroy();
        };

        if (searchInput) {
            var lastEffectiveSearch = zertiEffectiveProductSearchTerm(searchInput);
            searchInput.oninput = function () {
                var displayValue = zertiLatinToPersianDigits(this.value);
                if (this.value !== displayValue) {
                    this.value = displayValue;
                }
                var nextEffectiveSearch = zertiEffectiveProductSearchTerm(searchInput);
                if (nextEffectiveSearch !== lastEffectiveSearch) {
                    lastEffectiveSearch = nextEffectiveSearch;
                    refreshProductsInfiniteCache(api, true, dataSourceFactory);
                }
                replaceProductsGridUrlParams(api, cfg, searchInput);
            };
        }
    }

    window.zertiInitProductsTable = zertiInitProductsTable;
})(window, document);
