/**
 * TitanCart — Shop page AJAX filtering engine.
 *
 * Offcanvas filter panel with dynamic product grid updates.
 * Depends on titancartConfig (localized from PHP).
 *
 * @package TitanCart
 */
(function () {
    'use strict';

    var app      = document.getElementById('tc-shop-app');
    if (!app) return;

    var API      = app.dataset.api;        // e.g. /wp-json/titancart/v1/store
    var CAT_ID   = parseInt(app.dataset.category) || 0;
    var TAG_SLUG = app.dataset.tagSlug || '';
    var SYMBOL   = app.dataset.symbol || '$';
    var QV_ON    = app.dataset.enableQv !== '0';

    // DOM refs
    var grid         = document.getElementById('tc-product-grid');
    var countEl      = document.getElementById('tc-product-count');
    var pagination   = document.getElementById('tc-pagination');
    var loading      = document.getElementById('tc-loading');
    var filterPanel  = document.getElementById('tc-filter-panel');
    var filterOverlay= document.getElementById('tc-filter-overlay');
    var filterBody   = document.getElementById('tc-filter-body');
    var filterToggle = document.getElementById('tc-filter-toggle');
    var filterClose  = document.getElementById('tc-filter-close');
    var filterClear  = document.getElementById('tc-filter-clear');
    var filterApply  = document.getElementById('tc-filter-apply');
    var activeChips  = document.getElementById('tc-active-filters');
    var sortSelect   = document.getElementById('tc-sort');

    // State
    var defaultSort   = app.dataset.defaultSort || 'date-desc';
    var dsParts       = defaultSort.split('-');
    var filters       = {};   // { price_min, price_max, category: '5', availability: ['in_stock','on_sale'], attr_1: ['2','5'] }
    var filterDefs    = [];   // from /store/filters
    var currentPage   = 1;
    var currentSort   = dsParts[0] || 'date';
    var currentOrder  = dsParts[1] || 'desc';
    var isLoading     = false;
    var FILTER_MODE   = new URLSearchParams(window.location.search).get('filter') || app.dataset.filter || '';
    var SHOW_STOCK    = window.titancartConfig && window.titancartConfig.showStockCount;
    var SHOW_FILTER_COUNTS = window.titancartConfig && window.titancartConfig.showFilterCounts;
    var GUEST_POLICY  = (!window.titancartConfig || !window.titancartConfig.isLoggedIn) ? (window.titancartConfig && window.titancartConfig.allowShoppingGuest || 'allow') : 'allow';
    var HIDE_PRICE    = GUEST_POLICY === 'hide_price';
    var DENY_CART     = GUEST_POLICY === 'hide_price' || GUEST_POLICY === 'deny';
    var SHOW_TAX_CAT  = window.titancartConfig && window.titancartConfig.showTaxOnCatalog;
    var ACCT_URL      = (window.titancartConfig && window.titancartConfig.accountUrl) || '/my-account/';
    var PAG_TYPE      = (window.titancartConfig && window.titancartConfig.paginationType) || 'numbers'; // numbers | infinite_scroll | load_more
    var totalPages    = parseInt(app.dataset.totalPages) || 1;
    var isAppending   = false;  // true when loading next page in infinite/load_more mode

    // Catalog Mode flags
    var CATALOG_MODE  = window.titancartConfig && window.titancartConfig.extensions && window.titancartConfig.extensions.catalogMode;
    var CM_TYPE       = (CATALOG_MODE && window.titancartConfig.catalogMode) ? window.titancartConfig.catalogMode.type : 'catalog';
    var CM_HIDE_PRICE = CATALOG_MODE && window.titancartConfig.catalogMode && window.titancartConfig.catalogMode.hidePrices;
    var CM_INQUIRY_URL  = (CATALOG_MODE && window.titancartConfig.catalogMode) ? window.titancartConfig.catalogMode.inquiryUrl : '/contact/';
    var CM_INQUIRY_TEXT = (CATALOG_MODE && window.titancartConfig.catalogMode) ? window.titancartConfig.catalogMode.inquiryText : 'Request a Quote';
    if (CATALOG_MODE) {
        if (CM_HIDE_PRICE) HIDE_PRICE = true;
        DENY_CART = true;
    }

    // Price Per Unit flags
    var PPU_ON        = window.titancartConfig && window.titancartConfig.extensions && window.titancartConfig.extensions.pricePerUnit;
    var PPU_CATALOG   = PPU_ON && window.titancartConfig.pricePerUnit && window.titancartConfig.pricePerUnit.showOnCatalog;
    var PPU_UNIT      = (PPU_ON && window.titancartConfig.pricePerUnit) ? window.titancartConfig.pricePerUnit.defaultUnit : 'kg';
    var PPU_UNITS     = (PPU_ON && window.titancartConfig.pricePerUnit) ? parseFloat(window.titancartConfig.pricePerUnit.defaultUnits) || 1 : 1;
    var PPU_PER_X     = (PPU_ON && window.titancartConfig.pricePerUnit) ? parseFloat(window.titancartConfig.pricePerUnit.defaultPerX) || 1 : 1;

    /* ────────────────────────────────────────────────────────────────────────
     *  View mode switching (grid / list / compact)
     * ──────────────────────────────────────────────────────────────────────── */
    var viewToggle    = document.getElementById('tc-view-toggle');
    var _viewPerCat   = (typeof titancartConfig !== 'undefined' && titancartConfig.viewPerCategory);
    var _viewKey      = _viewPerCat && app.dataset.category
                        ? 'tc_view_mode_' + app.dataset.category
                        : 'tc_view_mode';
    var currentView   = localStorage.getItem(_viewKey) || app.dataset.defaultView || 'grid';
    var enabledViews  = (app.dataset.views || 'grid').split(',');
    var ALLOW_COMPARE = app.dataset.allowCompare === '1';
    var WISHLIST_ON   = app.dataset.wishlistOn !== '0';

    // Validate stored view is still enabled.
    if (enabledViews.indexOf(currentView) === -1) currentView = enabledViews[0] || 'grid';

    function applyViewMode(mode) {
        currentView = mode;
        localStorage.setItem(_viewKey, mode);
        // Toggle grid CSS classes
        grid.classList.remove('tc-product-grid--list', 'tc-product-grid--compact');
        if (mode === 'list')    grid.classList.add('tc-product-grid--list');
        if (mode === 'compact') grid.classList.add('tc-product-grid--compact');
        // Toggle active button
        if (viewToggle) {
            viewToggle.querySelectorAll('.tc-shop__view-btn').forEach(function(btn) {
                btn.classList.toggle('tc-shop__view-btn--active', btn.dataset.view === mode);
            });
        }
    }

    // Init on page load.
    applyViewMode(currentView);
// Initialize infinite scroll / load-more for server-rendered first page.
    if (totalPages > 1) {
        if (PAG_TYPE === 'infinite_scroll') {
            initInfiniteScroll(1, totalPages);
        } else if (PAG_TYPE === 'load_more') {
            var lmBtn = document.getElementById('tc-load-more');
            if (lmBtn) {
                lmBtn.addEventListener('click', function () {
                    currentPage = 2;
                    isAppending = true;
                    loadProducts(true);
                });
            }
        }
    }    // Click handlers on toggle buttons.
    if (viewToggle) {
        viewToggle.addEventListener('click', function(e) {
            var btn = e.target.closest('.tc-shop__view-btn');
            if (!btn || btn.dataset.view === currentView) return;
            applyViewMode(btn.dataset.view);
        });
    }

    /* ────────────────────────────────────────────────────────────────────────
     *  Offcanvas panel
     * ──────────────────────────────────────────────────────────────────────── */

    function openPanel()  {
        filterPanel.classList.add('tc-offcanvas--open');
        filterOverlay.hidden = false;
        document.body.style.overflow = 'hidden';
    }
    function closePanel() {
        filterPanel.classList.remove('tc-offcanvas--open');
        filterOverlay.hidden = true;
        document.body.style.overflow = '';
    }

    filterToggle.addEventListener('click', openPanel);
    filterClose.addEventListener('click', closePanel);
    filterOverlay.addEventListener('click', closePanel);

    filterApply.addEventListener('click', function () {
        closePanel();
        currentPage = 1;
        loadProducts();
    });

    filterClear.addEventListener('click', function () {
        filters = {};
        renderFilterForm();
        renderActiveChips();
        closePanel();
        currentPage = 1;
        loadProducts();
    });

    /* ────────────────────────────────────────────────────────────────────────
     *  Sort
     * ──────────────────────────────────────────────────────────────────────── */

    sortSelect.addEventListener('change', function () {
        var parts = sortSelect.value.split('-');
        currentSort  = parts[0];
        currentOrder = parts[1] || 'desc';
        currentPage  = 1;
        loadProducts();
    });

    /* ────────────────────────────────────────────────────────────────────────
     *  Load filter definitions
     * ──────────────────────────────────────────────────────────────────────── */

    function loadFilters() {
        var url = API + '/filters';
        if (CAT_ID) url += '?category=' + CAT_ID;
        if (TAG_SLUG) url += (url.indexOf('?') === -1 ? '?' : '&') + 'tag=' + encodeURIComponent(TAG_SLUG);
        // V1 NOTE: /filters does not yet tag-scope facet counts — facets reflect
        // the global catalog when on a tag archive. The product list itself is
        // still tag-scoped correctly via /products?tag=. Acceptable for V1; revisit
        // if facet drift becomes a UX issue.
        fetch(url).then(function (r) { return r.json(); }).then(function (data) {
            filterDefs = data;
            renderFilterForm();
        });
    }

    function renderFilterForm() {
        var html = '';

        filterDefs.forEach(function (def) {
            var expanded = def.start_expanded ? true : false;
            var openClass = expanded ? ' tc-filter-card--open' : '';
            html += '<div class="tc-filter-card' + openClass + '">';

            // ── Category list with collapsible subcategories ──
            if (def.type === 'category_list') {
                html += '<h5 class="tc-filter-card__title" data-collapse-toggle>' + esc(def.name) + '<span class="tc-filter-card__chevron"></span></h5>';
                html += '<div class="tc-filter-card__body"' + (expanded ? '' : ' style="display:none"') + '>';
                html += '<div class="tc-filter-catlist">';

                var activeCat = filters.category ? String(filters.category) : '';

                function hasActiveCatChild(kids) {
                    if (!kids) return false;
                    for (var i = 0; i < kids.length; i++) {
                        if (String(kids[i].value) === activeCat) return true;
                        if (hasActiveCatChild(kids[i].children)) return true;
                    }
                    return false;
                }

                function renderCatItem(opt, depth) {
                    var isActive = activeCat === String(opt.value);
                    var hasKids  = opt.children && opt.children.length > 0;
                    var childActive = hasKids && hasActiveCatChild(opt.children);
                    var showKids = childActive || isActive;
                    var pad = depth * 16;
                    var r = '';

                    r += '<div class="tc-filter-catlist__group">';
                    r += '<div class="tc-filter-catlist__row"' + (pad ? ' style="padding-left:' + pad + 'px"' : '') + '>';

                    if (hasKids) {
                        r += '<span class="tc-filter-catlist__toggle' + (showKids ? ' tc-filter-catlist__toggle--open' : '') + '" data-cat-toggle="' + opt.value + '"></span>';
                    } else if (depth === 0) {
                        r += '<span class="tc-filter-catlist__toggle-spacer"></span>';
                    }

                    r += '<a href="javascript:;" class="tc-filter-catlist__item' + (isActive ? ' tc-filter-catlist__item--active' : '') + '"';
                    r += ' data-filter="category" data-value="' + esc(opt.value) + '">';
                    r += '<span class="tc-filter-catlist__name">' + esc(opt.label) + '</span>';
                    r += '<span class="tc-filter-catlist__count">' + opt.count + '</span>';
                    r += '</a>';
                    r += '</div>';

                    if (hasKids) {
                        r += '<div class="tc-filter-catlist__children" data-cat-children="' + opt.value + '"' + (showKids ? '' : ' style="display:none"') + '>';
                        opt.children.forEach(function (child) { r += renderCatItem(child, depth + 1); });
                        r += '</div>';
                    }

                    r += '</div>';
                    return r;
                }

                (def.options || []).forEach(function (opt) {
                    html += renderCatItem(opt, 0);
                });

                html += '</div>';
                html += '</div>';
                html += '</div>';
                return;
            }

            html += '<h5 class="tc-filter-card__title" data-collapse-toggle>' + esc(def.name) + '<span class="tc-filter-card__chevron"></span></h5>';
            html += '<div class="tc-filter-card__body"' + (expanded ? '' : ' style="display:none"') + '>';

            // ── Range: min/max number inputs (price) ──
            if (def.type === 'range') {
                var curMin = filters.price_min != null ? filters.price_min : def.min;
                var curMax = filters.price_max != null ? filters.price_max : def.max;
                html += '<div class="tc-filter-range">';
                html += '<input type="number" class="tc-input tc-input--sm" data-filter="price_min" value="' + curMin + '" min="' + def.min + '" max="' + def.max + '" step="1" placeholder="Min">';
                html += '<span class="tc-filter-range__sep">\u2013</span>';
                html += '<input type="number" class="tc-input tc-input--sm" data-filter="price_max" value="' + curMax + '" min="' + def.min + '" max="' + def.max + '" step="1" placeholder="Max">';
                html += '</div>';

            // ── Slider: dual range slider (price) ──
            } else if (def.type === 'slider') {
                var sMin = filters.price_min != null ? filters.price_min : def.min;
                var sMax = filters.price_max != null ? filters.price_max : def.max;
                html += '<div class="tc-filter-slider" data-slider-key="' + esc(def.key) + '">';
                html += '<div class="tc-filter-slider__labels">';
                html += '<span class="tc-filter-slider__val" id="tc-slider-min-val">' + SYMBOL + sMin + '</span>';
                html += '<span class="tc-filter-slider__val" id="tc-slider-max-val">' + SYMBOL + sMax + '</span>';
                html += '</div>';
                html += '<div class="tc-filter-slider__track">';
                html += '<input type="range" class="tc-filter-slider__input" data-filter="price_min" min="' + def.min + '" max="' + def.max + '" value="' + sMin + '" step="1">';
                html += '<input type="range" class="tc-filter-slider__input" data-filter="price_max" min="' + def.min + '" max="' + def.max + '" value="' + sMax + '" step="1">';
                html += '</div>';
                html += '</div>';

            // ── Color checkboxes (ShopNest style — colored circle + name) ──
            } else if (def.type === 'color') {
                var active = filters[def.key] || [];
                html += '<div class="tc-filter-colors">';
                (def.options || []).forEach(function (opt) {
                    var checked = active.indexOf(String(opt.value)) > -1 ? ' checked' : '';
                    var hex = opt.color_hex || colorNameToHex(opt.label);
                    html += '<label class="tc-filter-color-check">';
                    html += '<input type="checkbox" class="tc-filter-color-check__input" data-filter="' + esc(def.key) + '" value="' + esc(opt.value) + '"' + checked;
                    html += ' style="--swatch-color:' + hex + '">';
                    html += '<span class="tc-filter-color-check__label">' + esc(opt.label) + '</span>';
                    if (SHOW_FILTER_COUNTS && opt.count != null) html += '<span class="tc-filter-color-check__count">(' + opt.count + ')</span>';
                    html += '</label>';
                });
                html += '</div>';

            // ── Size pills (ShopNest style — outline dark pills) ──
            } else if (def.type === 'size_pills') {
                var active = filters[def.key] || [];
                html += '<div class="tc-filter-sizes">';
                (def.options || []).forEach(function (opt) {
                    var isActive = active.indexOf(String(opt.value)) > -1;
                    html += '<button type="button" class="tc-filter-size-pill' + (isActive ? ' tc-filter-size-pill--active' : '') + '"';
                    html += ' data-filter="' + esc(def.key) + '" data-value="' + esc(opt.value) + '">';
                    html += esc(opt.label) + ((SHOW_FILTER_COUNTS && opt.count != null) ? ' (' + opt.count + ')' : '');
                    html += '</button>';
                });
                html += '</div>';

            // ── Checkbox (default — for availability, brands, etc.) ──
            } else {
                var active = filters[def.key] || [];
                (def.options || []).forEach(function (opt) {
                    var checked = active.indexOf(String(opt.value)) > -1 ? ' checked' : '';
                    html += '<label class="tc-filter-check">';
                    html += '<input type="checkbox" data-filter="' + esc(def.key) + '" value="' + esc(opt.value) + '"' + checked + '>';
                    html += '<span class="tc-filter-check__label">' + esc(opt.label) + '</span>';
                    if (SHOW_FILTER_COUNTS && opt.count != null) html += '<span class="tc-filter-check__count">(' + opt.count + ')</span>';
                    html += '</label>';
                });
            }

            html += '</div>'; // close tc-filter-card__body
            html += '</div>'; // close tc-filter-card
        });

        filterBody.innerHTML = html;
        wireUpFilterEvents();
    }

    /* Wire up all filter interaction events after rendering */
    function wireUpFilterEvents() {
        // Collapse toggles
        filterBody.querySelectorAll('[data-collapse-toggle]').forEach(function (title) {
            title.addEventListener('click', function () {
                var card = title.closest('.tc-filter-card');
                var body = card ? card.querySelector('.tc-filter-card__body') : null;
                if (!card || !body) return;
                var isOpen = card.classList.contains('tc-filter-card--open');
                if (isOpen) {
                    card.classList.remove('tc-filter-card--open');
                    body.style.display = 'none';
                } else {
                    card.classList.add('tc-filter-card--open');
                    body.style.display = '';
                }
            });
        });

        // Subcategory collapse toggles
        filterBody.querySelectorAll('[data-cat-toggle]').forEach(function (toggle) {
            toggle.addEventListener('click', function (e) {
                e.stopPropagation();
                var catId = toggle.getAttribute('data-cat-toggle');
                var children = filterBody.querySelector('[data-cat-children="' + catId + '"]');
                if (!children) return;
                var isOpen = toggle.classList.contains('tc-filter-catlist__toggle--open');
                if (isOpen) {
                    toggle.classList.remove('tc-filter-catlist__toggle--open');
                    children.style.display = 'none';
                } else {
                    toggle.classList.add('tc-filter-catlist__toggle--open');
                    children.style.display = '';
                }
            });
        });

        // Price number inputs
        filterBody.querySelectorAll('[data-filter="price_min"],[data-filter="price_max"]').forEach(function (inp) {
            if (inp.type === 'number') {
                inp.addEventListener('change', function () {
                    filters[inp.dataset.filter] = inp.value ? parseFloat(inp.value) : null;
                });
            }
        });

        // Slider range inputs
        filterBody.querySelectorAll('.tc-filter-slider__input').forEach(function (inp) {
            inp.addEventListener('input', function () {
                var key = inp.dataset.filter;
                filters[key] = parseFloat(inp.value);
                var labelId = key === 'price_min' ? 'tc-slider-min-val' : 'tc-slider-max-val';
                var labelEl = document.getElementById(labelId);
                if (labelEl) labelEl.textContent = SYMBOL + inp.value;
            });
        });

        // Checkboxes (availability, brands, colors, etc.)
        filterBody.querySelectorAll('input[type="checkbox"][data-filter]').forEach(function (cb) {
            cb.addEventListener('change', function () {
                var key = cb.dataset.filter;
                if (!filters[key]) filters[key] = [];
                if (cb.checked) {
                    if (filters[key].indexOf(cb.value) === -1) filters[key].push(cb.value);
                } else {
                    filters[key] = filters[key].filter(function (v) { return v !== cb.value; });
                    if (filters[key].length === 0) delete filters[key];
                }
            });
        });

        // Size pill buttons (toggle)
        filterBody.querySelectorAll('.tc-filter-size-pill[data-filter]').forEach(function (btn) {
            btn.addEventListener('click', function () {
                var key = btn.dataset.filter;
                var val = btn.dataset.value;
                if (!filters[key]) filters[key] = [];
                var idx = filters[key].indexOf(val);
                if (idx > -1) {
                    filters[key].splice(idx, 1);
                    btn.classList.remove('tc-filter-size-pill--active');
                    if (filters[key].length === 0) delete filters[key];
                } else {
                    filters[key].push(val);
                    btn.classList.add('tc-filter-size-pill--active');
                }
            });
        });

        // Category list items (single select — clicking toggles)
        filterBody.querySelectorAll('.tc-filter-catlist__item[data-filter="category"]').forEach(function (link) {
            link.addEventListener('click', function (e) {
                e.preventDefault();
                var val = link.dataset.value;
                if (filters.category === val) {
                    delete filters.category;
                    link.classList.remove('tc-filter-catlist__item--active');
                } else {
                    // Remove active from all
                    filterBody.querySelectorAll('.tc-filter-catlist__item--active').forEach(function (el) {
                        el.classList.remove('tc-filter-catlist__item--active');
                    });
                    filters.category = val;
                    link.classList.add('tc-filter-catlist__item--active');
                }
            });
        });
    }

    /* Color name → hex mapper for swatches */
    var COLOR_MAP = {
        'red':'#EF4444','blue':'#3B82F6','green':'#22C55E','yellow':'#EAB308',
        'orange':'#F97316','purple':'#A855F7','pink':'#EC4899','black':'#171717',
        'white':'#FAFAFA','gray':'#6B7280','grey':'#6B7280','brown':'#92400E',
        'navy':'#1E3A5F','beige':'#D4B896','cream':'#FFFDD0','gold':'#D4AF37',
        'silver':'#C0C0C0','teal':'#14B8A6','coral':'#F87171','indigo':'#6366F1',
        'maroon':'#7F1D1D','olive':'#84CC16','tan':'#D2B48C','turquoise':'#06B6D4',
        'burgundy':'#800020','charcoal':'#36454F','ivory':'#FFFFF0','lavender':'#C4B5FD',
        'mint':'#A7F3D0','peach':'#FDBA74','rust':'#B91C1C','sage':'#9CA38F',
        'slate':'#64748B','wine':'#722F37','khaki':'#C3B091','mauve':'#E0B0FF',
        'mustard':'#CEAB07','plum':'#7B2D8B','taupe':'#483C32',
    };
    function colorNameToHex(name) {
        var lower = (name || '').toLowerCase().trim();
        if (COLOR_MAP[lower]) return COLOR_MAP[lower];
        if (/^#[0-9a-f]{3,6}$/i.test(lower)) return lower;
        var hash = 0;
        for (var i = 0; i < lower.length; i++) hash = lower.charCodeAt(i) + ((hash << 5) - hash);
        var h = Math.abs(hash) % 360;
        return 'hsl(' + h + ',55%,50%)';
    }

    /* ────────────────────────────────────────────────────────────────────────
     *  Active filter chips
     * ──────────────────────────────────────────────────────────────────────── */

    function renderActiveChips() {
        var html = '';
        var hasAny = false;

        Object.keys(filters).forEach(function (key) {
            var val = filters[key];
            if (val == null || (Array.isArray(val) && val.length === 0)) return;

            // Price range chip (combine min+max)
            if (key === 'price_min' || key === 'price_max') {
                if (filters.price_min || filters.price_max) {
                    var label = SYMBOL + (filters.price_min || 0) + ' – ' + SYMBOL + (filters.price_max || '∞');
                    html += chip('Price: ' + label, 'price');
                    hasAny = true;
                }
                return;
            }

            // Category chip
            if (key === 'category') {
                var def = filterDefs.find(function (d) { return d.key === 'category'; });
                var opt = def ? (def.options || []).find(function (o) { return String(o.value) === String(val); }) : null;
                html += chip(opt ? opt.label : 'Category', 'category');
                hasAny = true;
                return;
            }

            // Array-based filters (availability, attributes, etc.)
            var def = filterDefs.find(function (d) { return d.key === key; });
            if (!def) return;

            (Array.isArray(val) ? val : [val]).forEach(function (v) {
                var opt = (def.options || []).find(function (o) { return String(o.value) === String(v); });
                var label = opt ? opt.label : v;
                html += chip(label, key + ':' + v);
                hasAny = true;
            });
        });

        if (hasAny) {
            html += '<button class="tc-chip tc-chip--clear" data-clear-all>Clear All</button>';
        }

        activeChips.innerHTML = html;

        // Wire chip remove buttons
        activeChips.querySelectorAll('[data-remove]').forEach(function (btn) {
            btn.addEventListener('click', function () {
                var removeKey = btn.dataset.remove;
                if (removeKey === 'price') {
                    delete filters.price_min;
                    delete filters.price_max;
                } else if (removeKey === 'category') {
                    delete filters.category;
                } else {
                    var parts = removeKey.split(':');
                    if (parts.length === 2) {
                        var key = parts[0], v = parts[1];
                        if (filters[key]) {
                            filters[key] = filters[key].filter(function (x) { return x !== v; });
                            if (filters[key].length === 0) delete filters[key];
                        }
                    }
                }
                renderFilterForm();
                renderActiveChips();
                currentPage = 1;
                loadProducts();
            });
        });

        // Clear all
        var clearBtn = activeChips.querySelector('[data-clear-all]');
        if (clearBtn) {
            clearBtn.addEventListener('click', function () {
                filters = {};
                renderFilterForm();
                renderActiveChips();
                currentPage = 1;
                loadProducts();
            });
        }
    }

    function chip(label, removeKey) {
        return '<span class="tc-chip">' + esc(label) +
            '<button class="tc-chip__remove" data-remove="' + esc(removeKey) + '" aria-label="Remove">×</button>' +
            '</span>';
    }

    /* ────────────────────────────────────────────────────────────────────────
     *  Load products via AJAX
     * ──────────────────────────────────────────────────────────────────────── */

    function loadProducts(append) {
        if (isLoading) return;
        isLoading = true;
        if (!append) loading.hidden = false;

        var params = new URLSearchParams();
        params.set('page', currentPage);
        params.set('sort', currentSort);
        params.set('order', currentOrder);

        // Category: use filter selection OR fall back to page-level category
        var catVal = filters.category ? parseInt(filters.category) : CAT_ID;
        if (catVal) params.set('category', catVal);

        // Tag: page-level tag archive scope (no filter UI for tags in V1)
        if (TAG_SLUG) params.set('tag', TAG_SLUG);

        // Price
        if (filters.price_min != null) params.set('price_min', filters.price_min);
        if (filters.price_max != null) params.set('price_max', filters.price_max);

        // Availability (multi-select: in_stock, out_of_stock, on_sale, free_delivery)
        if (filters.availability && filters.availability.length) {
            params.set('availability', filters.availability.join(','));
        }

        // Legacy stock param (if the old 'stock' key exists)
        if (filters.stock && filters.stock.length) {
            params.set('stock', filters.stock[0]);
        }

        // Attribute filters
        Object.keys(filters).forEach(function (key) {
            if (key.indexOf('attr_') === 0 && filters[key].length) {
                params.set(key, filters[key].join(','));
            }
        });

        // Special filter mode (Just For You landing pages)
        if (FILTER_MODE) params.set('filter', FILTER_MODE);

        fetch(API + '/products?' + params.toString())
            .then(function (r) { return r.json(); })
            .then(function (data) {
                totalPages = data.total_pages || 1;
                if (append) {
                    appendGrid(data.products || []);
                } else {
                    renderGrid(data.products || []);
                }
                renderPagination(data.page || 1, totalPages);
                updateCount(data.total || 0);
                if (!append) renderActiveChips();
                isLoading = false;
                isAppending = false;
                loading.hidden = true;
                if (!append) {
                    app.scrollIntoView({ behavior: 'smooth', block: 'start' });
                }
            })
            .catch(function () {
                isLoading = false;
                isAppending = false;
                loading.hidden = true;
            });
    }

    function renderGrid(products) {
        if (!products.length) {
            grid.innerHTML = '<div class="tc-shop__empty">' +
                '<i class="bi bi-box-seam" style="font-size:48px;color:#d4d4d4"></i>' +
                '<p>No products found.</p>' +
                '<p class="tc-muted">Try adjusting your filters or browse all products.</p></div>';
            return;
        }

        grid.innerHTML = buildCardsHtml(products);

        // Re-apply current view mode CSS class after AJAX re-render.
        applyViewMode(currentView);
    }

    function appendGrid(products) {
        if (!products.length) return;
        grid.insertAdjacentHTML('beforeend', buildCardsHtml(products));
        applyViewMode(currentView);
    }

    function buildCardsHtml(products) {
        return products.map(function (p) {
            var hasDiscount = p.list_price > p.price && p.list_price > 0;
            var inStock     = p.stock_status === 'in_stock';
            var imgHtml     = p.image && p.image.url
                ? '<img src="' + esc(p.image.url) + '" alt="' + esc(p.name) + '" class="product-img" loading="lazy">'
                : '<div class="product-img-placeholder"><i class="bi bi-image" style="font-size:2.5rem;color:#d4d4d4"></i></div>';

            var cartOverlay = '';
            if (CATALOG_MODE && CM_TYPE === 'inquiry' && inStock) {
                cartOverlay = '<div class="product-card__cart-overlay product-card__cart-overlay--inquiry"><a href="' + esc(CM_INQUIRY_URL) + '" class="product-card__cart-btn product-card__inquiry-btn"><i class="bi bi-envelope"></i> ' + esc(CM_INQUIRY_TEXT) + '</a></div>';
            } else if (inStock && !DENY_CART) {
                cartOverlay = p.has_variations
                    ? '<div class="product-card__cart-overlay"><a href="' + esc(p.url) + '" class="product-card__cart-btn product-card__choose-btn">Choose Options</a></div>'
                    : '<div class="product-card__cart-overlay"><button type="button" class="product-card__cart-btn tc-add-to-cart-btn" data-product-id="' + p.id + '">Add to Cart</button></div>';
            } else if (!inStock) {
                cartOverlay = '<div class="product-card__cart-overlay product-card__cart-overlay--notify"><a href="' + esc(p.url) + '#tc-notify-wrap" class="product-card__cart-btn product-card__notify-btn"><i class="bi bi-bell"></i> Notify Me</a></div>';
            }

            var stockBadge = '';
            if ( !inStock ) {
                stockBadge = '<span class="product-card__stock-badge product-card__stock-badge--out">Out of Stock</span>';
            } else if ( SHOW_STOCK && p.stock_quantity > 0 ) {
                stockBadge = '<span class="product-card__stock-badge product-card__stock-badge--count">' + p.stock_quantity + ' in stock</span>';
            }

            // Price HTML (reused in grid-price and control column).
            var priceRow = '';
            var ppuHtml  = '';
            if ( HIDE_PRICE ) {
                priceRow = '<a href="' + esc(ACCT_URL) + '" class="product-price" style="font-size:12px;color:#2563EB">Login to see price</a>';
            } else {
                priceRow = '<span class="product-price">' + SYMBOL + fmtPrice(p.price) + '</span>' +
                    (hasDiscount ? '<span class="product-original-price">' + SYMBOL + fmtPrice(p.list_price) + '</span>' : '') +
                    (SHOW_TAX_CAT ? '<span class="product-card__tax-note">incl. tax</span>' : '');
                // Price per unit
                if ( PPU_CATALOG && p.price > 0 && PPU_UNITS > 0 ) {
                    var ppuPrice = (p.price / PPU_UNITS) * PPU_PER_X;
                    // Only show when per-unit price meaningfully differs from product price.
                    if ( Math.abs(ppuPrice - p.price) > 0.01 ) {
                        var ppuLabel = PPU_PER_X > 1 ? PPU_PER_X + ' ' + PPU_UNIT : PPU_UNIT;
                        ppuHtml = '<span class="product-card__ppu">(' + SYMBOL + fmtPrice(ppuPrice) + ' per ' + esc(ppuLabel) + ')</span>';
                    }
                }
            }

            // ── Stars (list view) ──
            var starsHtml = '';
            if ( p.rating_count > 0 ) {
                starsHtml = '<span class="product-card__stars">';
                for (var s = 1; s <= 5; s++) {
                    starsHtml += '<i class="bi bi-star' + (s <= Math.round(p.rating_avg) ? '-fill' : '') + '" style="color:#f59e0b;font-size:12px"></i>';
                }
                starsHtml += '<span class="product-card__review-count">(' + p.rating_count + ')</span></span>';
            }

            // ── Features — honor each attribute's "List Display" (filter_type):
            //    'color' → swatch dots (grid + list), 'none' → hidden (excluded
            //    server-side), anything else → text chip (list view only). ──
            var featsHtml = '';
            var feats = p.features || [];
            if (feats.length > 0) {
                // Group colour values by attribute name so multiple values
                // (e.g. Blue/Green/Grey) render as one labelled row of dots.
                var colorGroups = {}, colorOrder = [], textChips = [];
                feats.forEach(function (f) {
                    if ((f.filter_type || '') === 'color') {
                        if (!colorGroups[f.name]) { colorGroups[f.name] = []; colorOrder.push(f.name); }
                        colorGroups[f.name].push({ label: f.value, hex: f.color_hex || colorNameToHex(f.value) });
                    } else {
                        textChips.push(f);
                    }
                });

                colorOrder.forEach(function (nm) {
                    featsHtml += '<div class="product-card__swatches"><em class="product-card__swatches-label">' + esc(nm) + '</em><span class="product-card__swatch-dots">';
                    colorGroups[nm].forEach(function (c) {
                        featsHtml += '<span class="product-card__swatch" title="' + esc(c.label) + '" style="background:' + esc(c.hex) + '"></span>';
                    });
                    featsHtml += '</span></div>';
                });

                if (textChips.length) {
                    featsHtml += '<div class="product-card__features">';
                    textChips.forEach(function (f) {
                        featsHtml += '<span class="product-card__feature"><em class="product-card__feature-label">' + esc(f.name) + '</em><em>' + esc(f.value) + '</em></span>';
                    });
                    featsHtml += '</div>';
                }
            }

            // ── Stock label for control column ──
            var stockLabel = inStock
                ? '<span class="product-card__stock-badge product-card__stock-badge--in" style="margin:8px 0">In Stock</span>'
                : '<span class="product-card__stock-badge product-card__stock-badge--out" style="margin:8px 0">Out of Stock</span>';

            // ── Cart button for control column ──
            var listCartBtn = '';
            if (CATALOG_MODE && CM_TYPE === 'inquiry' && inStock) {
                listCartBtn = '<a href="' + esc(CM_INQUIRY_URL) + '" class="product-card__list-cart-btn product-card__list-inquiry-btn"><i class="bi bi-envelope"></i> ' + esc(CM_INQUIRY_TEXT) + '</a>';
            } else if (inStock && !DENY_CART) {
                listCartBtn = p.has_variations
                    ? '<a href="' + esc(p.url) + '" class="product-card__list-cart-btn product-card__list-choose-btn"><i class="bi bi-sliders"></i> Choose Options</a>'
                    : '<button type="button" class="product-card__list-cart-btn tc-add-to-cart-btn" data-product-id="' + p.id + '"><i class="bi bi-cart-plus"></i> Add to Cart</button>';
            } else if (!inStock) {
                listCartBtn = '<a href="' + esc(p.url) + '#tc-notify-wrap" class="product-card__list-cart-btn product-card__list-notify-btn"><i class="bi bi-bell"></i> Notify Me</a>';
            }

            return '<div class="product-card" data-product-id="' + p.id + '">' +

                // ── Image column ──
                '<div class="product-card__image-wrap">' +
                    '<a href="' + esc(p.url) + '">' + imgHtml + '</a>' +
                    (hasDiscount ? '<span class="product-card__badge">Sale</span>' : '') +
                    '<div class="product-hover-actions">' +
                        (WISHLIST_ON ? '<button type="button" class="btn-action-circle tc-wishlist-toggle" data-product-id="' + p.id + '" title="Wishlist"><i class="bi bi-heart"></i></button>' : '') +
                        (QV_ON ? '<button type="button" class="btn-action-circle tc-quickview-btn" data-quickview="' + p.id + '" title="Quick view"><i class="bi bi-eye"></i></button>' : '') +
                        (ALLOW_COMPARE ? '<button type="button" class="btn-action-circle tc-compare-toggle" data-product-id="' + p.id + '" title="Compare"><i class="bi bi-arrow-left-right"></i></button>' : '') +
                    '</div>' +
                    cartOverlay +
                '</div>' +

                // ── Info column (body) ──
                '<div class="product-card__body">' +
                    (p.brand ? '<span class="product-card__brand">' + esc(p.brand) + '</span>' : '') +
                    '<a href="' + esc(p.url) + '" class="product-title">' + esc(p.name) + '</a>' +
                    '<div class="product-card__meta-row">' + starsHtml +
                        (p.sku ? '<span class="product-card__sku">Code: ' + esc(p.sku) + '</span>' : '') +
                    '</div>' +
                    '<span class="product-card__body-stock product-card__body-stock--' + (inStock ? 'in' : 'out') + '">' + (inStock ? 'In Stock' : 'Out of Stock') + '</span>' +
                    (p.short_desc ? '<p class="product-card__short-desc">' + esc(p.short_desc) + '</p>' : '') +
                    featsHtml +
                    '<div class="product-card__price-grid"><div class="product-card__price-row">' + priceRow + ppuHtml + '</div>' + stockBadge + '</div>' +
                '</div>' +

                // ── Control column (list view only — hidden in grid via CSS) ──
                '<div class="product-card__control">' +
                    '<div class="product-card__control-price"><div class="product-card__price-row">' + priceRow + ppuHtml + '</div></div>' +
                    stockLabel +
                    listCartBtn +
                    '<div class="product-card__control-actions">' +
                        (WISHLIST_ON ? '<button type="button" class="product-card__control-icon tc-wishlist-toggle" data-product-id="' + p.id + '" title="Wishlist"><i class="bi bi-heart"></i></button>' : '') +
                        (ALLOW_COMPARE ? '<button type="button" class="product-card__control-icon tc-compare-toggle" data-product-id="' + p.id + '" title="Compare"><i class="bi bi-arrow-left-right"></i></button>' : '') +
                    '</div>' +
                '</div>' +

            '</div>';
        }).join('');
    }

    function renderPagination(page, totalPages) {
        if (!pagination) {
            pagination = document.createElement('div');
            pagination.className = 'tc-pagination';
            pagination.id = 'tc-pagination';
            grid.parentNode.insertBefore(pagination, grid.nextSibling);
        }

        // ── Load More button mode ──
        if (PAG_TYPE === 'load_more') {
            var topPag = document.getElementById('tc-pagination-top');
            if (topPag) topPag.innerHTML = '';
            if (page >= totalPages) {
                pagination.innerHTML = '';
                return;
            }
            pagination.innerHTML = '<button type="button" class="tc-pagination__load-more" id="tc-load-more">Load more products</button>';
            document.getElementById('tc-load-more').addEventListener('click', function () {
                currentPage = page + 1;
                isAppending = true;
                loadProducts(true);
            });
            return;
        }

        // ── Infinite scroll mode ──
        if (PAG_TYPE === 'infinite_scroll') {
            var topPag = document.getElementById('tc-pagination-top');
            if (topPag) topPag.innerHTML = '';
            pagination.innerHTML = page < totalPages
                ? '<div class="tc-pagination__scroll-sentinel" id="tc-scroll-sentinel"><div class="tc-spinner tc-spinner--sm"></div></div>'
                : '';
            initInfiniteScroll(page, totalPages);
            return;
        }

        // ── Default: numbered pages ──
        if (totalPages <= 1) {
            pagination.innerHTML = '';
            var topPag = document.getElementById('tc-pagination-top');
            if (topPag) topPag.innerHTML = '';
            return;
        }

        var html = '';
        for (var i = 1; i <= totalPages; i++) {
            var cls = i === page ? 'tc-pagination__item tc-pagination__item--active' : 'tc-pagination__item';
            html += '<a href="javascript:;" class="' + cls + '" data-page="' + i + '">' + i + '</a>';
        }
        pagination.innerHTML = html;

        // Also update top pagination if present.
        var topPag = document.getElementById('tc-pagination-top');
        if (topPag) topPag.innerHTML = html;

        bindPaginationClicks();
    }

    // Bind click handlers on pagination items (both top and bottom).
    function bindPaginationClicks() {
        document.querySelectorAll('#tc-pagination [data-page], #tc-pagination-top [data-page]').forEach(function (btn) {
            btn.addEventListener('click', function (e) {
                e.preventDefault();
                currentPage = parseInt(btn.dataset.page);
                loadProducts();
                window.scrollTo({ top: 0, behavior: 'smooth' });
            });
        });
    }

    // ── Infinite scroll via IntersectionObserver ──
    var scrollObserver = null;
    function initInfiniteScroll(page, totalPages) {
        if (scrollObserver) scrollObserver.disconnect();
        if (page >= totalPages) return;
        var sentinel = document.getElementById('tc-scroll-sentinel');
        if (!sentinel) return;
        scrollObserver = new IntersectionObserver(function (entries) {
            if (entries[0].isIntersecting && !isLoading) {
                currentPage = page + 1;
                isAppending = true;
                loadProducts(true);
            }
        }, { rootMargin: '200px' });
        scrollObserver.observe(sentinel);
    }

    // Bind on initial PHP-rendered pagination so it works before any AJAX fetch.
    bindPaginationClicks();

    function updateCount(total) {
        if (countEl) countEl.textContent = total;
    }

    /* ────────────────────────────────────────────────────────────────────────
     *  Helpers
     * ──────────────────────────────────────────────────────────────────────── */

    function esc(s) {
        var el = document.createElement('span');
        el.textContent = s || '';
        return el.innerHTML;
    }

    function fmtPrice(n) {
        return parseFloat(n || 0).toFixed(2);
    }

    /* ────────────────────────────────────────────────────────────────────────
     *  Init
     * ──────────────────────────────────────────────────────────────────────── */

    loadFilters();

    /* ────────────────────────────────────────────────────────────────────────
     *  Quick View Modal (works for plugin-only and theme users)
     * ──────────────────────────────────────────────────────────────────────── */

    if (QV_ON && !document.querySelector('.tc-qv-overlay')) {
        var qvOverlay = document.createElement('div');
        qvOverlay.className = 'tc-qv-overlay';
        qvOverlay.innerHTML =
            '<div class="tc-qv-modal">' +
                '<button type="button" class="tc-qv-close" aria-label="Close">&times;</button>' +
                '<div class="tc-qv-body">' +
                    '<div class="tc-qv-gallery"><div class="tc-qv-loading">Loading…</div></div>' +
                    '<div class="tc-qv-info"></div>' +
                '</div>' +
            '</div>';
        document.body.appendChild(qvOverlay);

        var qvGallery = qvOverlay.querySelector('.tc-qv-gallery');
        var qvInfo    = qvOverlay.querySelector('.tc-qv-info');
        var qvClose   = qvOverlay.querySelector('.tc-qv-close');

        function qvOpen(pid) {
            qvGallery.innerHTML = '<div class="tc-qv-loading">Loading…</div>';
            qvInfo.innerHTML = '';
            qvOverlay.classList.add('tc-qv-overlay--open');
            document.body.style.overflow = 'hidden';

            fetch(API.replace(/\/store\/?$/, '') + '/store/products/' + pid)
                .then(function(r) { return r.json(); })
                .then(function(p) {
                    if (p.error) { qvInfo.innerHTML = '<p>Product not found.</p>'; return; }
                    // Gallery
                    if (p.image && p.image.url) {
                        qvGallery.innerHTML = '<img src="' + esc(p.image.url) + '" alt="' + esc(p.name) + '">';
                    } else {
                        qvGallery.innerHTML = '<i class="bi bi-image tc-qv-gallery-placeholder"></i>';
                    }
                    // Info
                    var hasSale = p.list_price > 0 && p.list_price > p.price;
                    var discount = hasSale ? Math.round(((p.list_price - p.price) / p.list_price) * 100) : 0;
                    var inStock = p.stock_status === 'in_stock';
                    var h = '';
                    if (p.categories && p.categories.length) h += '<div class="tc-qv-category">' + esc(p.categories.join(' / ')) + '</div>';
                    h += '<h2 class="tc-qv-title">' + esc(p.name) + '</h2>';
                    h += '<div class="tc-qv-price">';
                    if (hasSale) h += '<span class="tc-qv-price-was">' + SYMBOL + fmtPrice(p.list_price) + '</span> ';
                    h += SYMBOL + fmtPrice(p.price);
                    if (discount > 0) h += ' <span style="font-size:13px;color:#dc2626;font-weight:500;">(-' + discount + '%)</span>';
                    h += '</div>';
                    h += '<div class="tc-qv-stock ' + (inStock ? 'tc-qv-stock--in' : 'tc-qv-stock--out') + '">' + (inStock ? '● In Stock' : '● Out of Stock') + '</div>';
                    if (p.short_desc || p.description) {
                        var desc = p.short_desc || (p.description || '').substring(0, 200);
                        if (desc.length > 200) desc = desc.substring(0, 200) + '…';
                        h += '<div class="tc-qv-desc">' + esc(desc) + '</div>';
                    }
                    if (p.sku) h += '<div style="font-size:12px;color:#999;">Product Code: ' + esc(p.sku) + '</div>';
                    if (inStock) {
                        h += '<div class="tc-qv-actions">';
                        if (p.has_variations) {
                            // Variation member: options must be chosen on the PDP
                            // (KI-21). Route there instead of a direct add.
                            h += '<a href="' + esc(p.url) + '" class="tc-qv-add-btn tc-qv-choose-btn"><i class="bi bi-sliders"></i> Choose Options</a>';
                        } else {
                            h += '<input type="number" class="tc-qv-qty" value="1" min="1" max="99">';
                            h += '<button type="button" class="tc-qv-add-btn tc-add-to-cart-btn" data-product-id="' + p.id + '"><i class="bi bi-cart-plus"></i> Add to Cart</button>';
                        }
                        h += '</div>';
                    }
                    h += '<a href="' + esc(p.url) + '" class="tc-qv-view-full">View Full Details →</a>';
                    qvInfo.innerHTML = h;
                })
                .catch(function() { qvInfo.innerHTML = '<p>Failed to load product.</p>'; });
        }

        function qvCloseModal() {
            qvOverlay.classList.remove('tc-qv-overlay--open');
            document.body.style.overflow = '';
        }

        qvClose.addEventListener('click', qvCloseModal);
        qvOverlay.addEventListener('click', function(e) { if (e.target === qvOverlay) qvCloseModal(); });
        document.addEventListener('keydown', function(e) {
            if (e.key === 'Escape' && qvOverlay.classList.contains('tc-qv-overlay--open')) qvCloseModal();
        });

        document.addEventListener('click', function(e) {
            var btn = e.target.closest('[data-quickview]');
            if (!btn) return;
            e.preventDefault();
            e.stopPropagation();
            var pid = btn.getAttribute('data-quickview');
            if (pid) qvOpen(pid);
        });
    }

})();
