/**
 * ITSWEBER CMS – Page Builder
 *
 * @author    Benny Weber (ITSWEBER)
 * @copyright © ITSWEBER – www.itsweber.de
 */

(function () {
    'use strict';

    // ── Constants ─────────────────────────────────────────────────────────────
    var SECTION_LIMIT = 20;
    var TOAST_DURATION = 3000;
    var SORT_ANIMATION = 150;

    // ── CSRF Token ────────────────────────────────────────────────────────────

    /**
     * Read the CSRF token from a hidden input or meta tag.
     * @returns {string} The token value or empty string.
     */
    function getCsrfToken() {
        var input = document.getElementById('pb-csrf-token');
        if (input && input.value) {
            return input.value;
        }
        input = document.querySelector('input[name="_csrf_token"]');
        if (input && input.value) {
            return input.value;
        }
        var meta = document.querySelector('meta[name="csrf-token"]');
        if (meta) {
            return meta.getAttribute('content') || '';
        }
        return '';
    }

    // ── AJAX Helper ───────────────────────────────────────────────────────────

    /**
     * Perform an AJAX request.
     *
     * @param {string}   method   - HTTP method (GET, POST, etc.)
     * @param {string}   url      - Request URL
     * @param {*}        data     - Data to send (string, FormData, or plain object)
     * @param {function} callback - function(success, responseData)
     */
    function pbAjax(method, url, data, callback) {
        var xhr = new XMLHttpRequest();
        xhr.open(method, url, true);
        xhr.setRequestHeader('X-Requested-With', 'XMLHttpRequest');
        xhr.timeout = 30000;

        xhr.onload = function () {
            var parsed;
            try {
                parsed = JSON.parse(xhr.responseText);
            } catch (e) {
                parsed = { success: false, error: 'Ungültige Server-Antwort (HTTP ' + xhr.status + ')' };
            }
            if (typeof callback === 'function') {
                var ok = xhr.status >= 200 && xhr.status < 300 && parsed.success !== false;
                callback(ok, parsed);
            }
        };

        xhr.onerror = function () {
            if (typeof callback === 'function') {
                callback(false, { success: false, error: 'Netzwerkfehler – keine Verbindung zum Server.' });
            }
        };

        xhr.ontimeout = function () {
            if (typeof callback === 'function') {
                callback(false, { success: false, error: 'Zeitüberschreitung – der Server hat nicht geantwortet.' });
            }
        };

        if (method.toUpperCase() === 'GET' || !data) {
            xhr.send();
        } else if (data instanceof FormData) {
            xhr.send(data);
        } else if (typeof data === 'object') {
            xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
            var token = getCsrfToken();
            var parts = [];
            if (token) {
                parts.push('_csrf_token=' + encodeURIComponent(token));
            }
            var key;
            for (key in data) {
                if (data.hasOwnProperty(key)) {
                    parts.push(encodeURIComponent(key) + '=' + encodeURIComponent(data[key]));
                }
            }
            xhr.send(parts.join('&'));
        } else {
            // Assume pre-encoded string; ensure CSRF token is present
            var str = String(data);
            var tkn = getCsrfToken();
            if (tkn && str.indexOf('_csrf_token=') === -1) {
                str += (str ? '&' : '') + '_csrf_token=' + encodeURIComponent(tkn);
            }
            xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
            xhr.send(str);
        }
    }

    // ── Script Activation (innerHTML does not execute <script> tags) ─────────

    /**
     * After setting innerHTML, <script> tags are inert. This function clones
     * each <script> into a new element so the browser executes it.
     *
     * @param {HTMLElement} container - The element whose scripts should run
     */
    function activateScripts(container) {
        var scripts = container.querySelectorAll('script');
        for (var i = 0; i < scripts.length; i++) {
            var old = scripts[i];
            var neu = document.createElement('script');
            // Copy attributes (src, type, etc.)
            for (var j = 0; j < old.attributes.length; j++) {
                neu.setAttribute(old.attributes[j].name, old.attributes[j].value);
            }
            neu.textContent = old.textContent;
            old.parentNode.replaceChild(neu, old);
        }
    }

    /**
     * Wire up move-up / move-down / remove buttons inside a sortable container.
     * Called after AJAX form load for sections that use .item-move-up/down.
     *
     * @param {HTMLElement} container - The .pb-form-content or similar wrapper
     */
    function wireItemMoveButtons(container) {
        container.addEventListener('click', function (e) {
            var btn = e.target.closest('.item-move-up');
            if (btn) {
                e.preventDefault();
                var item = btn.closest('.section-item-sortable');
                if (item && item.previousElementSibling && item.previousElementSibling.classList.contains('section-item-sortable')) {
                    item.parentNode.insertBefore(item, item.previousElementSibling);
                }
                return;
            }
            btn = e.target.closest('.item-move-down');
            if (btn) {
                e.preventDefault();
                var item = btn.closest('.section-item-sortable');
                if (item && item.nextElementSibling && item.nextElementSibling.classList.contains('section-item-sortable')) {
                    item.parentNode.insertBefore(item.nextElementSibling, item);
                }
                return;
            }
        });
    }

    // ── Toast Notifications ───────────────────────────────────────────────────

    /**
     * Show a toast notification.
     *
     * @param {string} message - Text to display
     * @param {string} type    - 'success' or 'error'
     */
    function showToast(message, type) {
        type = type || 'success';

        // Ensure container exists
        var container = document.getElementById('pb-toast-container');
        if (!container) {
            container = document.createElement('div');
            container.id = 'pb-toast-container';
            container.style.cssText = 'position:fixed;top:20px;right:20px;z-index:100000;display:flex;flex-direction:column;gap:8px;pointer-events:none;';
            document.body.appendChild(container);
        }

        var toast = document.createElement('div');
        toast.className = 'pb-toast pb-toast--' + type;
        toast.style.cssText = 'pointer-events:auto;padding:12px 20px;border-radius:6px;color:#fff;font-size:14px;'
            + 'font-family:inherit;box-shadow:0 4px 12px rgba(0,0,0,.15);opacity:0;transform:translateX(40px);'
            + 'transition:opacity .3s ease,transform .3s ease;max-width:380px;word-wrap:break-word;';

        if (type === 'success') {
            toast.style.backgroundColor = '#16a34a';
        } else {
            toast.style.backgroundColor = '#dc2626';
        }

        // Build icon SVG
        var iconSvg = '';
        if (type === 'success') {
            iconSvg = '<svg width="16" height="16" viewBox="0 0 16 16" fill="none" style="vertical-align:middle;margin-right:8px;flex-shrink:0;">'
                + '<path d="M13.5 4.5L6.5 11.5L2.5 7.5" stroke="#fff" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>'
                + '</svg>';
        } else {
            iconSvg = '<svg width="16" height="16" viewBox="0 0 16 16" fill="none" style="vertical-align:middle;margin-right:8px;flex-shrink:0;">'
                + '<circle cx="8" cy="8" r="6.5" stroke="#fff" stroke-width="1.5"/>'
                + '<path d="M8 4.5V8.5M8 10.5V11" stroke="#fff" stroke-width="1.5" stroke-linecap="round"/>'
                + '</svg>';
        }

        toast.innerHTML = '<span style="display:inline-flex;align-items:center;">'
            + iconSvg
            + '<span>' + escapeHtml(message) + '</span>'
            + '</span>';

        container.appendChild(toast);

        // Animate in
        setTimeout(function () {
            toast.style.opacity = '1';
            toast.style.transform = 'translateX(0)';
        }, 10);

        // Auto-remove
        setTimeout(function () {
            toast.style.opacity = '0';
            toast.style.transform = 'translateX(40px)';
            setTimeout(function () {
                if (toast.parentNode) {
                    toast.parentNode.removeChild(toast);
                }
            }, 300);
        }, TOAST_DURATION);
    }

    /**
     * Escape HTML entities.
     * @param {string} str
     * @returns {string}
     */
    function escapeHtml(str) {
        var div = document.createElement('div');
        div.appendChild(document.createTextNode(str || ''));
        return div.innerHTML;
    }

    // ── Section Limit ─────────────────────────────────────────────────────────

    /**
     * Check section count and disable/enable the add button accordingly.
     */
    function checkSectionLimit() {
        var count = document.querySelectorAll('.pb-section-card').length;
        var addBtn = document.querySelector('.pb-add-section-btn');
        if (!addBtn) return;

        if (count >= SECTION_LIMIT) {
            addBtn.disabled = true;
            addBtn.title = 'Maximum von ' + SECTION_LIMIT + ' Sections erreicht';
        } else {
            addBtn.disabled = false;
            addBtn.title = '';
        }
    }

    // ── SortableJS Initialization ─────────────────────────────────────────────

    /**
     * Initialise SortableJS on the sections list container.
     */
    function initSortable() {
        var container = document.querySelector('.pb-sections-list');
        if (!container || typeof Sortable === 'undefined') return;

        Sortable.create(container, {
            handle: '.pb-section-handle',
            animation: SORT_ANIMATION,
            ghostClass: 'sortable-ghost',
            chosenClass: 'sortable-chosen',
            dragClass: 'sortable-drag',
            onEnd: function () {
                var cards = container.querySelectorAll('.pb-section-card');
                var order = [];
                var i;
                for (i = 0; i < cards.length; i++) {
                    order.push(cards[i].getAttribute('data-section-id'));
                }
                pbAjax('POST', '/admin/pagebuilder/reorder', { order: JSON.stringify(order) }, function (ok, resp) {
                    if (ok) {
                        showToast('Reihenfolge gespeichert', 'success');
                    } else {
                        showToast(resp.error || 'Reihenfolge konnte nicht gespeichert werden', 'error');
                    }
                });
            }
        });
    }

    // ── Section Card Interactions ─────────────────────────────────────────────

    /**
     * Expand or collapse a section card.
     *
     * @param {HTMLElement} card - The .pb-section-card element
     */
    function toggleExpand(card) {
        var body = card.querySelector('.pb-section-body');
        var btn = card.querySelector('.pb-btn-expand');
        if (!body) return;

        var isOpen = card.classList.contains('pb-section-card--expanded');

        if (isOpen) {
            // Collapse
            card.classList.remove('pb-section-card--expanded');
            if (btn) {
                btn.classList.remove('is-expanded');
                btn.setAttribute('aria-expanded', 'false');
            }
        } else {
            // Expand
            card.classList.add('pb-section-card--expanded');
            if (btn) {
                btn.classList.add('is-expanded');
                btn.setAttribute('aria-expanded', 'true');
            }

            // Lazy-load form content on first expand
            var formContent = card.querySelector('.pb-form-content');
            if (formContent && formContent.getAttribute('data-loaded') !== '1') {
                var sectionId = card.getAttribute('data-section-id');
                formContent.innerHTML = '<p style="text-align:center;padding:1rem;color:#6b7280;">Formular wird geladen...</p>';
                pbAjax('GET', '/admin/pagebuilder/section/' + sectionId + '/form', null, function (ok, resp) {
                    if (ok && resp.html) {
                        formContent.innerHTML = resp.html;
                        formContent.setAttribute('data-loaded', '1');
                        // Execute inline <script> tags (innerHTML does not run them)
                        activateScripts(formContent);
                        // Wire move-up/move-down buttons via event delegation
                        wireItemMoveButtons(formContent);
                        // Wire media picker buttons for dynamically loaded content
                        if (window.iwWireMpfButtons) {
                            window.iwWireMpfButtons(formContent);
                        }
                        // Wire section media fields (admin_media_picker helper)
                        if (window.initSectionMediaFields) {
                            window.initSectionMediaFields(formContent);
                        }
                    } else {
                        formContent.innerHTML = '<p style="text-align:center;padding:1rem;color:#dc2626;">'
                            + escapeHtml(resp.error || resp.message || 'Formular konnte nicht geladen werden.')
                            + '</p>';
                    }
                });
            }
        }
    }

    /**
     * Toggle section active/inactive state.
     *
     * @param {HTMLElement} card - The .pb-section-card element
     */
    function toggleActive(card) {
        if (card.getAttribute('data-toggling')) return;
        card.setAttribute('data-toggling', '1');

        var sectionId = card.getAttribute('data-section-id');
        var btn = card.querySelector('.pb-btn-toggle');
        var dot = card.querySelector('.pb-status-dot');

        pbAjax('POST', '/admin/pagebuilder/section/' + sectionId + '/toggle', {}, function (ok, resp) {
            card.removeAttribute('data-toggling');
            if (ok) {
                var isActive = resp.isActive || resp.is_active || resp.active;
                if (dot) {
                    if (isActive) {
                        dot.classList.remove('pb-status-dot--inactive');
                        dot.classList.add('pb-status-dot--active');
                        dot.title = 'Aktiv';
                    } else {
                        dot.classList.remove('pb-status-dot--active');
                        dot.classList.add('pb-status-dot--inactive');
                        dot.title = 'Inaktiv';
                    }
                }
                // Swap eye icon SVG
                if (btn) {
                    var eyeOpen = '<svg viewBox="0 0 24 24" width="16" height="16" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">'
                        + '<path d="M1 12s4-7 11-7 11 7 11 7-4 7-11 7S1 12 1 12z"/>'
                        + '<circle cx="12" cy="12" r="3"/></svg>';
                    var eyeClosed = '<svg viewBox="0 0 24 24" width="16" height="16" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">'
                        + '<path d="M17.94 17.94A10.07 10.07 0 0112 20c-7 0-11-8-11-8a18.45 18.45 0 015.06-5.94"/>'
                        + '<path d="M9.9 4.24A9.12 9.12 0 0112 4c7 0 11 8 11 8a18.5 18.5 0 01-2.16 3.19"/>'
                        + '<line x1="1" y1="1" x2="23" y2="23"/></svg>';
                    btn.innerHTML = isActive ? eyeOpen : eyeClosed;
                    btn.title = isActive ? 'Deaktivieren' : 'Aktivieren';
                }
                // Update card styling
                if (isActive) {
                    card.classList.remove('pb-section-card--inactive');
                } else {
                    card.classList.add('pb-section-card--inactive');
                }
                showToast(isActive ? 'Section aktiviert' : 'Section deaktiviert', 'success');
            } else {
                showToast(resp.error || 'Status konnte nicht geändert werden', 'error');
            }
        });
    }

    /**
     * Duplicate a section.
     *
     * @param {HTMLElement} card - The .pb-section-card element
     */
    function duplicateSection(card) {
        var sectionId = card.getAttribute('data-section-id');

        // Prevent duplicate if at limit
        if (document.querySelectorAll('.pb-section-card').length >= SECTION_LIMIT) {
            showToast('Maximum von ' + SECTION_LIMIT + ' Sections erreicht', 'error');
            return;
        }

        pbAjax('POST', '/admin/pagebuilder/section/' + sectionId + '/duplicate', {}, function (ok, resp) {
            if (ok && resp.html) {
                // Insert the new card HTML after the current card
                var temp = document.createElement('div');
                temp.innerHTML = resp.html;
                var newCard = temp.firstElementChild;
                if (newCard) {
                    card.parentNode.insertBefore(newCard, card.nextSibling);
                    checkSectionLimit();
                    showToast('Section dupliziert', 'success');

                    // Scroll to new card
                    setTimeout(function () {
                        newCard.scrollIntoView({ behavior: 'smooth', block: 'nearest' });
                    }, 100);
                }
            } else {
                showToast(resp.error || 'Section konnte nicht dupliziert werden', 'error');
            }
        });
    }

    /**
     * Delete a section after confirmation.
     *
     * @param {HTMLElement} card - The .pb-section-card element
     */
    function deleteSection(card) {
        var sectionId = card.getAttribute('data-section-id');
        var sectionTitle = card.querySelector('.pb-section-title');
        var titleText = sectionTitle ? sectionTitle.textContent.trim() : 'Section #' + sectionId;

        if (!confirm('Section "' + titleText + '" wirklich löschen?\n\nDiese Aktion kann nicht rückgängig gemacht werden.')) {
            return;
        }

        pbAjax('POST', pbConfig.urls.sectionBase + '/' + sectionId + '/delete', {}, function (ok, resp) {
            if (ok) {
                // Animate out and remove
                card.style.transition = 'opacity .3s ease, transform .3s ease';
                card.style.opacity = '0';
                card.style.transform = 'translateX(-20px)';
                setTimeout(function () {
                    if (card.parentNode) {
                        card.parentNode.removeChild(card);
                    }
                    checkSectionLimit();

                    // Show empty state if no sections left
                    var remaining = document.querySelectorAll('.pb-section-card');
                    if (remaining.length === 0) {
                        var container = document.getElementById('pb-sections-container');
                        var list = document.getElementById('pb-sections-list');
                        if (list) list.remove();
                        if (container) {
                            container.innerHTML = '<div class="pb-empty-state" id="pb-empty-state">'
                                + '<h3 class="pb-empty-state-title">Noch keine Sections</h3>'
                                + '<p class="pb-empty-state-text">Fügen Sie Ihre erste Section hinzu, um die Seite zu gestalten.</p>'
                                + '</div>';
                        }
                    }

                    showToast(resp.message || 'Section gelöscht', 'success');
                }, 300);
            } else {
                showToast(resp.message || resp.error || 'Section konnte nicht gelöscht werden', 'error');
            }
        });
    }

    // ── Section Form Saving ───────────────────────────────────────────────────

    /**
     * Handle section form submission via AJAX.
     *
     * @param {HTMLFormElement} form - The .pb-section-form element
     */
    function saveSection(form) {
        var card = form.closest('.pb-section-card');
        if (!card) return;
        if (form.getAttribute('data-saving')) return;
        form.setAttribute('data-saving', '1');

        var sectionId = card.getAttribute('data-section-id');
        var formData = new FormData(form);

        // Ensure CSRF token is present in form data
        var token = getCsrfToken();
        if (token && !formData.get('_csrf_token')) {
            formData.append('_csrf_token', token);
        }

        // Disable submit button during save
        var submitBtn = form.querySelector('[type="submit"]');
        if (submitBtn) {
            submitBtn.disabled = true;
            submitBtn.setAttribute('data-original-text', submitBtn.textContent);
            submitBtn.textContent = 'Speichert...';
        }

        var xhr = new XMLHttpRequest();
        xhr.open('POST', '/admin/pagebuilder/section/' + sectionId + '/save', true);
        xhr.setRequestHeader('X-Requested-With', 'XMLHttpRequest');
        xhr.timeout = 30000;

        xhr.onload = function () {
            var resp;
            try {
                resp = JSON.parse(xhr.responseText);
            } catch (e) {
                resp = { success: false, error: 'Ungültige Server-Antwort' };
            }

            // Re-enable submit button and unlock form
            form.removeAttribute('data-saving');
            if (submitBtn) {
                submitBtn.disabled = false;
                submitBtn.textContent = submitBtn.getAttribute('data-original-text') || 'Speichern';
            }

            if (xhr.status >= 200 && xhr.status < 300 && resp.success !== false) {
                showToast(resp.message || 'Section gespeichert', 'success');

                // Update card title if returned
                if (resp.title) {
                    var titleEl = card.querySelector('.pb-section-title');
                    if (titleEl) {
                        titleEl.textContent = resp.title;
                    }
                }
            } else {
                showToast(resp.error || 'Speichern fehlgeschlagen', 'error');
            }
        };

        xhr.onerror = function () {
            form.removeAttribute('data-saving');
            if (submitBtn) {
                submitBtn.disabled = false;
                submitBtn.textContent = submitBtn.getAttribute('data-original-text') || 'Speichern';
            }
            showToast('Netzwerkfehler – bitte erneut versuchen', 'error');
        };

        xhr.ontimeout = function () {
            form.removeAttribute('data-saving');
            if (submitBtn) {
                submitBtn.disabled = false;
                submitBtn.textContent = submitBtn.getAttribute('data-original-text') || 'Speichern';
            }
            showToast('Zeitüberschreitung – bitte erneut versuchen', 'error');
        };

        xhr.send(formData);
    }

    // ── Add Section Modal ─────────────────────────────────────────────────────

    /**
     * Open the add-section modal.
     */
    function openAddModal() {
        var modal = document.getElementById('pb-add-section-modal');
        if (!modal) return;

        modal.style.display = 'flex';
        // Trigger reflow for transition
        modal.offsetHeight; // jshint ignore:line
        modal.classList.add('is-open');

        // Focus first tab or first type card
        var firstTab = modal.querySelector('.pb-modal-tab.is-active');
        if (firstTab) firstTab.focus();

        // Trap focus / close on escape
        document.addEventListener('keydown', modalKeyHandler);
    }

    /**
     * Close the add-section modal.
     */
    function closeAddModal() {
        var modal = document.getElementById('pb-add-section-modal');
        if (!modal) return;

        modal.classList.remove('is-open');
        setTimeout(function () {
            modal.style.display = 'none';
        }, 300);

        document.removeEventListener('keydown', modalKeyHandler);
    }

    /**
     * Handle keyboard events while modal is open.
     *
     * @param {KeyboardEvent} e
     */
    function modalKeyHandler(e) {
        if (e.key === 'Escape') {
            closeAddModal();
        }
    }

    /**
     * Switch modal category tabs.
     *
     * @param {HTMLElement} tab - The clicked .pb-modal-tab element
     */
    function switchModalTab(tab) {
        var modal = document.getElementById('pb-add-section-modal');
        if (!modal) return;

        var category = tab.getAttribute('data-tab') || tab.getAttribute('data-category') || '';

        // Update active tab
        var tabs = modal.querySelectorAll('.pb-modal-tab');
        var i;
        for (i = 0; i < tabs.length; i++) {
            tabs[i].classList.remove('active');
            tabs[i].classList.remove('is-active');
        }
        tab.classList.add('active');

        // Show/hide panels by category
        var panels = modal.querySelectorAll('.pb-modal-grid-panel');
        for (i = 0; i < panels.length; i++) {
            var panelCat = panels[i].getAttribute('data-panel');
            if (panelCat === category) {
                panels[i].style.display = '';
            } else {
                panels[i].style.display = 'none';
            }
        }
    }

    /**
     * Add a new section of the given type.
     *
     * @param {HTMLElement} typeCard - The clicked .pb-type-card element
     */
    function addSection(typeCard) {
        // Don't add if disabled
        if (typeCard.classList.contains('is-disabled') || typeCard.hasAttribute('disabled')) {
            return;
        }

        // Prevent exceeding limit
        if (document.querySelectorAll('.pb-section-card').length >= SECTION_LIMIT) {
            showToast('Maximum von ' + SECTION_LIMIT + ' Sections erreicht', 'error');
            return;
        }

        var typeKey = typeCard.getAttribute('data-type');
        if (!typeKey) return;

        // Determine page context (default: homepage)
        var container = document.querySelector('.pb-sections-list');
        var pageId = container ? (container.getAttribute('data-page-id') || '') : '';

        pbAjax('POST', pbConfig.urls.addSection, {
            type_key: typeKey,
            context: pbConfig.context || 'homepage'
        }, function (ok, resp) {
            if (ok && resp.html) {
                closeAddModal();

                // Append new section card to container
                var list = document.querySelector('.pb-sections-list');
                if (!list) {
                    // First section: replace empty state with sortable list
                    var emptyState = document.getElementById('pb-empty-state');
                    var container = document.getElementById('pb-sections-container');
                    if (emptyState) emptyState.remove();
                    list = document.createElement('div');
                    list.className = 'pb-sections-list';
                    list.id = 'pb-sections-list';
                    list.setAttribute('data-reorder-url', pbConfig.urls.reorder || '/admin/pagebuilder/reorder');
                    list.setAttribute('data-csrf-token', getCsrfToken());
                    if (container) container.appendChild(list);
                    initSortable();
                }
                if (list) {
                    var temp = document.createElement('div');
                    temp.innerHTML = resp.html;
                    var newCard = temp.firstElementChild;
                    if (newCard) {
                        list.appendChild(newCard);
                        checkSectionLimit();

                        // Scroll to new card and auto-expand
                        setTimeout(function () {
                            newCard.scrollIntoView({ behavior: 'smooth', block: 'nearest' });
                            setTimeout(function () {
                                toggleExpand(newCard);
                            }, 400);
                        }, 100);

                        showToast('Section hinzugefügt', 'success');
                    }
                }
            } else {
                showToast(resp.error || 'Section konnte nicht hinzugefügt werden', 'error');
            }
        });
    }

    // ── Live Preview ─────────────────────────────────────────────────────────

    /**
     * Show a live preview of a section in a modal iframe.
     *
     * @param {HTMLElement} card - The .pb-section-card element
     */
    function previewSection(card) {
        var sectionId = card.getAttribute('data-section-id');
        var form = card.querySelector('.pb-section-form');
        var formContent = card.querySelector('.pb-form-content');
        var formLoaded = formContent && formContent.getAttribute('data-loaded') === '1';

        // Build form data from current (possibly unsaved) form state
        var formData;
        if (form && formLoaded) {
            formData = new FormData(form);
        } else {
            // Form not loaded — send minimal data so server uses saved content
            formData = new FormData();
            formData.append('_use_saved', '1');
        }

        var token = getCsrfToken();
        if (!formData.get || !formData.get('_csrf_token')) {
            formData.append('_csrf_token', token);
        }

        // Show preview modal
        showPreviewModal();

        var xhr = new XMLHttpRequest();
        xhr.open('POST', '/admin/pagebuilder/section/' + sectionId + '/preview', true);
        xhr.setRequestHeader('X-Requested-With', 'XMLHttpRequest');
        xhr.timeout = 30000;

        xhr.onload = function () {
            var resp;
            try { resp = JSON.parse(xhr.responseText); } catch (e) { resp = { success: false, error: 'JSON parse error: ' + xhr.responseText.substring(0, 200) }; }

            if (resp.success && resp.html) {
                var iframe = document.getElementById('pb-preview-iframe');
                if (iframe) {
                    // Use srcdoc to avoid document.write() CSP violations
                    iframe.srcdoc = resp.html;
                }
                // Hide loading text
                var loading = document.querySelector('.pb-preview-loading');
                if (loading) loading.style.display = 'none';
            } else {
                closePreviewModal();
                showToast(resp.error || resp.message || 'Vorschau konnte nicht geladen werden', 'error');
            }
        };

        xhr.onerror = function () {
            closePreviewModal();
            showToast('Netzwerkfehler', 'error');
        };

        xhr.send(formData);
    }

    /**
     * Show the preview modal with an iframe.
     */
    function showPreviewModal() {
        var modal = document.getElementById('pb-preview-modal');
        if (!modal) {
            modal = document.createElement('div');
            modal.id = 'pb-preview-modal';
            modal.className = 'pb-modal-overlay';
            modal.innerHTML = '<div class="pb-modal pb-preview-modal-inner">'
                + '<div class="pb-modal-header">'
                + '<h3>'
                + '<svg viewBox="0 0 24 24" width="20" height="20" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" style="vertical-align:-3px;margin-right:6px;">'
                + '<polygon points="5 3 19 12 5 21 5 3"/></svg>'
                + (window.pbConfig && window.pbConfig.translations && window.pbConfig.translations.previewTitle || 'Vorschau')
                + '</h3>'
                + '<div class="pb-preview-size-btns">'
                + '<button type="button" class="pb-preview-size-btn active" data-width="100%"  title="Desktop">'
                + '<svg viewBox="0 0 24 24" width="16" height="16" fill="none" stroke="currentColor" stroke-width="2"><rect x="2" y="3" width="20" height="14" rx="2"/><line x1="8" y1="21" x2="16" y2="21"/><line x1="12" y1="17" x2="12" y2="21"/></svg>'
                + '</button>'
                + '<button type="button" class="pb-preview-size-btn" data-width="768px" title="Tablet">'
                + '<svg viewBox="0 0 24 24" width="16" height="16" fill="none" stroke="currentColor" stroke-width="2"><rect x="4" y="2" width="16" height="20" rx="2"/><line x1="12" y1="18" x2="12.01" y2="18"/></svg>'
                + '</button>'
                + '<button type="button" class="pb-preview-size-btn" data-width="375px" title="Mobile">'
                + '<svg viewBox="0 0 24 24" width="16" height="16" fill="none" stroke="currentColor" stroke-width="2"><rect x="5" y="2" width="14" height="20" rx="2"/><line x1="12" y1="18" x2="12.01" y2="18"/></svg>'
                + '</button>'
                + '</div>'
                + '<button type="button" class="pb-modal-close pb-preview-close">'
                + '<svg viewBox="0 0 24 24" width="20" height="20" fill="none" stroke="currentColor" stroke-width="2"><line x1="18" y1="6" x2="6" y2="18"/><line x1="6" y1="6" x2="18" y2="18"/></svg>'
                + '</button>'
                + '</div>'
                + '<div class="pb-modal-body pb-preview-body">'
                + '<div class="pb-preview-frame-wrapper">'
                + '<iframe id="pb-preview-iframe" class="pb-preview-iframe"></iframe>'
                + '</div>'
                + '<p class="pb-preview-loading" style="text-align:center;padding:2rem;color:#6b7280;">Vorschau wird geladen...</p>'
                + '</div>'
                + '</div>';
            document.body.appendChild(modal);

            // Size toggle clicks
            modal.addEventListener('click', function (e) {
                var sizeBtn = findParent(e.target, 'pb-preview-size-btn');
                if (sizeBtn) {
                    var btns = modal.querySelectorAll('.pb-preview-size-btn');
                    for (var i = 0; i < btns.length; i++) btns[i].classList.remove('active');
                    sizeBtn.classList.add('active');
                    var wrapper = modal.querySelector('.pb-preview-frame-wrapper');
                    if (wrapper) wrapper.style.maxWidth = sizeBtn.getAttribute('data-width');
                    return;
                }
                var close = findParent(e.target, 'pb-preview-close');
                if (close) { closePreviewModal(); return; }
                if (e.target === modal) closePreviewModal();
            });
        }

        modal.style.display = 'flex';
        setTimeout(function () { modal.classList.add('is-open'); }, 10);

        // Reset iframe
        var iframe = document.getElementById('pb-preview-iframe');
        if (iframe) {
            iframe.srcdoc = '';
        }
        var loading = modal.querySelector('.pb-preview-loading');
        if (loading) loading.style.display = '';

        document.addEventListener('keydown', previewKeyHandler);
    }

    function closePreviewModal() {
        var modal = document.getElementById('pb-preview-modal');
        if (!modal) return;
        modal.classList.remove('is-open');
        setTimeout(function () { modal.style.display = 'none'; }, 300);
        document.removeEventListener('keydown', previewKeyHandler);
    }

    function previewKeyHandler(e) {
        if (e.key === 'Escape') closePreviewModal();
    }

    // ── Section Export ────────────────────────────────────────────────────────

    /**
     * Export a section as JSON file download.
     *
     * @param {HTMLElement} card - The .pb-section-card element
     */
    function exportSection(card) {
        var sectionId = card.getAttribute('data-section-id');

        pbAjax('GET', '/admin/pagebuilder/section/' + sectionId + '/export', null, function (ok, resp) {
            if (ok && resp.export) {
                // Trigger JSON download
                var jsonStr = JSON.stringify(resp.export, null, 2);
                var blob = new Blob([jsonStr], { type: 'application/json' });
                var url = URL.createObjectURL(blob);
                var a = document.createElement('a');
                a.href = url;
                a.download = resp.filename || 'section_export.json';
                document.body.appendChild(a);
                a.click();
                setTimeout(function () {
                    document.body.removeChild(a);
                    URL.revokeObjectURL(url);
                }, 100);
                showToast('Section exportiert', 'success');
            } else {
                showToast(resp.error || 'Export fehlgeschlagen', 'error');
            }
        });
    }

    // ── Section Import ────────────────────────────────────────────────────────

    /**
     * Handle section import from JSON file.
     */
    function initImport() {
        var importBtn = document.getElementById('pb-import-btn');
        var importFile = document.getElementById('pb-import-file');
        if (!importBtn || !importFile) return;

        importBtn.addEventListener('click', function (e) {
            e.preventDefault();
            e.stopPropagation();
            importFile.click();
        });

        importFile.addEventListener('change', function () {
            var file = importFile.files[0];
            if (!file) return;

            var reader = new FileReader();
            reader.onload = function (e) {
                var jsonStr = e.target.result;

                // Validate JSON
                try {
                    var parsed = JSON.parse(jsonStr);
                    if (parsed.iw_export !== 'section') {
                        showToast('Ungültige Import-Datei', 'error');
                        importFile.value = '';
                        return;
                    }
                } catch (err) {
                    showToast('Ungültiges JSON-Format', 'error');
                    importFile.value = '';
                    return;
                }

                pbAjax('POST', pbConfig.urls.importSection || (pbConfig.urls.addSection.replace('/section/add', '/section/import')), {
                    import_data: jsonStr,
                    context: pbConfig.context || 'homepage'
                }, function (ok, resp) {
                    if (ok && resp.html) {
                        closeAddModal();

                        var list = document.querySelector('.pb-sections-list');
                        if (list) {
                            var temp = document.createElement('div');
                            temp.innerHTML = resp.html;
                            var newCard = temp.firstElementChild;
                            if (newCard) {
                                list.appendChild(newCard);
                                checkSectionLimit();
                                setTimeout(function () {
                                    newCard.scrollIntoView({ behavior: 'smooth', block: 'nearest' });
                                }, 100);
                            }
                        }
                        showToast(resp.message || 'Section importiert', 'success');
                    } else {
                        showToast(resp.error || resp.message || 'Import fehlgeschlagen', 'error');
                    }
                    importFile.value = '';
                });
            };
            reader.readAsText(file);
        });
    }

    // ── Content Versioning ────────────────────────────────────────────────────

    /**
     * Show revisions modal for a section.
     *
     * @param {HTMLElement} card - The .pb-section-card element
     */
    function showRevisions(card) {
        var sectionId = card.getAttribute('data-section-id');
        var sectionTitle = card.querySelector('.pb-section-type-label');
        var title = sectionTitle ? sectionTitle.textContent.trim() : 'Section #' + sectionId;

        pbAjax('GET', '/admin/pagebuilder/section/' + sectionId + '/revisions', null, function (ok, resp) {
            if (!ok) {
                showToast(resp.error || 'Versionen konnten nicht geladen werden', 'error');
                return;
            }

            showRevisionsModal(sectionId, title, resp.revisions || []);
        });
    }

    /**
     * Render and display the revisions modal.
     */
    function showRevisionsModal(sectionId, sectionTitle, revisions) {
        // Remove existing modal if present
        var existing = document.getElementById('pb-revisions-modal');
        if (existing) existing.parentNode.removeChild(existing);

        var modal = document.createElement('div');
        modal.id = 'pb-revisions-modal';
        modal.className = 'pb-modal-overlay';

        var bodyHtml = '';
        if (revisions.length === 0) {
            bodyHtml = '<p class="pb-revisions-empty">'
                + (window.pbConfig && window.pbConfig.translations && window.pbConfig.translations.noRevisions || 'Noch keine Versionen vorhanden.')
                + '</p>';
        } else {
            bodyHtml = '<div class="pb-revisions-list">';
            for (var i = 0; i < revisions.length; i++) {
                var rev = revisions[i];
                bodyHtml += '<div class="pb-revision-item" data-revision-id="' + rev.id + '">'
                    + '<div class="pb-revision-info">'
                    + '<span class="pb-revision-date">' + escapeHtml(rev.created_at) + '</span>'
                    + (rev.changed_by ? '<span class="pb-revision-user">' + escapeHtml(rev.changed_by) + '</span>' : '')
                    + (rev.summary ? '<span class="pb-revision-summary">' + escapeHtml(rev.summary) + '</span>' : '')
                    + '</div>'
                    + '<button type="button" class="btn btn-secondary btn-sm pb-revision-restore" data-section-id="' + sectionId + '" data-revision-id="' + rev.id + '">'
                    + '<svg viewBox="0 0 24 24" width="14" height="14" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" style="vertical-align:-2px;margin-right:4px;">'
                    + '<polyline points="1 4 1 10 7 10"/><path d="M3.51 15a9 9 0 102.13-9.36L1 10"/></svg>'
                    + (window.pbConfig && window.pbConfig.translations && window.pbConfig.translations.restore || 'Wiederherstellen')
                    + '</button>'
                    + '</div>';
            }
            bodyHtml += '</div>';
        }

        modal.innerHTML = '<div class="pb-modal pb-revisions-modal-inner">'
            + '<div class="pb-modal-header">'
            + '<h3>'
            + '<svg viewBox="0 0 24 24" width="20" height="20" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" style="vertical-align:-3px;margin-right:6px;">'
            + '<polyline points="1 4 1 10 7 10"/><path d="M3.51 15a9 9 0 102.13-9.36L1 10"/></svg>'
            + (window.pbConfig && window.pbConfig.translations && window.pbConfig.translations.revisionsTitle || 'Versionen')
            + ' – ' + escapeHtml(sectionTitle)
            + '</h3>'
            + '<button type="button" class="pb-modal-close pb-revisions-close">'
            + '<svg viewBox="0 0 24 24" width="20" height="20" fill="none" stroke="currentColor" stroke-width="2"><line x1="18" y1="6" x2="6" y2="18"/><line x1="6" y1="6" x2="18" y2="18"/></svg>'
            + '</button>'
            + '</div>'
            + '<div class="pb-modal-body">'
            + bodyHtml
            + '</div>'
            + '</div>';

        document.body.appendChild(modal);
        modal.style.display = 'flex';
        setTimeout(function () { modal.classList.add('is-open'); }, 10);

        // Event handlers
        modal.addEventListener('click', function (e) {
            var restoreBtn = findParent(e.target, 'pb-revision-restore');
            if (restoreBtn) {
                e.preventDefault();
                var sid = restoreBtn.getAttribute('data-section-id');
                var rid = restoreBtn.getAttribute('data-revision-id');
                restoreRevision(sid, rid);
                return;
            }
            var close = findParent(e.target, 'pb-revisions-close');
            if (close) { closeRevisionsModal(); return; }
            if (e.target === modal) closeRevisionsModal();
        });

        document.addEventListener('keydown', revisionsKeyHandler);
    }

    function closeRevisionsModal() {
        var modal = document.getElementById('pb-revisions-modal');
        if (!modal) return;
        modal.classList.remove('is-open');
        setTimeout(function () {
            if (modal.parentNode) modal.parentNode.removeChild(modal);
        }, 300);
        document.removeEventListener('keydown', revisionsKeyHandler);
    }

    function revisionsKeyHandler(e) {
        if (e.key === 'Escape') closeRevisionsModal();
    }

    /**
     * Restore a specific revision.
     */
    function restoreRevision(sectionId, revisionId) {
        if (!confirm(window.pbConfig && window.pbConfig.translations && window.pbConfig.translations.confirmRestore || 'Version wiederherstellen? Der aktuelle Inhalt wird als neue Version gespeichert.')) {
            return;
        }

        pbAjax('POST', '/admin/pagebuilder/section/' + sectionId + '/restore/' + revisionId, {}, function (ok, resp) {
            if (ok) {
                closeRevisionsModal();
                showToast(resp.message || 'Version wiederhergestellt', 'success');

                // Reload the section form if it was expanded
                var card = document.querySelector('.pb-section-card[data-section-id="' + sectionId + '"]');
                if (card) {
                    var formContent = card.querySelector('.pb-form-content');
                    if (formContent) {
                        formContent.setAttribute('data-loaded', '0');
                        // Re-expand to reload form
                        if (card.classList.contains('pb-section-card--expanded')) {
                            card.classList.remove('pb-section-card--expanded');
                            setTimeout(function () { toggleExpand(card); }, 100);
                        }
                    }
                }
            } else {
                showToast(resp.error || 'Wiederherstellung fehlgeschlagen', 'error');
            }
        });
    }

    // ── Event Delegation ──────────────────────────────────────────────────────

    /**
     * Walk up the DOM from a target element to find an ancestor matching
     * one of the given class names. Stops at stopEl (exclusive).
     * Works reliably across SVG/HTML boundaries.
     */
    function findAncestor(target, classNames, stopEl) {
        var el = target;
        while (el && el !== stopEl && el !== document) {
            // SVG elements may not have classList; skip them
            if (el.classList) {
                for (var i = 0; i < classNames.length; i++) {
                    if (el.classList.contains(classNames[i])) {
                        return { el: el, cls: classNames[i] };
                    }
                }
            }
            el = el.parentNode || el.parentElement;
        }
        return null;
    }

    /**
     * Walk up the DOM to find an ancestor with the given class.
     */
    function findParent(el, className) {
        var node = el;
        while (node && node !== document) {
            if (node.classList && node.classList.contains(className)) {
                return node;
            }
            node = node.parentNode || node.parentElement;
        }
        return null;
    }

    /**
     * Initialise event delegation for all page builder interactions.
     * Uses document-level delegation for maximum reliability.
     */
    function initEventDelegation() {

        // ── Document-level click handler for ALL page builder interactions ──
        document.addEventListener('click', function (e) {
            var target = e.target;
            // ── 1. Section card action buttons ─────────────────────────────
            var match = findAncestor(target, [
                'pb-btn-expand', 'pb-btn-toggle', 'pb-btn-duplicate', 'pb-btn-delete', 'pb-btn-collapse',
                'pb-btn-preview', 'pb-btn-export', 'pb-btn-revisions'
            ], document);

            if (match) {
                var card = findParent(match.el, 'pb-section-card');
                if (!card) return;

                e.preventDefault();
                e.stopPropagation();

                switch (match.cls) {
                    case 'pb-btn-expand':
                        toggleExpand(card);
                        break;
                    case 'pb-btn-toggle':
                        toggleActive(card);
                        break;
                    case 'pb-btn-duplicate':
                        duplicateSection(card);
                        break;
                    case 'pb-btn-delete':
                        deleteSection(card);
                        break;
                    case 'pb-btn-collapse':
                        toggleExpand(card);
                        break;
                    case 'pb-btn-preview':
                        previewSection(card);
                        break;
                    case 'pb-btn-export':
                        exportSection(card);
                        break;
                    case 'pb-btn-revisions':
                        showRevisions(card);
                        break;
                }
                return;
            }

            // ── 2. Add section button (any button with pb-add-section-btn) ──
            var addBtn = findParent(target, 'pb-add-section-btn');
            if (addBtn) {
                e.preventDefault();
                if (addBtn.disabled) return;
                openAddModal();
                return;
            }

            // ── 3. Modal interactions ───────────────────────────────────────
            var modal = document.getElementById('pb-add-section-modal');
            if (!modal) return;

            // Only handle clicks inside the modal overlay
            if (!modal.contains(target) && target !== modal) return;

            // Close button
            var closeBtn = findParent(target, 'pb-modal-close');
            if (closeBtn) {
                e.preventDefault();
                closeAddModal();
                return;
            }

            // Tab switching
            var tab = findParent(target, 'pb-modal-tab');
            if (tab) {
                e.preventDefault();
                switchModalTab(tab);
                return;
            }

            // Type card selection
            var typeCard = findParent(target, 'pb-type-card');
            if (typeCard) {
                e.preventDefault();
                addSection(typeCard);
                return;
            }

            // Click on overlay backdrop closes modal
            if (target === modal) {
                closeAddModal();
            }
        });

        // ── Form submit delegation (document level) ────────────────────────
        document.addEventListener('submit', function (e) {
            var form = e.target;
            if (!form.classList || !form.classList.contains('pb-section-form')) return;

            e.preventDefault();
            saveSection(form);
        });
    }

    // ── Utility: Serialize form data as URL-encoded string ────────────────────

    /**
     * Serialize a form into a URL-encoded string.
     * Handles select, textarea, input (text, hidden, checkbox, radio).
     *
     * @param {HTMLFormElement} form
     * @returns {string}
     */
    function serializeForm(form) {
        var parts = [];
        var elements = form.elements;
        var i, el;

        for (i = 0; i < elements.length; i++) {
            el = elements[i];
            if (!el.name || el.disabled) continue;
            if (el.type === 'checkbox' || el.type === 'radio') {
                if (!el.checked) continue;
            }
            if (el.type === 'file') continue;
            parts.push(encodeURIComponent(el.name) + '=' + encodeURIComponent(el.value));
        }

        return parts.join('&');
    }

    // ── Initialization ────────────────────────────────────────────────────────

    document.addEventListener('DOMContentLoaded', function () {
        initSortable();
        initEventDelegation();
        initImport();
        checkSectionLimit();
    });

    // ── Public API (attach to window for external use if needed) ──────────────
    window.PageBuilder = {
        showToast: showToast,
        checkSectionLimit: checkSectionLimit,
        openAddModal: openAddModal,
        closeAddModal: closeAddModal,
        getCsrfToken: getCsrfToken,
        pbAjax: pbAjax
    };

})();
