(function () {
    var PORTAL_CLASS = 'vl-filter-picker-menu--portal';
    var openMenus = [];
    var globalListenersBound = false;

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

    function walk(nodes, fn) {
        (nodes || []).forEach(function (node) {
            fn(node);
            if (node.children && node.children.length) {
                walk(node.children, fn);
            }
        });
    }

    function positionFilterMenu(toggle, menu, minWidth) {
        var rect = toggle.getBoundingClientRect();
        var gap = 4;
        var padding = 8;
        var width = Math.max(rect.width, minWidth || 280);

        menu.style.position = 'fixed';
        menu.style.left = rect.left + 'px';
        menu.style.top = (rect.bottom + gap) + 'px';
        menu.style.width = width + 'px';
        menu.style.minWidth = width + 'px';
        menu.style.right = 'auto';
        menu.style.zIndex = '1200';

        var menuHeight = menu.offsetHeight || 320;
        if (rect.bottom + gap + menuHeight > window.innerHeight - padding) {
            menu.style.top = Math.max(padding, rect.top - menuHeight - gap) + 'px';
        }

        if (rect.left + width > window.innerWidth - padding) {
            menu.style.left = (window.innerWidth - width - padding) + 'px';
        }
    }

    function openFilterMenu(picker, toggle, menu, minWidth) {
        closeAllFilterMenus();
        document.body.appendChild(menu);
        menu.classList.add(PORTAL_CLASS);
        menu.classList.remove('d-none');
        toggle.setAttribute('aria-expanded', 'true');
        positionFilterMenu(toggle, menu, minWidth);
        openMenus.push({ picker: picker, toggle: toggle, menu: menu, minWidth: minWidth || 280 });
    }

    function closeFilterMenu(picker, toggle, menu) {
        if (!menu) {
            return;
        }

        menu.classList.add('d-none');
        if (toggle) {
            toggle.setAttribute('aria-expanded', 'false');
        }

        menu.style.position = '';
        menu.style.top = '';
        menu.style.left = '';
        menu.style.width = '';
        menu.style.right = '';
        menu.style.zIndex = '';
        menu.style.minWidth = '';
        menu.classList.remove(PORTAL_CLASS);

        if (picker && menu.parentElement !== picker) {
            picker.appendChild(menu);
        }

        openMenus = openMenus.filter(function (entry) {
            return entry.menu !== menu;
        });
    }

    function closeAllFilterMenus() {
        openMenus.slice().forEach(function (entry) {
            closeFilterMenu(entry.picker, entry.toggle, entry.menu);
        });
    }

    function isFilterMenuClick(event, picker, menu) {
        return picker.contains(event.target) || (menu && menu.contains(event.target));
    }

    function bindGlobalFilterMenuListeners() {
        if (globalListenersBound) {
            return;
        }

        globalListenersBound = true;

        document.addEventListener('click', function (event) {
            openMenus.slice().forEach(function (entry) {
                if (!isFilterMenuClick(event, entry.picker, entry.menu)) {
                    closeFilterMenu(entry.picker, entry.toggle, entry.menu);
                }
            });
        });

        window.addEventListener('scroll', function () {
            openMenus.forEach(function (entry) {
                positionFilterMenu(entry.toggle, entry.menu, entry.minWidth || 280);
            });
        }, true);

        window.addEventListener('resize', function () {
            openMenus.forEach(function (entry) {
                positionFilterMenu(entry.toggle, entry.menu, entry.minWidth || 280);
            });
        });
    }

    function initMultiFilterPickers() {
        document.querySelectorAll('.pl-multi-filter-picker').forEach(function (picker) {
            if (picker.dataset.vlReady === '1') {
                return;
            }

            var selected = new Set();
            var labelMap = {};

            try {
                JSON.parse(picker.dataset.selected || '[]').forEach(function (value) {
                    selected.add(String(value));
                });
                labelMap = JSON.parse(picker.dataset.labels || '{}');
            } catch (error) {
                selected = new Set();
                labelMap = {};
            }

            var toggleBtn = picker.querySelector('[data-multi-filter-toggle]');
            var menu = picker.querySelector('[data-multi-filter-menu]');
            var searchInput = picker.querySelector('[data-multi-filter-search]');
            var emptyEl = picker.querySelector('[data-multi-filter-empty]');
            var labelEl = picker.querySelector('[data-multi-filter-label]');
            var inputsEl = picker.querySelector('[data-multi-filter-inputs]');
            var clearBtn = picker.querySelector('[data-multi-filter-clear]');
            var emptyLabel = picker.dataset.emptyLabel || 'All';
            var inputName = picker.dataset.inputName || 'items[]';

            function syncInputs() {
                if (!inputsEl) {
                    return;
                }

                inputsEl.innerHTML = '';
                Array.from(selected).sort().forEach(function (value) {
                    var input = document.createElement('input');
                    input.type = 'hidden';
                    input.name = inputName;
                    input.value = value;
                    inputsEl.appendChild(input);
                });
            }

            function syncLabel() {
                if (!labelEl) {
                    return;
                }

                if (selected.size === 0) {
                    labelEl.textContent = emptyLabel;
                    return;
                }

                if (selected.size === 1) {
                    var only = Array.from(selected)[0];
                    labelEl.textContent = labelMap[only] || only;
                    return;
                }

                labelEl.textContent = selected.size + ' selected';
            }

            function filterOptions() {
                var term = (searchInput?.value || '').trim().toLowerCase();
                var visible = 0;

                picker.querySelectorAll('[data-multi-filter-option]').forEach(function (option) {
                    var label = option.dataset.label || '';
                    var show = !term || label.indexOf(term) !== -1;
                    option.classList.toggle('d-none', !show);
                    if (show) {
                        visible += 1;
                    }
                });

                emptyEl?.classList.toggle('d-none', visible > 0);
            }

            function closeMenu() {
                closeFilterMenu(picker, toggleBtn, menu);
            }

            toggleBtn?.addEventListener('click', function (event) {
                event.preventDefault();
                event.stopPropagation();
                var isOpen = !menu?.classList.contains('d-none');
                if (isOpen) {
                    closeMenu();
                    return;
                }

                openFilterMenu(picker, toggleBtn, menu, 300);
                searchInput?.focus();
            });

            picker.addEventListener('change', function (event) {
                var checkbox = event.target.closest('[data-multi-filter-checkbox]');
                if (!checkbox) {
                    return;
                }

                if (checkbox.checked) {
                    selected.add(checkbox.value);
                } else {
                    selected.delete(checkbox.value);
                }

                syncInputs();
                syncLabel();
            });

            searchInput?.addEventListener('input', filterOptions);

            clearBtn?.addEventListener('click', function () {
                selected.clear();
                picker.querySelectorAll('[data-multi-filter-checkbox]').forEach(function (checkbox) {
                    checkbox.checked = false;
                });
                syncInputs();
                syncLabel();
                if (searchInput) {
                    searchInput.value = '';
                }
                filterOptions();
            });

            syncInputs();
            syncLabel();
            filterOptions();
            picker.dataset.vlReady = '1';
        });
    }

    function initDimensionFilters() {
        document.querySelectorAll('.vl-dimension-filter-picker').forEach(function (picker) {
            if (picker.dataset.vlReady === '1') {
                return;
            }

            var tree = [];
            var selected = new Set();
            var nameMap = {};
            var expanded = new Set();

            try {
                tree = JSON.parse(picker.dataset.tree || '[]');
                JSON.parse(picker.dataset.selected || '[]').forEach(function (id) {
                    selected.add(String(id));
                });
            } catch (error) {
                tree = [];
            }

            walk(tree, function (node) {
                if (node.selectable) {
                    nameMap[String(node.id)] = node.name;
                }
                if (node.children && node.children.length) {
                    expanded.add(String(node.id));
                }
            });

            var toggleBtn = picker.querySelector('[data-vl-dimension-toggle]');
            var menu = picker.querySelector('[data-vl-dimension-menu]');
            var treeEl = picker.querySelector('[data-vl-dimension-tree]');
            var searchInput = picker.querySelector('[data-vl-dimension-search]');
            var emptyEl = picker.querySelector('[data-vl-dimension-empty]');
            var labelEl = picker.querySelector('[data-vl-dimension-label]');
            var inputsEl = picker.querySelector('[data-vl-dimension-inputs]');
            var clearBtn = picker.querySelector('[data-vl-dimension-clear]');
            var emptyLabel = picker.dataset.emptyLabel || 'All';
            var inputName = picker.dataset.inputName || 'dimension_value_ids[]';

            function syncInputs() {
                if (!inputsEl) {
                    return;
                }

                inputsEl.innerHTML = '';
                Array.from(selected).sort(function (a, b) {
                    return Number(a) - Number(b);
                }).forEach(function (id) {
                    var input = document.createElement('input');
                    input.type = 'hidden';
                    input.name = inputName;
                    input.value = id;
                    inputsEl.appendChild(input);
                });
            }

            function syncLabel() {
                if (!labelEl) {
                    return;
                }

                if (selected.size === 0) {
                    labelEl.textContent = emptyLabel;
                    return;
                }

                if (selected.size === 1) {
                    labelEl.textContent = nameMap[Array.from(selected)[0]] || '1 selected';
                    return;
                }

                labelEl.textContent = selected.size + ' selected';
            }

            function nodeMatches(node, term) {
                if (!term) {
                    return true;
                }

                var haystack = ((node.name || '') + ' ' + (node.code || '')).toLowerCase();
                return haystack.indexOf(term) !== -1;
            }

            function subtreeMatches(node, term) {
                if (!term) {
                    return true;
                }

                if (nodeMatches(node, term)) {
                    return true;
                }

                return (node.children || []).some(function (child) {
                    return subtreeMatches(child, term);
                });
            }

            function renderNodes(nodes, depth) {
                return nodes.map(function (node) {
                    var id = String(node.id);
                    var term = (searchInput?.value || '').trim().toLowerCase();
                    var hasChildren = !!(node.children && node.children.length);
                    var padding = depth * 16;

                    if (term && !subtreeMatches(node, term)) {
                        return '';
                    }

                    if (!node.selectable) {
                        var groupChildren = renderNodes(node.children || [], depth + 1);
                        if (!groupChildren) {
                            return '';
                        }

                        return `
                            <div class="vl-dimension-picker-item vl-dimension-picker-item-group" style="padding-left:${padding}px">
                                <button type="button" class="pl-dimension-picker-toggle-btn ${hasChildren ? '' : 'is-hidden'}" data-vl-dimension-expand="${id}" aria-label="Expand">
                                    <i class="bi ${expanded.has(id) ? 'bi-chevron-down' : 'bi-chevron-right'}" style="font-size:11px"></i>
                                </button>
                                <div class="pl-dimension-picker-group">${escapeHtml(node.name)}</div>
                            </div>
                            <div class="${expanded.has(id) ? '' : 'd-none'}" data-vl-dimension-children="${id}">
                                ${groupChildren}
                            </div>
                        `;
                    }

                    var childHtml = hasChildren && expanded.has(id) ? renderNodes(node.children, depth + 1) : '';
                    var isSelected = selected.has(id);

                    return `
                        <div class="vl-dimension-picker-item vl-dimension-picker-row ${isSelected ? 'is-selected' : ''}" style="padding-left:${padding}px">
                            <button type="button" class="pl-dimension-picker-toggle-btn ${hasChildren ? '' : 'is-hidden'}" data-vl-dimension-expand="${id}" aria-label="Expand">
                                <i class="bi ${expanded.has(id) ? 'bi-chevron-down' : 'bi-chevron-right'}" style="font-size:11px"></i>
                            </button>
                            <label class="vl-dimension-picker-choice">
                                <input type="checkbox" value="${id}" data-vl-dimension-checkbox ${isSelected ? 'checked' : ''}>
                            </label>
                            <span class="vl-dimension-picker-code">${escapeHtml(node.code || '—')}</span>
                            <span class="vl-dimension-picker-name">${escapeHtml(node.name)}</span>
                        </div>
                        ${hasChildren ? `<div class="${expanded.has(id) ? '' : 'd-none'}" data-vl-dimension-children="${id}">${childHtml}</div>` : ''}
                    `;
                }).join('');
            }

            function renderTree() {
                if (!treeEl) {
                    return;
                }

                var html = renderNodes(tree, 0);
                treeEl.innerHTML = html;
                emptyEl?.classList.toggle('d-none', html.trim() !== '');

                openMenus.forEach(function (entry) {
                    if (entry.menu === menu) {
                        positionFilterMenu(entry.toggle, entry.menu, entry.minWidth || 320);
                    }
                });
            }

            function closeMenu() {
                closeFilterMenu(picker, toggleBtn, menu);
            }

            toggleBtn?.addEventListener('click', function (event) {
                event.preventDefault();
                event.stopPropagation();
                var isOpen = !menu?.classList.contains('d-none');
                if (isOpen) {
                    closeMenu();
                    return;
                }

                openFilterMenu(picker, toggleBtn, menu, 360);
                searchInput?.focus();
            });

            treeEl?.addEventListener('click', function (event) {
                var expandBtn = event.target.closest('[data-vl-dimension-expand]');
                if (expandBtn) {
                    event.preventDefault();
                    var nodeId = expandBtn.getAttribute('data-vl-dimension-expand');
                    if (expanded.has(nodeId)) {
                        expanded.delete(nodeId);
                    } else {
                        expanded.add(nodeId);
                    }
                    renderTree();
                    return;
                }
            });

            treeEl?.addEventListener('change', function (event) {
                var checkbox = event.target.closest('[data-vl-dimension-checkbox]');
                if (!checkbox) {
                    return;
                }

                if (checkbox.checked) {
                    selected.add(checkbox.value);
                } else {
                    selected.delete(checkbox.value);
                }

                syncInputs();
                syncLabel();
                renderTree();
            });

            searchInput?.addEventListener('input', function () {
                if (searchInput.value.trim()) {
                    walk(tree, function (node) {
                        if (node.children && node.children.length) {
                            expanded.add(String(node.id));
                        }
                    });
                }
                renderTree();
            });

            clearBtn?.addEventListener('click', function () {
                selected.clear();
                if (searchInput) {
                    searchInput.value = '';
                }
                syncInputs();
                syncLabel();
                renderTree();
            });

            syncInputs();
            syncLabel();
            renderTree();
            picker.dataset.vlReady = '1';
        });
    }

    function initPartyFilter() {
        var picker = document.querySelector('.vl-party-filter-picker');
        if (!picker || picker.dataset.vlReady === '1') {
            return;
        }

        var tree = [];
        var selected = new Set();
        var expanded = new Set();
        var labelMap = {};

        try {
            tree = JSON.parse(picker.dataset.tree || '[]');
            JSON.parse(picker.dataset.selected || '[]').forEach(function (value) {
                if (value) {
                    selected.add(String(value));
                }
            });
        } catch (error) {
            tree = [];
        }

        walk(tree, function (node) {
            if (node.selectable && node.value) {
                labelMap[node.value] = node.name + (node.type_label ? ' (' + node.type_label + ')' : '');
            } else if (node.children && node.children.length) {
                expanded.add(String(node.id));
            }
        });

        var toggleBtn = picker.querySelector('[data-vl-party-toggle]');
        var menu = picker.querySelector('[data-vl-party-menu]');
        var treeEl = picker.querySelector('[data-vl-party-tree]');
        var searchInput = picker.querySelector('[data-vl-party-search]');
        var emptyEl = picker.querySelector('[data-vl-party-empty]');
        var labelEl = picker.querySelector('[data-vl-party-label]');
        var inputsEl = picker.querySelector('[data-vl-party-inputs]');
        var clearBtn = picker.querySelector('[data-vl-party-clear]');
        var emptyLabel = picker.dataset.emptyLabel || 'All names';
        var inputName = picker.dataset.inputName || 'parties[]';

        function syncInputs() {
            if (!inputsEl) {
                return;
            }

            inputsEl.innerHTML = '';
            Array.from(selected).sort().forEach(function (value) {
                var input = document.createElement('input');
                input.type = 'hidden';
                input.name = inputName;
                input.value = value;
                inputsEl.appendChild(input);
            });
        }

        function syncLabel() {
            if (!labelEl) {
                return;
            }

            if (selected.size === 0) {
                labelEl.textContent = emptyLabel;
                return;
            }

            if (selected.size === 1) {
                labelEl.textContent = labelMap[Array.from(selected)[0]] || '1 name selected';
                return;
            }

            labelEl.textContent = selected.size + ' names selected';
        }

        function nodeMatches(node, term) {
            if (!term) {
                return true;
            }

            var haystack = ((node.code || '') + ' ' + (node.name || '') + ' ' + (node.type_label || '')).toLowerCase();
            return haystack.indexOf(term) !== -1;
        }

        function subtreeMatches(node, term) {
            if (!term) {
                return true;
            }

            if (nodeMatches(node, term)) {
                return true;
            }

            return (node.children || []).some(function (child) {
                return subtreeMatches(child, term);
            });
        }

        function renderNodes(nodes, depth) {
            return nodes.map(function (node) {
                var id = String(node.id);
                var term = (searchInput?.value || '').trim().toLowerCase();
                var hasChildren = !!(node.children && node.children.length);

                if (term && !subtreeMatches(node, term)) {
                    return '';
                }

                if (!node.selectable) {
                    var groupChildren = renderNodes(node.children || [], depth + 1);
                    if (!groupChildren) {
                        return '';
                    }

                    return `
                        <div class="pl-party-picker-item pl-party-picker-item-group" style="padding-left:${depth * 16}px">
                            <button type="button" class="pl-party-picker-toggle-btn ${hasChildren ? '' : 'is-hidden'}" data-vl-party-expand="${id}" aria-label="Expand">
                                <i class="bi ${expanded.has(id) ? 'bi-chevron-down' : 'bi-chevron-right'}" style="font-size:11px"></i>
                            </button>
                            <div class="pl-party-picker-group">${escapeHtml(node.name)}</div>
                        </div>
                        <div class="${expanded.has(id) ? '' : 'd-none'}" data-vl-party-children="${id}">
                            ${groupChildren}
                        </div>
                    `;
                }

                var childHtml = hasChildren && expanded.has(id) ? renderNodes(node.children, depth + 1) : '';
                var isSelected = selected.has(node.value);

                return `
                    <div class="pl-party-picker-item pl-party-picker-row ${isSelected ? 'is-selected' : ''}"
                         style="padding-left:${depth * 16}px">
                        <button type="button" class="pl-party-picker-toggle-btn ${hasChildren ? '' : 'is-hidden'}" data-vl-party-expand="${id}" aria-label="Expand">
                            <i class="bi ${expanded.has(id) ? 'bi-chevron-down' : 'bi-chevron-right'}" style="font-size:11px"></i>
                        </button>
                        <label class="pl-party-picker-choice">
                            <input type="checkbox" value="${escapeHtml(node.value)}" data-vl-party-checkbox ${isSelected ? 'checked' : ''}>
                        </label>
                        <span class="pl-party-picker-code">${escapeHtml(node.code || '—')}</span>
                        <span class="pl-party-picker-name">${escapeHtml(node.name || '')}</span>
                        <span class="pl-party-picker-type">${escapeHtml(node.type_label || '')}</span>
                    </div>
                    ${hasChildren ? `<div class="${expanded.has(id) ? '' : 'd-none'}" data-vl-party-children="${id}">${childHtml}</div>` : ''}
                `;
            }).join('');
        }

        function renderTree() {
            if (!treeEl) {
                return;
            }

            var html = renderNodes(tree, 0);
            treeEl.innerHTML = html;
            emptyEl?.classList.toggle('d-none', html.trim() !== '');
        }

        function closeMenu() {
            closeFilterMenu(picker, toggleBtn, menu);
        }

        toggleBtn?.addEventListener('click', function (event) {
            event.preventDefault();
            event.stopPropagation();
            var isOpen = !menu?.classList.contains('d-none');
            if (isOpen) {
                closeMenu();
                return;
            }

            openFilterMenu(picker, toggleBtn, menu, 560);
            searchInput?.focus();
        });

        treeEl?.addEventListener('click', function (event) {
            var expandBtn = event.target.closest('[data-vl-party-expand]');
            if (expandBtn) {
                event.preventDefault();
                var nodeId = expandBtn.getAttribute('data-vl-party-expand');
                if (expanded.has(nodeId)) {
                    expanded.delete(nodeId);
                } else {
                    expanded.add(nodeId);
                }
                renderTree();
            }
        });

        treeEl?.addEventListener('change', function (event) {
            var checkbox = event.target.closest('[data-vl-party-checkbox]');
            if (!checkbox) {
                return;
            }

            if (checkbox.checked) {
                selected.add(checkbox.value);
            } else {
                selected.delete(checkbox.value);
            }

            syncInputs();
            syncLabel();
            renderTree();
        });

        searchInput?.addEventListener('input', function () {
            if (searchInput.value.trim()) {
                walk(tree, function (node) {
                    if (node.children && node.children.length) {
                        expanded.add(String(node.id));
                    }
                });
            }
            renderTree();
        });

        clearBtn?.addEventListener('click', function () {
            selected.clear();
            if (searchInput) {
                searchInput.value = '';
            }
            syncInputs();
            syncLabel();
            renderTree();
        });

        syncInputs();
        syncLabel();
        renderTree();
        picker.dataset.vlReady = '1';
    }

    document.addEventListener('DOMContentLoaded', function () {
        bindGlobalFilterMenuListeners();
        initMultiFilterPickers();
        initDimensionFilters();
        initPartyFilter();
    });
})();
