// === GLOBAL EEL CALL SAFETY WRAPPER ===
// Wraps all Eel calls with timeout to prevent UI freeze on disconnect
const MAX_EEL_TIMEOUT = 45000; // 45 seconds max per call (allows preview render + gallery loading)
let eelCallsInProgress = 0;

// Monkey-patch eel to add safety timeouts to all calls
if (window.eel && window.eel._js_call !== undefined) {
    const originalJsCall = window.eel._js_call.bind(window.eel);
    
    window.eel._js_call = function(name, ...args) {
        eelCallsInProgress++;
        const callId = eelCallsInProgress;
        
        const actualPromise = originalJsCall(name, ...args);
        
        const timeoutPromise = new Promise((_, reject) => {
            const timer = setTimeout(() => {
                const error = new Error(`[TIMEOUT] Eel.${name} did not respond in ${MAX_EEL_TIMEOUT}ms`);
                error.isConnectionTimeout = true;
                reject(error);
            }, MAX_EEL_TIMEOUT);
            
            // Cancel timeout if actual promise resolves first
            actualPromise.finally(() => clearTimeout(timer));
        });
        
        return Promise.race([actualPromise, timeoutPromise]).catch(error => {
            if (error.isConnectionTimeout) {
                console.error(`[EEL TIMEOUT] ${name} - Websocket likely disconnected. Reloading...`);
                setTimeout(() => location.reload(), 100);
            }
            throw error;
        });
    };
}

// === REQUEST QUEUE & DEBOUNCE SYSTEM ===
// Prevents rapid overlapping requests from destabilizing the connection
let requestQueue = [];
let isProcessingRequest = false;
let requestDebounceTimers = {};
const REQUEST_DEBOUNCE_MS = 300; // Minimum time between similar requests

/**
 * Queue an Eel request to prevent rapid overlapping calls
 * @param {string} funcName - The Eel function name
 * @param {*} params - Parameters (can be single value or array)
 * @param {object} options - Options like {debounce: true, debounceKey: 'uniqueId', timeout: ms}
 * @returns {Promise}
 */
async function queueEelRequest(funcName, params, options = {}) {
    // If debounce is enabled, cancel previous pending request with same key
    if (options.debounce && options.debounceKey) {
        const key = `${funcName}::${options.debounceKey}`;
        if (requestDebounceTimers[key]) {
            clearTimeout(requestDebounceTimers[key]);
            delete requestDebounceTimers[key];
        }
    }

    return new Promise((resolve, reject) => {
        const request = {
            funcName,
            params: Array.isArray(params) ? params : [params],
            resolve,
            reject,
            timeout: options.timeout || 10000,
            debounceKey: options.debounceKey
        };

        // If debounce is enabled, delay the request
        if (options.debounce && options.debounceKey) {
            const key = `${funcName}::${options.debounceKey}`;
            requestDebounceTimers[key] = setTimeout(() => {
                delete requestDebounceTimers[key];
                requestQueue.push(request);
                processRequestQueue();
            }, REQUEST_DEBOUNCE_MS);
        } else {
            requestQueue.push(request);
            processRequestQueue();
        }
    });
}

async function processRequestQueue() {
    if (isProcessingRequest || requestQueue.length === 0) {
        return;
    }

    isProcessingRequest = true;
    const request = requestQueue.shift();

    try {
        const eelFunc = eel[request.funcName];
        if (!eelFunc) {
            throw new Error(`Eel function '${request.funcName}' not found`);
        }

        // Call the Eel function with timeout
        const timeoutPromise = new Promise((_, reject) =>
            setTimeout(() => reject(new Error('Request timeout')), request.timeout)
        );

        const result = await Promise.race([
            eelFunc(...request.params)(),
            timeoutPromise
        ]);

        request.resolve(result);
    } catch (error) {
        console.error(`Eel request failed (${request.funcName}):`, error);
        
        // If the error suggests connection loss, reload immediately
        const errorMsg = (error.message || '').toLowerCase();
        if (errorMsg.includes('connection') || 
            errorMsg.includes('socket') || 
            errorMsg.includes('closed') ||
            errorMsg.includes('econnrefused') ||
            errorMsg.includes('disconnected')) {
            console.error("[QUEUE] Connection lost during Eel call. Reloading page...");
            setTimeout(() => location.reload(), 100);
            return; // Don't continue processing
        }
        
        request.reject(error);
    } finally {
        isProcessingRequest = false;
        // Process next request in queue
        if (requestQueue.length > 0) {
            processRequestQueue();
        }
    }
}

let selectBlockInProgress = false;
let selectBlockQueue = null;
let selectBlockTimer = null;  // Timer for debouncing rapid calls

function selectBlock(id) {
    // If already processing a selection, queue this one instead
    if (selectBlockInProgress) {
        selectBlockQueue = id;
        return;
    }
    
    // Clear any pending debounce timer
    if (selectBlockTimer !== null) {
        clearTimeout(selectBlockTimer);
    }
    
    selectBlockInProgress = true;
    selectedBlockId = id;
    
    // Update visual style inside iframe
    try {
        let doc = document.getElementById('preview-frame').contentDocument;
        if (doc) {
            doc.querySelectorAll('.editor-block').forEach(el => {
                el.classList.remove('selected');
                if(el.dataset.id === id) el.classList.add('selected');
            });
        }
    } catch (e) {
        console.warn("Error updating preview selection:", e);
    }

    // renderProperties is fast but wrap it for consistency
    try {
        renderProperties(id);
    } catch (e) {
        console.error("Error rendering properties:", e);
    } finally {
        selectBlockInProgress = false;
        
        // Process queued selection if any
        if (selectBlockQueue !== null) {
            const nextId = selectBlockQueue;
            selectBlockQueue = null;
            // Add small delay to allow WebSocket to process the current action
            selectBlockTimer = setTimeout(() => selectBlock(nextId), 100);
        }
    }
}

// Confirm dialog for copying files to project
eel.expose(confirm_copy_to_project);
function confirm_copy_to_project(fileName) {
    return confirm(
        `A kiválasztott fájl (${fileName}) a projekten kívül van.\n\n` +
        `Szeretnéd átmásolni a projekt 'uploads' mappájába?\n\n` +
        `Ha nem, a fájl nem lesz használva.`
    );
}

// Block common browser shortcuts inside the packaged app (Ctrl+F/Ctrl+S/F12/DevTools combos)
function blockBrowserShortcuts(e) {
    const key = (e.key || '').toLowerCase();
    const ctrl = e.ctrlKey || e.metaKey;
    const shift = e.shiftKey;

    // Let Ctrl+S trigger save instead of browser default
    if (ctrl && key === 's') {
        e.preventDefault();
        e.stopPropagation();
        if (typeof saveProject === 'function') {
            try {
                saveProject();
            } catch (_) {
                /* ignore save errors here */
            }
        }
        return false;
    }

    // Ctrl+Z -> undo, Ctrl+Y -> redo
    if (ctrl && !shift && key === 'z') {
        e.preventDefault();
        e.stopPropagation();
        if (typeof undo === 'function') { try { undo(); } catch (_) {} }
        return false;
    }
    if (ctrl && key === 'y') {
        e.preventDefault();
        e.stopPropagation();
        if (typeof redo === 'function') { try { redo(); } catch (_) {} }
        return false;
    }

    const isBlocked =
        (ctrl && key === 'f') ||
        key === 'f12' ||
        (ctrl && shift && (key === 'i' || key === 'j' || key === 'c'));

    if (isBlocked) {
        e.preventDefault();
        e.stopPropagation();
        return false;
    }
    return true;
}

function installShortcutGuards() {
    // Top-level window
    window.addEventListener('keydown', blockBrowserShortcuts, true);

    // Also guard the preview iframe so shortcuts don't leak there
    const iframe = document.getElementById('preview-frame');
    const attach = (frame) => {
        try {
            const doc = frame && frame.contentDocument;
            if (!doc) return;
            doc.addEventListener('keydown', blockBrowserShortcuts, true);
        } catch (_) {
            /* ignore cross-origin frames (not expected here) */
        }
    };

    if (iframe) {
        iframe.addEventListener('load', () => attach(iframe));
        attach(iframe);
    }
}

document.addEventListener('DOMContentLoaded', installShortcutGuards);
document.addEventListener('DOMContentLoaded', initializeBlockButtons);
document.addEventListener('DOMContentLoaded', setupBlockButtonObserver);

// Global ESC handler to close any open dialogs/overlays (settings, deploy, AI chat, editors)
function isElementVisible(el) {
    if (!el) return false;
    const cs = getComputedStyle(el);
    if (cs.display === 'none' || cs.visibility === 'hidden' || parseFloat(cs.opacity || '1') === 0) return false;
    return !(el.offsetWidth === 0 && el.offsetHeight === 0);
}

function getVisibleOverlays() {
    // Collect all potentially visible overlays with their z-indexes
    const overlays = [];
    
    const elements = [
        { el: document.querySelector('.rich-editor-overlay'), type: 'rte', closeFunc: null },
        { el: document.querySelector('.text-editor-overlay'), type: 'text', closeFunc: null },
        { el: document.getElementById('script-editor-overlay'), type: 'script-editor', closeFunc: null },
        { el: document.getElementById('project-content-overlay'), type: 'project-editor', closeFunc: 'closeProjectContentEditor' },
        { el: document.querySelector('[style*="position: fixed"][style*="z-index: 99999"]')?.parentElement?.querySelector('div'), type: 'page-assignment', closeFunc: null },
        { el: document.getElementById('settings-overlay'), type: 'settings', closeFunc: 'closeGlobalSettingsOverlay' },
        { el: document.getElementById('deploy-overlay'), type: 'deploy', closeFunc: 'closeDeployModal' },
        { el: document.getElementById('preset-library-modal'), type: 'preset-library', closeFunc: null },
        { el: document.getElementById('ai-chat-window'), type: 'ai-chat', closeFunc: 'closeAIChatWindow' }
    ];
    
    for (const item of elements) {
        if (item.el && isElementVisible(item.el)) {
            const zIndex = parseInt(getComputedStyle(item.el).zIndex, 10) || 0;
            overlays.push({ ...item, zIndex });
        }
    }
    
    return overlays;
}

function handleGlobalEscape(e) {
    if (e.key !== 'Escape') return;

    // Get all visible overlays sorted by z-index (highest first)
    const visibleOverlays = getVisibleOverlays();
    if (visibleOverlays.length === 0) return;
    
    visibleOverlays.sort((a, b) => b.zIndex - a.zIndex);
    const topmost = visibleOverlays[0];
    
    e.preventDefault();
    
    switch (topmost.type) {
        case 'rte':
            topmost.el.remove();
            break;
        case 'text':
            topmost.el.remove();
            break;
        case 'script-editor':
            topmost.el.remove();
            break;
        case 'project-editor':
            if (typeof closeProjectContentEditor === 'function') {
                closeProjectContentEditor();
            } else if (topmost.el) {
                topmost.el.style.display = 'none';
            }
            break;
        case 'page-assignment':
            if (topmost.el.closest('[style*="position: fixed"]')) {
                topmost.el.closest('[style*="position: fixed"]').remove();
            } else {
                topmost.el.remove();
            }
            break;
        case 'settings':
            if (typeof closeGlobalSettingsOverlay === 'function') {
                closeGlobalSettingsOverlay();
            }
            break;
        case 'deploy':
            if (typeof closeDeployModal === 'function') {
                closeDeployModal();
            }
            break;
        case 'preset-library':
            topmost.el.remove();
            break;
        case 'ai-chat':
            if (typeof closeAIChatWindow === 'function') {
                closeAIChatWindow();
            } else {
                topmost.el.style.display = 'none';
            }
            break;
    }
}

window.addEventListener('keydown', handleGlobalEscape, true);

// Ctrl+Shift+R or Cmd+Shift+R to force refresh from disk
window.addEventListener('keydown', async (e) => {
    if ((e.ctrlKey || e.metaKey) && e.shiftKey && e.key === 'r') {
        e.preventDefault();
        console.log("[Shortcut] === Ctrl+Shift+R TRIGGERED ===");
        try {
            console.log("[Shortcut] Calling refresh_project_from_disk()...");
            const result = await eel.refresh_project_from_disk()();
            console.log("[Shortcut] refresh_project_from_disk() returned:", result);
            
            if (result.success) {
                console.log("[Shortcut] Clearing old appData and fetching fresh data...");
                appData = {};
                appData = await eel.get_project_data()();
                console.log("[Shortcut] Fresh appData loaded - pages:", Object.keys(appData.pages || {}), "blocks:", Object.keys(appData.blocks_data || {}).length);
                console.log("[Shortcut] Calling renderCanvas()...");
                renderCanvas();
                showToast("Project refreshed from disk", 2000);
            } else {
                alert("Refresh failed: " + result.message);
            }
        } catch (e) {
            console.error("[Shortcut] Error:", e);
            alert("Refresh error: " + e);
        }
    }
});

// ===== FILE PICKER DEBOUNCE =====
let filePickerActive = false;

async function pickImageForBlock(blockId) {
    if (filePickerActive) {
        console.log("File picker already active, ignoring");
        return;
    }
    filePickerActive = true;
    
    try {
        let p = await eel.pick_image()();
        if (p) {
            updateBlockData(blockId, 'src', p);
        }
    } finally {
        setTimeout(() => { filePickerActive = false; }, 500);
    }
}

async function pickImagesForBlock(blockId, propertyName = 'images') {
    if (filePickerActive) {
        console.log("File picker already active, ignoring");
        return [];
    }
    filePickerActive = true;
    
    try {
        const picked = await eel.pick_images_multiselect()();
        const paths = Array.isArray(picked)
            ? picked.filter(Boolean).map(p => String(p).replace(/\\/g, '/'))
            : [];
        return paths;
    } catch (err) {
        console.error('pickImagesForBlock failed', err);
        return [];
    } finally {
        setTimeout(() => { filePickerActive = false; }, 500);
    }
}

async function pickVideoForBlock(blockId) {
    if (filePickerActive) {
        console.log("File picker already active, ignoring");
        return;
    }
    filePickerActive = true;
    
    try {
        let path = await eel.pick_video()();
        if (path) {
            updateBlockData(blockId, 'src', path);
        }
    } finally {
        setTimeout(() => { filePickerActive = false; }, 500);
    }
}

// UI helpers: transient modal + toast for generation status
window.showGeneratingModal = function(message) {
    // remove existing if any
    let existing = document.getElementById('generating-modal');
    if (existing) existing.remove();

    const modal = document.createElement('div');
    modal.id = 'generating-modal';
    modal.style.position = 'fixed';
    modal.style.inset = '0';
    modal.style.display = 'flex';
    modal.style.alignItems = 'center';
    modal.style.justifyContent = 'center';
    modal.style.background = 'rgba(0,0,0,0.55)';
    modal.style.zIndex = 99999;
    modal.style.backdropFilter = 'blur(3px)';

    const box = document.createElement('div');
    box.style.background = '#1e1e1e';
    box.style.color = '#eee';
    box.style.padding = '24px 30px';
    box.style.borderRadius = '12px';
    box.style.boxShadow = '0 16px 44px rgba(0,0,0,0.5)';
    box.style.maxWidth = '90%';
    box.style.display = 'flex';
    box.style.flexDirection = 'column';
    box.style.alignItems = 'center';
    box.style.gap = '16px';
    box.style.border = '1px solid #333';

    const spinner = document.createElement('div');
    spinner.style.width = '36px';
    spinner.style.height = '36px';
    spinner.style.border = '3px solid rgba(255,255,255,0.1)';
    spinner.style.borderTop = '3px solid #1976d2';
    spinner.style.borderRadius = '50%';
    spinner.style.animation = 'genspin 0.8s linear infinite';

    const msg = document.createElement('div');
    msg.style.fontSize = '14px';
    msg.style.textAlign = 'center';
    msg.style.lineHeight = '1.5';
    msg.innerText = message || 'Generating...';
    
    const hint = document.createElement('div');
    hint.style.fontSize = '12px';
    hint.style.color = '#888';
    hint.innerText = 'Please wait, this may take a moment...';

    box.appendChild(spinner);
    box.appendChild(msg);
    box.appendChild(hint);
    modal.appendChild(box);

    // simple animation keyframes (insert once)
    if (!document.getElementById('genspin-style')) {
        const style = document.createElement('style');
        style.id = 'genspin-style';
        style.innerHTML = `@keyframes genspin { from { transform: rotate(0deg) } to { transform: rotate(360deg) } }`;
        document.head.appendChild(style);
    }

    document.body.appendChild(modal);
    return modal;
};

window.hideGeneratingModal = function() {
    let existing = document.getElementById('generating-modal');
    if (existing) existing.remove();
};

/**
 * Enhanced toast notification system with types and icons.
 * @param {string} message - The message to display
 * @param {number} [timeout=3500] - Duration in ms
 * @param {string} [type='info'] - One of: 'success', 'error', 'warning', 'info'
 */
window.showToast = function(message, timeout, type) {
    timeout = typeof timeout === 'number' ? timeout : 3500;
    type = type || 'info';
    
    // Auto-detect type from message content if not explicitly set
    if (type === 'info') {
        const msgLower = (message || '').toLowerCase();
        if (msgLower.includes('fail') || msgLower.includes('error') || msgLower.includes('could not')) {
            type = 'error';
        } else if (msgLower.includes('success') || msgLower.includes('saved') || msgLower.includes('finished') || msgLower.includes('completed')) {
            type = 'success';
        } else if (msgLower.includes('warn') || msgLower.includes('orphan')) {
            type = 'warning';
        }
    }
    
    const icons = {
        success: '✓',
        error: '✕',
        warning: '⚠',
        info: 'ℹ'
    };
    
    const id = 'gen-toast';
    let toast = document.getElementById(id);
    if (toast) toast.remove();
    
    toast = document.createElement('div');
    toast.id = id;
    toast.className = 'toast-notification toast-' + type;
    
    const iconEl = document.createElement('span');
    iconEl.className = 'toast-icon';
    iconEl.textContent = icons[type] || icons.info;
    
    const bodyEl = document.createElement('span');
    bodyEl.className = 'toast-body';
    bodyEl.textContent = message || '';
    
    const dismissEl = document.createElement('button');
    dismissEl.className = 'toast-dismiss';
    dismissEl.textContent = '×';
    dismissEl.onclick = function() {
        toast.classList.remove('toast-visible');
        setTimeout(() => { try { toast.remove(); } catch(e){} }, 300);
    };
    
    toast.appendChild(iconEl);
    toast.appendChild(bodyEl);
    toast.appendChild(dismissEl);
    document.body.appendChild(toast);
    
    // Trigger entrance animation
    requestAnimationFrame(() => {
        requestAnimationFrame(() => {
            toast.classList.add('toast-visible');
        });
    });
    
    // Auto dismiss
    setTimeout(() => {
        if (toast.parentNode) {
            toast.classList.remove('toast-visible');
            setTimeout(() => { try { toast.remove(); } catch(e){} }, 300);
        }
    }, timeout);
};

/**
 * Custom confirm dialog that replaces native confirm() for a more polished UX.
 * Falls back to native confirm() on any error.
 * @param {string} message - The confirmation message
 * @param {object} [options] - Optional settings
 * @param {string} [options.title] - Dialog title
 * @param {string} [options.confirmText] - Confirm button label
 * @param {string} [options.cancelText] - Cancel button label
 * @param {string} [options.type] - 'danger' or 'info' (changes confirm button style)
 * @returns {Promise<boolean>}
 */
window.showConfirm = function(message, options) {
    options = options || {};
    return new Promise((resolve) => {
        try {
            const overlay = document.createElement('div');
            overlay.className = 'confirm-overlay';
            
            const dialog = document.createElement('div');
            dialog.className = 'confirm-dialog';
            
            const title = document.createElement('h3');
            title.className = 'confirm-title';
            title.textContent = options.title || ((typeof t === 'function') ? t('confirm_title') : 'Confirm');
            
            const msg = document.createElement('p');
            msg.className = 'confirm-message';
            msg.textContent = message;
            
            const actions = document.createElement('div');
            actions.className = 'confirm-actions';
            
            const cancelBtn = document.createElement('button');
            cancelBtn.className = 'btn-secondary';
            cancelBtn.textContent = options.cancelText || ((typeof t === 'function') ? t('cancel') : 'Cancel');
            cancelBtn.onclick = function() {
                overlay.classList.remove('confirm-visible');
                setTimeout(() => { overlay.remove(); resolve(false); }, 200);
            };
            
            const confirmBtn = document.createElement('button');
            confirmBtn.className = (options.type === 'danger') ? 'btn-danger' : 'btn-primary';
            confirmBtn.textContent = options.confirmText || 'OK';
            confirmBtn.onclick = function() {
                overlay.classList.remove('confirm-visible');
                setTimeout(() => { overlay.remove(); resolve(true); }, 200);
            };
            
            // Close on Escape
            const onKey = function(e) {
                if (e.key === 'Escape') {
                    document.removeEventListener('keydown', onKey);
                    overlay.classList.remove('confirm-visible');
                    setTimeout(() => { overlay.remove(); resolve(false); }, 200);
                }
            };
            document.addEventListener('keydown', onKey);
            
            actions.appendChild(cancelBtn);
            actions.appendChild(confirmBtn);
            dialog.appendChild(title);
            dialog.appendChild(msg);
            dialog.appendChild(actions);
            overlay.appendChild(dialog);
            document.body.appendChild(overlay);
            
            // Animate in
            requestAnimationFrame(() => {
                requestAnimationFrame(() => {
                    overlay.classList.add('confirm-visible');
                    confirmBtn.focus();
                });
            });
        } catch (e) {
            // Fallback to native confirm
            resolve(confirm(message));
        }
    });
};

window.generateSite = async function(skipServerOpen = false) {
    stopHeartbeat();
    // Show transient modal while generation runs
    try {
        showGeneratingModal('Generating site and images — this may take a while...');
        const result = await eel.generate_website()();
        hideGeneratingModal();

        // If backend returns an object (new behavior), handle orphan files
        if (result && typeof result === 'object') {
            if (!result.success) {
                showToast(result.message || 'Generation failed', 6000, 'error');
            } else {
                if ((result.orphans && result.orphans.length > 0) || (result.orphan_folders && result.orphan_folders.length > 0) || (result.orphan_smal_images && result.orphan_smal_images.length > 0) || (result.name_conflicts && result.name_conflicts.length > 0) || (result.orphan_uploads && result.orphan_uploads.length > 0)) {
                    // show cleanup modal with list of files, folders, and conflicts
                    openCleanupModal(result.orphans || [], result.orphan_folders || [], result.orphan_smal_images || [], result.name_conflicts || [], result.orphan_uploads || []);
                    showToast(result.message || 'Generation finished with orphan files/folders', 6000, 'warning');
                } else {
                    showToast(result.message || 'Generation finished', 6000, 'success');
                }
                // Open generated site in browser via local server (unless skipServerOpen is true)
                if (!skipServerOpen && window.openGeneratedSite) window.openGeneratedSite();
            }
        } else {
            // Backwards-compatible: text response
            showToast(result || 'Generation finished', 6000);
            // Open generated site in browser via local server (unless skipServerOpen is true)
            if (!skipServerOpen && window.openGeneratedSite) window.openGeneratedSite();
        }
    } catch (err) {
        hideGeneratingModal();
        // Use enhanced toast for error feedback
        showToast('Generation failed: ' + (err && err.message ? err.message : err), 6000, 'error');
    } finally {
        // 2. VISSZAKAPCSOLJUK, ha végzett
        startHeartbeat(); 
    }
};

window.openGeneratedSite = async function() {
    console.log("Opening generated site...");
    try {
        await eel.serve_and_open_site('.', 'index.html')();
    } catch (e) {
        console.error("Error opening site:", e);
    }
};


window.openCleanupModal = function(files, folders, smal_images, name_conflicts, orphan_uploads) {
    const fileListEl = document.getElementById('cleanup-files-list');
    const folderListEl = document.getElementById('cleanup-folders-list');
    const smalListEl = document.getElementById('cleanup-smal-list');
    const conflictsListEl = document.getElementById('cleanup-conflicts-list');
    const uploadsListEl = document.getElementById('cleanup-uploads-list');

    // Render name conflicts as warnings
    if (!name_conflicts || name_conflicts.length === 0) {
        conflictsListEl.innerHTML = '';
        conflictsListEl.style.display = 'none';
    } else {
        conflictsListEl.style.display = 'block';
        conflictsListEl.innerHTML = '';
        
        const conflictHeader = document.createElement('div');
        conflictHeader.style.color = '#FFB74D';
        conflictHeader.style.fontWeight = 'bold';
        conflictHeader.style.marginBottom = '10px';
        conflictHeader.innerHTML = (typeof t === 'function') ? t('name_conflicts_title') : '⚠️ Name Conflicts:';
        conflictsListEl.appendChild(conflictHeader);
        
        const conflictDesc = document.createElement('div');
        conflictDesc.style.color = '#FFB74D';
        conflictDesc.style.fontSize = '12px';
        conflictDesc.style.marginBottom = '12px';
        conflictDesc.style.opacity = '0.9';
        conflictDesc.innerText = (typeof t === 'function') ? t('name_conflict_desc') : 'Folder and HTML file with the same name - may cause conflicts on host';
        conflictsListEl.appendChild(conflictDesc);
        
        name_conflicts.forEach((conflict) => {
            const row = document.createElement('div');
            row.style.padding = '8px';
            row.style.marginBottom = '6px';
            row.style.backgroundColor = '#3d2818';
            row.style.borderLeft = '3px solid #FFB74D';
            row.style.borderRadius = '4px';
            row.style.color = '#FFB74D';
            row.style.fontSize = '13px';
            row.innerText = `${conflict.conflict}`;
            conflictsListEl.appendChild(row);
        });
    }

    // Render files
    fileListEl.innerHTML = '';
    if (!files || files.length === 0) {
        fileListEl.innerHTML = '<div style="color:#aaa;">Nincsenek ismeretlen fájlok.</div>';
    } else {
        files.forEach((fn) => {
            const row = document.createElement('div');
            row.className = 'cleanup-item';
            row.style.display = 'flex';
            row.style.justifyContent = 'space-between';
            row.style.alignItems = 'center';
            row.style.padding = '6px 8px';
            row.style.borderBottom = '1px solid #2b2b2b';

            const name = document.createElement('div');
            name.style.flex = '1';
            name.style.color = '#ddd';
            name.style.wordBreak = 'break-all';
            name.innerText = fn;

            const controls = document.createElement('div');
            controls.style.display = 'flex';
            controls.style.gap = '8px';
            controls.style.alignItems = 'center';

            const wlLabel = document.createElement('label');
            wlLabel.style.fontSize = '12px';
            wlLabel.style.color = '#aaa';
            wlLabel.style.display = 'flex';
            wlLabel.style.gap = '6px';
            wlLabel.style.alignItems = 'center';

            const wlCheckbox = document.createElement('input');
            wlCheckbox.type = 'checkbox';
            wlCheckbox.dataset.filename = fn;
            wlCheckbox.title = 'Add to whitelist (ignore in future)';

            wlLabel.appendChild(wlCheckbox);
            wlLabel.appendChild(document.createTextNode('Megtartás'));

            controls.appendChild(wlLabel);

            row.appendChild(name);
            row.appendChild(controls);
            fileListEl.appendChild(row);
        });
    }

    // Render folders
    folderListEl.innerHTML = '';
    if (!folders || folders.length === 0) {
        folderListEl.innerHTML = '<div style="color:#aaa;">Nincsenek ismeretlen mappák.</div>';
    } else {
        folders.forEach((fn) => {
            const row = document.createElement('div');
            row.className = 'cleanup-item';
            row.style.display = 'flex';
            row.style.justifyContent = 'space-between';
            row.style.alignItems = 'center';
            row.style.padding = '6px 8px';
            row.style.borderBottom = '1px solid #2b2b2b';

            const name = document.createElement('div');
            name.style.flex = '1';
            name.style.color = '#ddd';
            name.style.wordBreak = 'break-all';
            name.innerText = fn;

            const controls = document.createElement('div');
            controls.style.display = 'flex';
            controls.style.gap = '8px';
            controls.style.alignItems = 'center';

            const wlLabel = document.createElement('label');
            wlLabel.style.fontSize = '12px';
            wlLabel.style.color = '#aaa';
            wlLabel.style.display = 'flex';
            wlLabel.style.gap = '6px';
            wlLabel.style.alignItems = 'center';

            const wlCheckbox = document.createElement('input');
            wlCheckbox.type = 'checkbox';
            wlCheckbox.dataset.foldername = fn;
            wlCheckbox.title = 'Add to whitelist (ignore in future)';

            wlLabel.appendChild(wlCheckbox);
            wlLabel.appendChild(document.createTextNode('Megtartás'));

            controls.appendChild(wlLabel);

            row.appendChild(name);
            row.appendChild(controls);
            folderListEl.appendChild(row);
        });
    }
    
    // Render orphan smal images
    smalListEl.innerHTML = '';
    if (!smal_images || smal_images.length === 0) {
        smalListEl.innerHTML = '<div style="color:#aaa;">Nincsenek szükségtelen responsive képek.</div>';
    } else {
        smal_images.forEach((fn) => {
            const row = document.createElement('div');
            row.className = 'cleanup-item';
            row.style.display = 'flex';
            row.style.justifyContent = 'space-between';
            row.style.alignItems = 'center';
            row.style.padding = '6px 8px';
            row.style.borderBottom = '1px solid #2b2b2b';

            const name = document.createElement('div');
            name.style.flex = '1';
            name.style.color = '#ddd';
            name.style.wordBreak = 'break-all';
            name.innerText = fn;

            const controls = document.createElement('div');
            controls.style.display = 'flex';
            controls.style.gap = '8px';
            controls.style.alignItems = 'center';

            const wlLabel = document.createElement('label');
            wlLabel.style.fontSize = '12px';
            wlLabel.style.color = '#aaa';
            wlLabel.style.display = 'flex';
            wlLabel.style.gap = '6px';
            wlLabel.style.alignItems = 'center';

            const wlCheckbox = document.createElement('input');
            wlCheckbox.type = 'checkbox';
            wlCheckbox.dataset.smal_image = fn;
            wlCheckbox.title = 'Keep (do not delete)';

            wlLabel.appendChild(wlCheckbox);
            wlLabel.appendChild(document.createTextNode('Megtartás'));

            controls.appendChild(wlLabel);

            row.appendChild(name);
            row.appendChild(controls);
            smalListEl.appendChild(row);
        });
    }

    // Render orphan uploads as mosaic grid with thumbnails
    if (uploadsListEl) {
        uploadsListEl.innerHTML = '';
        if (!orphan_uploads || orphan_uploads.length === 0) {
            uploadsListEl.innerHTML = '<div style="color:#aaa;">' + ((typeof t === 'function') ? t('no_unused_uploads') : 'Nincsenek nem használt fájlok az uploads mappában.') + '</div>';
        } else {
            const imageExts = new Set(['.jpg','.jpeg','.png','.gif','.webp','.avif','.svg','.bmp','.ico']);
            const grid = document.createElement('div');
            grid.style.display = 'grid';
            grid.style.gridTemplateColumns = 'repeat(auto-fill, minmax(120px, 1fr))';
            grid.style.gap = '8px';
            grid.style.padding = '2px';

            orphan_uploads.forEach((fn) => {
                const ext = '.' + fn.split('.').pop().toLowerCase();
                const isImage = imageExts.has(ext);

                const card = document.createElement('div');
                card.style.background = '#2a2a2a';
                card.style.borderRadius = '6px';
                card.style.border = '1px solid #444';
                card.style.overflow = 'hidden';
                card.style.display = 'flex';
                card.style.flexDirection = 'column';
                card.style.position = 'relative';
                card.style.transition = 'border-color 0.2s';

                // Thumbnail / icon area
                const thumbArea = document.createElement('div');
                thumbArea.style.height = '80px';
                thumbArea.style.display = 'flex';
                thumbArea.style.alignItems = 'center';
                thumbArea.style.justifyContent = 'center';
                thumbArea.style.cursor = 'pointer';
                thumbArea.style.background = '#1a1a1a';
                thumbArea.style.position = 'relative';
                thumbArea.title = (typeof t === 'function') ? t('click_to_open') : 'Kattints a megnyitáshoz';
                thumbArea.addEventListener('mouseenter', () => { card.style.borderColor = '#888'; });
                thumbArea.addEventListener('mouseleave', () => { card.style.borderColor = '#444'; });

                // Filename overlay on top of thumbnail
                const fnOverlay = document.createElement('div');
                fnOverlay.style.position = 'absolute';
                fnOverlay.style.top = '0';
                fnOverlay.style.left = '0';
                fnOverlay.style.right = '0';
                fnOverlay.style.background = 'rgba(0,0,0,0.6)';
                fnOverlay.style.color = '#eee';
                fnOverlay.style.fontSize = '10px';
                fnOverlay.style.lineHeight = '1.2';
                fnOverlay.style.padding = '3px 5px';
                fnOverlay.style.wordBreak = 'break-all';
                fnOverlay.style.overflow = 'hidden';
                fnOverlay.style.maxHeight = '30px';
                fnOverlay.style.zIndex = '1';
                fnOverlay.style.pointerEvents = 'none';
                fnOverlay.textContent = fn.replace(/^uploads\//, '');

                if (isImage) {
                    const img = document.createElement('img');
                    // Use small responsive variant if available, fall back to original
                    img.src = '/project_assets/smal/480/' + fn;
                    img.onerror = function() { this.src = '/project_assets/' + fn; };
                    img.style.width = '100%';
                    img.style.height = '100%';
                    img.style.objectFit = 'cover';
                    img.style.display = 'block';
                    img.loading = 'lazy';
                    thumbArea.appendChild(img);
                } else {
                    const icon = document.createElement('div');
                    icon.style.fontSize = '32px';
                    icon.style.color = '#888';
                    const extUpper = ext.replace('.', '').toUpperCase();
                    const iconMap = {'.mp4':'🎬','.webm':'🎬','.mov':'🎬','.avi':'🎬','.pdf':'📄','.zip':'📦','.rar':'📦','.js':'📜','.css':'📜','.html':'📜','.json':'📜','.txt':'📝','.doc':'📄','.docx':'📄'};
                    icon.textContent = iconMap[ext] || '📁';
                    thumbArea.appendChild(icon);
                    const extLabel = document.createElement('div');
                    extLabel.style.color = '#666';
                    extLabel.style.fontSize = '11px';
                    extLabel.style.marginTop = '4px';
                    extLabel.textContent = extUpper;
                    thumbArea.appendChild(extLabel);
                    thumbArea.style.flexDirection = 'column';
                }

                thumbArea.appendChild(fnOverlay);

                // Click to open file with system default
                thumbArea.addEventListener('click', async () => {
                    try { await eel.open_file_in_system(fn)(); } catch(e) { console.error('Open file failed:', e); }
                });

                // Bottom bar: just the keep checkbox
                const infoBar = document.createElement('div');
                infoBar.style.padding = '3px 6px';
                infoBar.style.display = 'flex';
                infoBar.style.alignItems = 'center';
                infoBar.style.gap = '4px';
                infoBar.style.borderTop = '1px solid #333';

                const wlCheckbox = document.createElement('input');
                wlCheckbox.type = 'checkbox';
                wlCheckbox.dataset.upload_file = fn;
                wlCheckbox.title = 'Keep (do not delete)';
                wlCheckbox.style.flexShrink = '0';

                const keepLabel = document.createElement('span');
                keepLabel.style.fontSize = '10px';
                keepLabel.style.color = '#999';
                keepLabel.textContent = (typeof t === 'function') ? t('keep_item') : 'Megtartás';

                infoBar.appendChild(wlCheckbox);
                infoBar.appendChild(keepLabel);

                card.appendChild(thumbArea);
                card.appendChild(infoBar);
                grid.appendChild(card);
            });

            uploadsListEl.appendChild(grid);
        }
    }

    document.getElementById('cleanup-overlay').style.display = 'flex';
    // store current orphans on overlay for reference
    document.getElementById('cleanup-overlay').dataset.orphans_files = JSON.stringify(files || []);
    document.getElementById('cleanup-overlay').dataset.orphans_folders = JSON.stringify(folders || []);
    document.getElementById('cleanup-overlay').dataset.orphans_smal = JSON.stringify(smal_images || []);
    document.getElementById('cleanup-overlay').dataset.orphans_uploads = JSON.stringify(orphan_uploads || []);
};

function createSmalListElement() {
    let smalListEl = document.getElementById('cleanup-smal-list');
    if (!smalListEl) {
        smalListEl = document.createElement('div');
        smalListEl.id = 'cleanup-smal-list';
        const cleanupContent = document.querySelector('.cleanup-modal-content');
        if (cleanupContent) {
            const folderSection = document.getElementById('cleanup-folders-list').parentElement;
            cleanupContent.insertBefore(smalListEl, folderSection.nextElementSibling);
        }
    }
    return smalListEl;
}

window.closeCleanupModal = function() {
    document.getElementById('cleanup-overlay').style.display = 'none';
    try { delete document.getElementById('cleanup-overlay').dataset.orphans; } catch(e){}
};

window.deleteAllOrphans = async function() {
    const overlay = document.getElementById('cleanup-overlay');
    const orphans_files = JSON.parse(overlay.dataset.orphans_files || '[]');
    const orphans_folders = JSON.parse(overlay.dataset.orphans_folders || '[]');
    const orphans_smal = JSON.parse(overlay.dataset.orphans_smal || '[]');
    const orphans_uploads = JSON.parse(overlay.dataset.orphans_uploads || '[]');
    if ((!orphans_files || orphans_files.length === 0) && (!orphans_folders || orphans_folders.length === 0) && (!orphans_smal || orphans_smal.length === 0) && (!orphans_uploads || orphans_uploads.length === 0)) { closeCleanupModal(); return; }

    // Gather whitelist selections for files, folders, smal images, and uploads
    const fileList = document.getElementById('cleanup-files-list');
    const fileCheckboxes = fileList.querySelectorAll('input[type="checkbox"]');
    const files_to_whitelist = [];
    fileCheckboxes.forEach(cb => { if (cb.checked) files_to_whitelist.push(cb.dataset.filename); });

    const folderList = document.getElementById('cleanup-folders-list');
    const folderCheckboxes = folderList.querySelectorAll('input[type="checkbox"]');
    const folders_to_whitelist = [];
    folderCheckboxes.forEach(cb => { if (cb.checked) folders_to_whitelist.push(cb.dataset.foldername); });

    const smalList = document.getElementById('cleanup-smal-list');
    const smalCheckboxes = smalList ? smalList.querySelectorAll('input[type="checkbox"]') : [];
    const smal_to_whitelist = [];
    smalCheckboxes.forEach(cb => { if (cb.checked) smal_to_whitelist.push(cb.dataset.smal_image); });

    const uploadsList = document.getElementById('cleanup-uploads-list');
    const uploadsCheckboxes = uploadsList ? uploadsList.querySelectorAll('input[type="checkbox"]') : [];
    const uploads_to_whitelist = [];
    uploadsCheckboxes.forEach(cb => { if (cb.checked) uploads_to_whitelist.push(cb.dataset.upload_file); });

    // Compute actual deletion sets (exclude whitelisted items)
    const files_to_delete = orphans_files.filter(f => !files_to_whitelist.includes(f));
    const folders_to_delete = orphans_folders.filter(f => !folders_to_whitelist.includes(f));
    const smal_to_delete = orphans_smal.filter(f => !smal_to_whitelist.includes(f));
    const uploads_to_delete = orphans_uploads.filter(f => !uploads_to_whitelist.includes(f));

    const deleteFilesCount = files_to_delete.length;
    const deleteFoldersCount = folders_to_delete.length;
    const deleteSmalCount = smal_to_delete.length;
    const deleteUploadsCount = uploads_to_delete.length;

    if (deleteFilesCount === 0 && deleteFoldersCount === 0 && deleteSmalCount === 0 && deleteUploadsCount === 0 && files_to_whitelist.length === 0 && folders_to_whitelist.length === 0 && smal_to_whitelist.length === 0 && uploads_to_whitelist.length === 0) {
        alert('No files or folders selected for deletion or whitelisting.');
        closeCleanupModal();
        return;
    }

    let confirmMsg = '';
    let totalDelete = deleteFilesCount + deleteFoldersCount + deleteSmalCount + deleteUploadsCount;
    if (totalDelete > 0) {
        const parts = [];
        if (deleteFilesCount > 0) parts.push(`${deleteFilesCount} file(s)`);
        if (deleteFoldersCount > 0) parts.push(`${deleteFoldersCount} folder(s)`);
        if (deleteSmalCount > 0) parts.push(`${deleteSmalCount} responsive image(s)`);
        if (deleteUploadsCount > 0) parts.push(`${deleteUploadsCount} unused upload(s)`);
        confirmMsg = `Delete ${parts.join(' and ')}? This cannot be undone.`;
    }

    if (confirmMsg && !confirm(confirmMsg)) return;
    if (!confirmMsg && !confirm(`Add ${files_to_whitelist.length + folders_to_whitelist.length + smal_to_whitelist.length + uploads_to_whitelist.length} item(s) to whitelist?`)) return;

    try {
        let fileResp = null;
        let folderResp = null;
        let smalResp = null;
        let uploadsResp = null;
        if (deleteFilesCount > 0 || files_to_whitelist.length > 0) {
            fileResp = await eel.cleanup_site_files(files_to_delete, files_to_whitelist)();
        }
        if (deleteFoldersCount > 0 || folders_to_whitelist.length > 0) {
            folderResp = await eel.delete_project_folders(folders_to_delete, folders_to_whitelist)();
        }
        if (deleteSmalCount > 0 || smal_to_whitelist.length > 0) {
            smalResp = await eel.delete_orphan_smal_images(smal_to_delete, smal_to_whitelist)();
        }
        if (deleteUploadsCount > 0 || uploads_to_whitelist.length > 0) {
            uploadsResp = await eel.delete_orphan_uploads(uploads_to_delete, uploads_to_whitelist)();
        }

        // Aggregate messages
        const msgs = [];
        if (fileResp) msgs.push(fileResp.message || 'Files processed');
        if (folderResp) msgs.push(folderResp.message || 'Folders processed');
        if (smalResp) msgs.push(smalResp.message || 'Responsive images processed');
        if (uploadsResp) msgs.push(uploadsResp.message || 'Unused uploads processed');

        if ((fileResp && !fileResp.success) || (folderResp && !folderResp.success) || (smalResp && !smalResp.success) || (uploadsResp && !uploadsResp.success)) {
            showToast((msgs.join(' | ')) || 'Cleanup partially failed', 8000);
        } else {
            showToast((msgs.join(' | ')) || 'Cleanup completed', 6000);
        }
    } catch (err) {
        alert('Cleanup failed: ' + (err && err.message ? err.message : err));
    } finally {
        closeCleanupModal();
        // Refresh project data and UI to reflect possible whitelist changes
        appData = await eel.get_project_data()();
        updatePageSelector();
    }
};

window.openExceptionsModal = async function() {
    try {
        const exceptions = appData.ignore_files || [];
        const listEl = document.getElementById('exceptions-list');
        
        listEl.innerHTML = '';
        if (!exceptions || exceptions.length === 0) {
            const noExceptionsEl = document.createElement('div');
            noExceptionsEl.style.color = '#aaa';
            noExceptionsEl.style.padding = '12px';
            noExceptionsEl.textContent = t('no_exceptions');
            listEl.appendChild(noExceptionsEl);
        } else {
            exceptions.forEach((item) => {
                const row = document.createElement('div');
                row.style.display = 'flex';
                row.style.justifyContent = 'space-between';
                row.style.alignItems = 'center';
                row.style.padding = '8px 12px';
                row.style.borderBottom = '1px solid #2b2b2b';
                row.style.gap = '8px';

                const name = document.createElement('div');
                name.style.flex = '1';
                name.style.color = '#ddd';
                name.style.wordBreak = 'break-all';
                name.innerText = item;

                const removeBtn = document.createElement('button');
                removeBtn.style.background = '#d32f2f';
                removeBtn.style.color = 'white';
                removeBtn.style.border = 'none';
                removeBtn.style.borderRadius = '4px';
                removeBtn.style.padding = '6px 10px';
                removeBtn.style.cursor = 'pointer';
                removeBtn.style.fontSize = '12px';
                removeBtn.style.whiteSpace = 'nowrap';
                removeBtn.textContent = t('remove_exception');
                removeBtn.onclick = async () => {
                    const confirmMsg = `${t('remove_exception')}: ${item}?`;
                    if (!confirm(confirmMsg)) return;
                    try {
                        const resp = await eel.remove_exception_file(item)();
                        if (resp.success) {
                            showToast(t('exception_removed'), 5000);
                            appData = await eel.get_project_data()();
                            openExceptionsModal();
                        } else {
                            alert('Error: ' + (resp.message || 'Could not remove exception'));
                        }
                    } catch (err) {
                        alert('Error: ' + (err && err.message ? err.message : err));
                    }
                };

                row.appendChild(name);
                row.appendChild(removeBtn);
                listEl.appendChild(row);
            });
        }
        
        document.getElementById('exceptions-overlay').style.display = 'flex';
    } catch (err) {
        alert('Error opening exceptions: ' + (err && err.message ? err.message : err));
    }
};

window.closeExceptionsModal = function() {
    document.getElementById('exceptions-overlay').style.display = 'none';
};

function updatePageSelector() {
    let sel = document.getElementById('page-select');
    sel.innerHTML = '';
    // Use page_order if available, otherwise iterate over pages
    let pageOrder = appData.page_order || Object.keys(appData.pages);
    for(let k of pageOrder) {
        if (!appData.pages[k]) continue; // Skip if page no longer exists
        let opt = document.createElement('option');
        let hidden = (appData.page_hidden && appData.page_hidden[k]);
        let langVis = (appData.page_lang_visibility && appData.page_lang_visibility[k]);
        let suffix = '';
        if (hidden) suffix += ' (hidden)';
        if (langVis && langVis.length > 0) suffix += ' 🌐';
        opt.value = k;
        opt.innerText = (appData.page_titles[k] || k) + suffix;
        sel.appendChild(opt);
    }
}
window.changePage = (v) => { eel.set_current_page(v)(); renderCanvas(); };

///////////////////////////////////////////////
//Felül a gombok funkciói
///////////////////////////////////////////////


// Page management helpers: rename, duplicate, hide/unhide
window.renamePage = async function() {
    let sel = document.getElementById('page-select');
    let oldKey = sel.value;
    if(!oldKey) return;
    let newName = prompt('New page name (display name):', appData.page_titles[oldKey] || oldKey);
    if(!newName) return;
    // generate slug for key - removes accents properly
    let slug = slugifyPageName(newName);
    // If renaming the special 'index' page, do NOT change its key (filename).
    if (oldKey === 'index') {
        appData.page_titles[oldKey] = newName;
        await eel.save_project_data(appData)();
        updatePageSelector();
        renderProperties(oldKey);
        renderCanvas();
        return;
    }
    if(slug === oldKey) {
        // only update title
        appData.page_titles[oldKey] = newName;
        await eel.save_project_data(appData)();
        updatePageSelector();
        renderProperties(oldKey);
        renderCanvas();
        return;
    }
    
    // Ensure slug is unique - if conflict, append number
    let finalSlug = slug;
    let counter = 1;
    while(appData.pages[finalSlug]) {
        finalSlug = slug + '-' + counter;
        counter++;
    }
    slug = finalSlug;
    
    // move page entry
    appData.pages[slug] = appData.pages[oldKey];
    delete appData.pages[oldKey];
    // move title
    appData.page_titles[slug] = newName;
    delete appData.page_titles[oldKey];
    // move hidden state
    if(!appData.page_hidden) appData.page_hidden = {};
    appData.page_hidden[slug] = appData.page_hidden[oldKey] || false;
    delete appData.page_hidden[oldKey];
    // move language visibility
    if(!appData.page_lang_visibility) appData.page_lang_visibility = {};
    if(appData.page_lang_visibility[oldKey]) {
        appData.page_lang_visibility[slug] = appData.page_lang_visibility[oldKey];
        delete appData.page_lang_visibility[oldKey];
    }
    // update page_order to reflect key change
    if(!appData.page_order) appData.page_order = Object.keys(appData.pages);
    let idx = appData.page_order.indexOf(oldKey);
    if(idx >= 0) {
        appData.page_order[idx] = slug;
    }
    // update current page selection
    await eel.save_project_data(appData)();
    await eel.update_page_order(appData.page_order)();
    updatePageSelector();
    document.getElementById('page-select').value = slug;
    renderCanvas();
};

// Helper function to slugify text - removes accents and special characters
function slugifyPageName(text) {
    // Normalize to NFD (decomposed form), keeping base characters
    let normalized = text.normalize('NFD');
    // Remove combining marks (accents)
    let cleaned = normalized.replace(/[\u0300-\u036f]/g, '');
    // Lowercase and remove special characters (keep only alphanumeric, spaces, hyphens)
    let slug = cleaned.toLowerCase().replace(/[^a-z0-9\s-]/g, '').trim();
    // Replace spaces and multiple hyphens with single hyphen
    return slug.replace(/[\s-]+/g, '-');
}

window.editPageDescription = function() {
    let sel = document.getElementById('page-select');
    let pageKey = sel.value;
    if (!pageKey) return;
    
    // Ensure page_descriptions object exists
    if (!appData.page_descriptions) appData.page_descriptions = {};
    
    let currentDesc = appData.page_descriptions[pageKey] || "";
    let newDesc = prompt(`Edit Meta Description for "${appData.page_titles[pageKey] || pageKey}":\n\n(Leave empty to use global description)`, currentDesc);
    
    if (newDesc !== null) {
        appData.page_descriptions[pageKey] = newDesc;
        saveToHistory();
        saveProject(true);
    }
};

window.duplicatePage = async function() {
    let sel = document.getElementById('page-select');
    let key = sel.value;
    if(!key) return;
    let newName = prompt('Duplicate page name (display name):', (appData.page_titles[key] || key) + ' Copy');
    if(!newName) return;
    // generate slug for key - removes accents properly
    let slug = slugifyPageName(newName);
    
    // Ensure slug is unique - if conflict, append number
    let finalSlug = slug;
    let counter = 1;
    while(appData.pages[finalSlug]) {
        finalSlug = slug + '-' + counter;
        counter++;
    }
    slug = finalSlug;
    // deep copy blocks: create new block ids and copy block data
    let oldBlocks = appData.pages[key] || [];
    let newBlockIds = [];
    for(let bid of oldBlocks) {
        let block = appData.blocks_data[bid];
        if(!block) continue;
        // generate new id
        let newId = block.type + '_' + (Date.now().toString(36)) + '_' + Math.floor(Math.random()*10000).toString(36);
        // shallow copy of data
        let copy = JSON.parse(JSON.stringify(block));
        copy.id = newId;
        appData.blocks_data[newId] = copy;
        newBlockIds.push(newId);
    }
    appData.pages[slug] = newBlockIds;
    if(!appData.page_titles) appData.page_titles = {};
    appData.page_titles[slug] = newName;
    if(!appData.page_hidden) appData.page_hidden = {};
    appData.page_hidden[slug] = false;
    // Copy page description or initialize empty
    if(!appData.page_descriptions) appData.page_descriptions = {};
    appData.page_descriptions[slug] = appData.page_descriptions[key] || "";
    // Copy language visibility
    if(!appData.page_lang_visibility) appData.page_lang_visibility = {};
    if(appData.page_lang_visibility[key]) {
        appData.page_lang_visibility[slug] = JSON.parse(JSON.stringify(appData.page_lang_visibility[key]));
    }
    // Add new page to page_order
    if(!appData.page_order) appData.page_order = Object.keys(appData.pages);
    if(!appData.page_order.includes(slug)) {
        appData.page_order.push(slug);
    }
    await eel.save_project_data(appData)();
    await eel.update_page_order(appData.page_order)();
    updatePageSelector();
    document.getElementById('page-select').value = slug;
    renderCanvas();
};

window.toggleHidePage = async function() {
    let sel = document.getElementById('page-select');
    let key = sel.value;
    if(!key) return;
    if(!appData.page_hidden) appData.page_hidden = {};
    appData.page_hidden[key] = !appData.page_hidden[key];
    await eel.save_project_data(appData)();
    // Reload appData from server to ensure consistency
    appData = await eel.get_project_data()();
    updatePageSelector();
    document.getElementById('page-select').value = key;
    renderCanvas();
};

/**
 * Edit which languages a page is visible in.
 * Uses appData.page_lang_visibility[pageKey] = [langCode1, langCode2, ...]
 * If empty or missing, the page is visible in ALL languages.
 */
window.editPageLangVisibility = async function() {
    let sel = document.getElementById('page-select');
    let pageKey = sel.value;
    if (!pageKey) return;

    // Get project languages
    let langs;
    try {
        langs = await eel.get_project_languages()();
    } catch(e) {
        alert('Failed to load languages');
        return;
    }
    const entries = langs.entries || [];
    const defaultLang = langs.default || 'hun';

    // Build full language list (default + additional)
    let allLangs = [];
    let defaultLabel = defaultLang.toUpperCase();
    for (const e of entries) {
        if (e.code === defaultLang) {
            defaultLabel = e.label || defaultLabel;
            break;
        }
    }
    allLangs.push({ code: defaultLang, label: defaultLabel });
    for (const e of entries) {
        if (e.code !== defaultLang) {
            allLangs.push({ code: e.code, label: e.label || e.code });
        }
    }

    if (allLangs.length < 2) {
        alert(t('page_lang_no_languages'));
        return;
    }

    // Load per-language page titles (default title + language-specific overrides)
    let langTitles = {};
    let defaultTitle = (appData.page_titles && appData.page_titles[pageKey]) || pageKey;
    try {
        const titleResp = await eel.get_page_titles_for_languages(pageKey)();
        if (titleResp && titleResp.success) {
            langTitles = titleResp.titles_by_lang || {};
            defaultTitle = titleResp.default_title || defaultTitle;
        }
    } catch (e) {
        console.warn('[Lang Visibility] Failed to load per-language page titles:', e);
    }

    // Current visibility setting
    if (!appData.page_lang_visibility) appData.page_lang_visibility = {};
    let current = appData.page_lang_visibility[pageKey] || []; // empty = all

    // Remove any existing overlay
    let existing = document.getElementById('page-lang-visibility-overlay');
    if (existing) existing.remove();

    // Build modal
    let overlay = document.createElement('div');
    overlay.id = 'page-lang-visibility-overlay';
    overlay.style.cssText = 'position:fixed;top:0;left:0;width:100%;height:100%;background:rgba(0,0,0,0.5);display:flex;align-items:center;justify-content:center;z-index:10000;';

    let pageName = appData.page_titles[pageKey] || pageKey;
    let modal = document.createElement('div');
    modal.style.cssText = 'background:#1e1e1e;color:#ccc;border-radius:10px;padding:24px 28px;min-width:360px;max-width:560px;max-height:82vh;overflow:auto;box-shadow:0 8px 32px rgba(0,0,0,0.5);';
    modal.innerHTML = `
        <h3 style="margin:0 0 6px 0;font-size:15px;">${t('page_lang_visibility_title')}</h3>
        <p style="margin:0 0 14px 0;font-size:12px;color:#888;">${t('page_lang_visibility_hint').replace('{page}', pageName)}</p>

        <div style="font-size:12px;color:#9aa0a6;margin:0 0 8px 2px;">${t('page_lang_names_section') || 'Display names by language'}</div>
        <div id="plv-name-inputs" style="display:flex;flex-direction:column;gap:8px;margin-bottom:14px;"></div>

        <div style="font-size:12px;color:#9aa0a6;margin:0 0 8px 2px;">${t('page_lang_visibility_only_section') || 'Visible in languages'}</div>
        <div id="plv-checkboxes" style="display:flex;flex-direction:column;gap:6px;margin-bottom:16px;"></div>

        <label style="display:flex;align-items:flex-start;gap:8px;margin:0 0 14px 0;font-size:12px;color:#aaa;">
            <input id="plv-sync-url" type="checkbox" checked style="margin-top:2px;accent-color:#1976d2;">
            <span>
                ${t('page_lang_sync_url') || 'Gently sync URL with default language name'}
                <div id="plv-sync-url-preview" style="font-size:11px;color:#777;margin-top:4px;"></div>
            </span>
        </label>

        <div style="display:flex;gap:8px;justify-content:flex-end;">
            <button id="plv-cancel" style="padding:6px 16px;border:1px solid #555;background:transparent;color:#ccc;border-radius:6px;cursor:pointer;">${t('cancel')}</button>
            <button id="plv-save" style="padding:6px 16px;border:none;background:#1976d2;color:white;border-radius:6px;cursor:pointer;">${t('save')}</button>
        </div>
    `;

    overlay.appendChild(modal);
    document.body.appendChild(overlay);

    // Render name inputs
    const namesContainer = modal.querySelector('#plv-name-inputs');
    for (const lang of allLangs) {
        const row = document.createElement('div');
        row.style.cssText = 'display:grid;grid-template-columns:120px 1fr;gap:8px;align-items:center;';
        const currentVal = (langTitles && typeof langTitles[lang.code] === 'string')
            ? langTitles[lang.code]
            : (lang.code === defaultLang ? defaultTitle : '');
        const placeholder = lang.code === defaultLang
            ? (defaultTitle || pageName)
            : `${defaultTitle || pageName}`;
        row.innerHTML = `
            <div style="font-size:12px;color:#bbb;display:flex;align-items:center;gap:6px;">
                <span>${lang.label}</span>
                <span style="color:#666;">(${lang.code})</span>
            </div>
            <input
                type="text"
                data-lang-code="${lang.code}"
                value="${(currentVal || '').replace(/&/g, '&amp;').replace(/"/g, '&quot;').replace(/</g, '&lt;')}"
                placeholder="${(placeholder || '').replace(/&/g, '&amp;').replace(/"/g, '&quot;').replace(/</g, '&lt;')}"
                style="width:100%;padding:7px 10px;border:1px solid #444;border-radius:6px;background:#111;color:#ddd;outline:none;"
            >
        `;
        namesContainer.appendChild(row);
    }

    // Render checkboxes
    let cbContainer = modal.querySelector('#plv-checkboxes');
    for (const lang of allLangs) {
        let checked = current.length === 0 || current.includes(lang.code);
        let row = document.createElement('label');
        row.style.cssText = 'display:flex;align-items:center;gap:8px;cursor:pointer;font-size:13px;padding:4px 8px;border-radius:4px;background:rgba(255,255,255,0.05);';
        row.innerHTML = `<input type="checkbox" value="${lang.code}" ${checked ? 'checked' : ''} style="accent-color:#1976d2;width:16px;height:16px;"> ${lang.label} <span style="color:#666;font-size:11px;">(${lang.code})</span>`;
        cbContainer.appendChild(row);
    }

    // URL preview for gentle sync mode (based on default language page name)
    const urlPreview = modal.querySelector('#plv-sync-url-preview');
    const defaultNameInput = namesContainer.querySelector(`input[data-lang-code="${defaultLang}"]`);
    const syncUrlCheckbox = modal.querySelector('#plv-sync-url');
    const refreshUrlPreview = () => {
        if (!urlPreview || !defaultNameInput || !syncUrlCheckbox) return;
        const nextName = (defaultNameInput.value || '').trim() || defaultTitle || pageName;
        const nextSlug = slugifyPageName(nextName);
        if (!syncUrlCheckbox.checked) {
            urlPreview.textContent = t('page_lang_sync_url_off') || 'URL sync disabled.';
            return;
        }
        if (pageKey === 'index') {
            urlPreview.textContent = t('page_lang_sync_url_index') || 'Index page URL is fixed.';
            return;
        }
        if (!nextSlug) {
            urlPreview.textContent = t('page_lang_sync_url_empty') || 'No valid URL can be generated from this name.';
            return;
        }
        if (nextSlug === pageKey) {
            urlPreview.textContent = `${t('page_lang_sync_url_same') || 'URL unchanged:'} ${pageKey}.html`;
            return;
        }
        urlPreview.textContent = `${t('page_lang_sync_url_preview') || 'If safe, URL becomes:'} ${nextSlug}.html`;
    };
    if (defaultNameInput) {
        defaultNameInput.addEventListener('input', refreshUrlPreview);
    }
    if (syncUrlCheckbox) {
        syncUrlCheckbox.addEventListener('change', refreshUrlPreview);
    }
    refreshUrlPreview();

    // Cancel
    modal.querySelector('#plv-cancel').onclick = () => overlay.remove();
    overlay.onclick = (e) => { if (e.target === overlay) overlay.remove(); };

    // Save
    modal.querySelector('#plv-save').onclick = async () => {
        try {
            let checkboxes = cbContainer.querySelectorAll('input[type="checkbox"]');
            let selected = [];
            checkboxes.forEach(cb => { if (cb.checked) selected.push(cb.value); });

            // Empty array means "all languages" on backend as well.
            if (selected.length === allLangs.length || selected.length === 0) {
                selected = [];
            }

            const titleInputs = namesContainer.querySelectorAll('input[data-lang-code]');
            const titlesByLang = {};
            titleInputs.forEach(inp => {
                const code = inp.getAttribute('data-lang-code');
                if (!code) return;
                titlesByLang[code] = (inp.value || '').trim();
            });

            const syncUrl = !!(syncUrlCheckbox && syncUrlCheckbox.checked);
            const resp = await eel.save_page_language_settings(pageKey, {
                visibility: selected,
                titles_by_lang: titlesByLang,
                sync_url: syncUrl
            })();

            if (!resp || !resp.success) {
                throw new Error((resp && resp.message) ? resp.message : 'Failed to save page language settings');
            }

            const newPageKey = resp.page_key || pageKey;

            // IMPORTANT: keep the current editing language context after saving.
            // If we always reload base data here while editing a non-default language,
            // the next save can accidentally push default text into the language overlay.
            if (window._currentContentLang) {
                const langData = await eel.get_language_project_data()();
                if (langData && typeof langData === 'object') {
                    appData = langData;
                    window._langOverlayLoaded = true;
                } else {
                    appData = await eel.get_project_data()();
                    window._langOverlayLoaded = false;
                }
            } else {
                appData = await eel.get_project_data()();
                window._langOverlayLoaded = false;
            }

            updatePageSelector();
            document.getElementById('page-select').value = newPageKey;
            overlay.remove();

            if (window.showToast) {
                const savedMsg = resp.url_updated
                    ? (t('page_lang_visibility_saved_with_url') || 'Page language settings saved, URL updated')
                    : t('page_lang_visibility_saved');
                showToast(savedMsg, 2200, 'success');
            }
            renderCanvas();
        } catch (e) {
            console.error('[Lang Visibility] Save failed:', e);
            alert((t('page_lang_visibility_save_failed') || 'Failed to save page language settings') + ': ' + (e && e.message ? e.message : e));
        }
    };
};

window.movePageUp = async function() {
    let sel = document.getElementById('page-select');
    let key = sel.value;
    if(!key) return;
    if(!appData.page_order) appData.page_order = Object.keys(appData.pages);
    let idx = appData.page_order.indexOf(key);
    if(idx > 0) {
        // Swap with previous
        let temp = appData.page_order[idx - 1];
        appData.page_order[idx - 1] = appData.page_order[idx];
        appData.page_order[idx] = temp;
        await eel.update_page_order(appData.page_order)();
        // Reload appData to ensure consistency
        appData = await eel.get_project_data()();
        updatePageSelector();
        document.getElementById('page-select').value = key;
        renderCanvas();
    }
};

window.movePageDown = async function() {
    let sel = document.getElementById('page-select');
    let key = sel.value;
    if(!key) return;
    if(!appData.page_order) appData.page_order = Object.keys(appData.pages);
    let idx = appData.page_order.indexOf(key);
    if(idx < appData.page_order.length - 1) {
        // Swap with next
        let temp = appData.page_order[idx + 1];
        appData.page_order[idx + 1] = appData.page_order[idx];
        appData.page_order[idx] = temp;
        await eel.update_page_order(appData.page_order)();
        // Reload appData to ensure consistency
        appData = await eel.get_project_data()();
        updatePageSelector();
        document.getElementById('page-select').value = key;
        renderCanvas();
    }
};
window.addPage = async () => {
    let n = prompt("Name?");
    if(n) {
        let r = await eel.add_new_page(n)();
        if(r.success) { 
            appData = await eel.get_project_data()(); 
            // Ensure new page is added to page_order
            if(!appData.page_order) appData.page_order = Object.keys(appData.pages);
            if(!appData.page_order.includes(r.key)) {
                appData.page_order.push(r.key);
                await eel.update_page_order(appData.page_order)();
            }
            // Ensure page_descriptions is initialized for new page
            if(!appData.page_descriptions) appData.page_descriptions = {};
            if(!appData.page_descriptions[r.key]) appData.page_descriptions[r.key] = "";
            updatePageSelector(); 
            document.getElementById('page-select').value = r.key; 
            saveToHistory(); 
            renderCanvas(); 
        }
    }
};
window.deletePage = async () => {
    let p = document.getElementById('page-select').value;
    if(confirm("Delete "+p+"?")) {
        let r = await eel.delete_page(p)();
        if(r.success) { saveToHistory(); loadProject(); }
    }
};
window.showFaq = function() {
    let iframe = document.getElementById('preview-frame');
    iframe.src = "faq.html";
    // Show back-to-editor button in toolbar
    let backBtn = document.getElementById('back-to-editor-btn');
    if (backBtn) backBtn.style.display = 'inline-flex';
};

window.backToEditor = function() {
    // Hide back-to-editor button and re-render canvas
    let backBtn = document.getElementById('back-to-editor-btn');
    if (backBtn) backBtn.style.display = 'none';
    renderCanvas();
};

// ============= BLOCK ICON GENERATOR =============
function getBlockIconSVG(blockType) {
    const size = 40;
    const svgNS = 'http://www.w3.org/2000/svg';
    
    let svgContent = '';
    
    switch(blockType) {
        case 'hero':
            svgContent = `<svg width="${size}" height="${size}" viewBox="0 0 60 60" xmlns="${svgNS}"><rect width="60" height="60" fill="#ff9800" stroke="#fff" stroke-width="2"/><rect x="4" y="4" width="52" height="30" fill="rgba(255,255,255,0.3)" stroke="#fff" stroke-width="1.5"/><circle cx="24" cy="15" r="4" fill="#fff" opacity="0.6"/><line x1="4" y1="48" x2="56" y2="48" stroke="#fff" stroke-width="1"/><line x1="4" y1="54" x2="30" y2="54" stroke="#fff" stroke-width="1"/></svg>`;
            break;
        case 'text':
            svgContent = `<svg width="${size}" height="${size}" viewBox="0 0 60 60" xmlns="${svgNS}"><rect width="60" height="60" fill="#1976d2" stroke="#fff" stroke-width="2"/><line x1="4" y1="8" x2="56" y2="8" stroke="#fff" stroke-width="1.5"/><line x1="4" y1="16" x2="56" y2="16" stroke="#fff" stroke-width="1"/><line x1="4" y1="23" x2="50" y2="23" stroke="#fff" stroke-width="1"/><line x1="4" y1="30" x2="56" y2="30" stroke="#fff" stroke-width="1"/><line x1="4" y1="37" x2="48" y2="37" stroke="#fff" stroke-width="1"/><line x1="4" y1="44" x2="56" y2="44" stroke="#fff" stroke-width="1"/><line x1="4" y1="51" x2="42" y2="51" stroke="#fff" stroke-width="1"/></svg>`;
            break;
        case 'image':
            svgContent = `<svg width="${size}" height="${size}" viewBox="0 0 60 60" xmlns="${svgNS}"><rect width="60" height="60" fill="#4caf50" stroke="#fff" stroke-width="2"/><rect x="4" y="4" width="52" height="52" fill="rgba(255,255,255,0.2)" stroke="#fff" stroke-width="1.5"/><circle cx="18" cy="16" r="3" fill="#fff" opacity="0.8"/><path d="M 4 40 L 24 20 L 40 32 L 56 16" stroke="#fff" stroke-width="1.5" fill="none"/></svg>`;
            break;
        case 'image_text':
            svgContent = `<svg width="${size}" height="${size}" viewBox="0 0 60 60" xmlns="${svgNS}"><rect width="60" height="60" fill="#9c27b0" stroke="#fff" stroke-width="2"/><rect x="4" y="4" width="24" height="52" fill="rgba(255,255,255,0.3)" stroke="#fff" stroke-width="1.5"/><line x1="34" y1="8" x2="56" y2="8" stroke="#fff" stroke-width="1"/><line x1="34" y1="16" x2="56" y2="16" stroke="#fff" stroke-width="1"/><line x1="34" y1="24" x2="54" y2="24" stroke="#fff" stroke-width="1"/><line x1="34" y1="40" x2="56" y2="40" stroke="#fff" stroke-width="1"/><line x1="34" y1="48" x2="50" y2="48" stroke="#fff" stroke-width="1"/></svg>`;
            break;
        case 'image_text_left':
            svgContent = `<svg width="${size}" height="${size}" viewBox="0 0 60 60" xmlns="${svgNS}"><rect width="60" height="60" fill="#9c27b0" stroke="#fff" stroke-width="2"/><line x1="4" y1="8" x2="26" y2="8" stroke="#fff" stroke-width="1"/><line x1="4" y1="16" x2="26" y2="16" stroke="#fff" stroke-width="1"/><line x1="4" y1="24" x2="24" y2="24" stroke="#fff" stroke-width="1"/><line x1="4" y1="40" x2="26" y2="40" stroke="#fff" stroke-width="1"/><line x1="4" y1="48" x2="20" y2="48" stroke="#fff" stroke-width="1"/><rect x="32" y="4" width="24" height="52" fill="rgba(255,255,255,0.3)" stroke="#fff" stroke-width="1.5"/></svg>`;
            break;
        case 'gallery':
            svgContent = `<svg width="${size}" height="${size}" viewBox="0 0 60 60" xmlns="${svgNS}"><rect width="60" height="60" fill="#2196f3" stroke="#fff" stroke-width="2"/><rect x="4" y="4" width="24" height="24" fill="rgba(255,255,255,0.3)" stroke="#fff" stroke-width="1.5"/><rect x="32" y="4" width="24" height="24" fill="rgba(255,255,255,0.3)" stroke="#fff" stroke-width="1.5"/><rect x="4" y="32" width="24" height="24" fill="rgba(255,255,255,0.3)" stroke="#fff" stroke-width="1.5"/><rect x="32" y="32" width="24" height="24" fill="rgba(255,255,255,0.3)" stroke="#fff" stroke-width="1.5"/></svg>`;
            break;
        case 'photo_grid':
            svgContent = `<svg width="${size}" height="${size}" viewBox="0 0 60 60" xmlns="${svgNS}"><rect width="60" height="60" fill="#00bcd4" stroke="#fff" stroke-width="2"/><rect x="4" y="4" width="18" height="18" fill="rgba(255,255,255,0.3)" stroke="#fff" stroke-width="1.5"/><rect x="26" y="4" width="18" height="18" fill="rgba(255,255,255,0.3)" stroke="#fff" stroke-width="1.5"/><rect x="48" y="4" width="8" height="18" fill="rgba(255,255,255,0.3)" stroke="#fff" stroke-width="1.5"/><rect x="4" y="26" width="18" height="18" fill="rgba(255,255,255,0.3)" stroke="#fff" stroke-width="1.5"/><rect x="26" y="26" width="18" height="18" fill="rgba(255,255,255,0.3)" stroke="#fff" stroke-width="1.5"/><rect x="48" y="26" width="8" height="18" fill="rgba(255,255,255,0.3)" stroke="#fff" stroke-width="1.5"/><rect x="4" y="48" width="52" height="8" fill="rgba(255,255,255,0.3)" stroke="#fff" stroke-width="1.5"/></svg>`;
            break;
        case 'video':
            svgContent = `<svg width="${size}" height="${size}" viewBox="0 0 60 60" xmlns="${svgNS}"><rect width="60" height="60" fill="#ff6b6b" stroke="#fff" stroke-width="2"/><rect x="8" y="12" width="44" height="36" fill="rgba(255,255,255,0.2)" stroke="#fff" stroke-width="1.5"/><polygon points="24,24 24,48 42,36" fill="#fff" opacity="0.9"/></svg>`;
            break;
        case 'html':
            svgContent = `<svg width="${size}" height="${size}" viewBox="0 0 60 60" xmlns="${svgNS}"><rect width="60" height="60" fill="#e91e63" stroke="#fff" stroke-width="2"/><text x="30" y="36" font-family="monospace" font-size="20" font-weight="bold" fill="#fff" text-anchor="middle">&lt;/&gt;</text></svg>`;
            break;
        case 'button':
            svgContent = `<svg width="${size}" height="${size}" viewBox="0 0 60 60" xmlns="${svgNS}"><rect width="60" height="60" fill="#4caf50" stroke="#fff" stroke-width="2"/><rect x="12" y="22" width="36" height="16" fill="rgba(255,255,255,0.3)" stroke="#fff" stroke-width="2" rx="4"/><text x="30" y="34" font-family="Arial" font-size="10" fill="#fff" text-anchor="middle" dominant-baseline="middle">Button</text></svg>`;
            break;
        case 'container':
            svgContent = `<svg width="${size}" height="${size}" viewBox="0 0 60 60" xmlns="${svgNS}"><rect width="60" height="60" fill="#607d8b" stroke="#fff" stroke-width="2"/><rect x="4" y="6" width="10" height="48" fill="rgba(255,255,255,0.3)" stroke="#fff" stroke-width="1.5"/><rect x="18" y="6" width="10" height="48" fill="rgba(255,255,255,0.3)" stroke="#fff" stroke-width="1.5"/><rect x="32" y="6" width="10" height="48" fill="rgba(255,255,255,0.3)" stroke="#fff" stroke-width="1.5"/><rect x="46" y="6" width="10" height="48" fill="rgba(255,255,255,0.3)" stroke="#fff" stroke-width="1.5"/></svg>`;
            break;
        default:
            svgContent = `<svg width="${size}" height="${size}" viewBox="0 0 60 60" xmlns="${svgNS}"><rect width="60" height="60" fill="#666" stroke="#fff" stroke-width="2"/><text x="30" y="30" font-family="Arial" font-size="12" fill="#fff" text-anchor="middle" dominant-baseline="middle">Block</text></svg>`;
    }
    
    return svgContent;
}

// Initialize block buttons with icons
function initializeBlockButtons() {
    const blockButtons = document.querySelectorAll('.block-btn:not([data-icons-initialized])');
    blockButtons.forEach(btn => {
        const blockType = btn.dataset.blockType;
        
        // Save the original text before clearing
        const originalText = btn.textContent.trim();
        
        // Clear existing content except for data attributes
        btn.innerHTML = '';
        
        // Add icon
        const icon = document.createElement('div');
        icon.style.cssText = 'display: flex; align-items: center; justify-content: center; width: 50px; height: 50px; margin-bottom: 6px;';
        icon.innerHTML = getBlockIconSVG(blockType);
        btn.appendChild(icon);
        
        // Create a label wrapper for the text
        const labelDiv = document.createElement('div');
        labelDiv.style.cssText = 'display: flex; flex-direction: column; align-items: center; text-align: center; gap: 4px;';
        
        // Use the original text we captured
        const span = document.createElement('span');
        span.textContent = originalText || blockType;
        span.style.cssText = 'line-height: 1.2; word-break: break-word;';
        labelDiv.appendChild(span);
        
        btn.appendChild(labelDiv);
        btn.style.cssText = 'display: flex; flex-direction: column; align-items: center; justify-content: center;';
        
        // Mark as initialized to prevent re-processing
        btn.setAttribute('data-icons-initialized', 'true');
    });
}

// Watch for DOM changes to block buttons and restore icons if they get cleared
function setupBlockButtonObserver() {
    const blockList = document.querySelector('.block-list');
    if (!blockList) return;
    
    const observer = new MutationObserver((mutations) => {
        // Check if any button lost its icons
        const buttons = document.querySelectorAll('.block-btn[data-icons-initialized]');
        buttons.forEach(btn => {
            // Check if button still has its icon div
            if (!btn.querySelector('[style*="width: 50px"]')) {
                // Icon is missing, re-initialize
                btn.removeAttribute('data-icons-initialized');
                initializeBlockButtons();
            }
        });
    });
    
    observer.observe(blockList, {
        childList: true,
        subtree: true,
        attributes: true,
        attributeFilter: ['innerHTML'],
        characterData: true
    });
}

// Re-initialize buttons when settings/dialogs close
function ensureBlockButtonsInitialized() {
    // Check if any buttons are missing icons
    const uninitializedButtons = document.querySelectorAll('.block-btn:not([data-icons-initialized])');
    if (uninitializedButtons.length > 0) {
        initializeBlockButtons();
    }
}

// Watch for visibility changes or dialog opens/closes
window.addEventListener('focus', ensureBlockButtonsInitialized);
document.addEventListener('visibilitychange', ensureBlockButtonsInitialized);

// ============= DRAG & DROP BLOCK SYSTEM =============
// Store drag data for block toolbar items
let draggedBlockType = null;
let draggedBlockElement = null;

// Handle dragstart on block buttons
document.addEventListener('dragstart', function(e) {
    // Check if it's a block button
    if (e.target.classList && e.target.classList.contains('block-btn')) {
        draggedBlockType = e.target.dataset.blockType;
        draggedBlockElement = e.target;
        e.dataTransfer.effectAllowed = 'copy';
        e.dataTransfer.setData('text/html', e.target.innerHTML);
        
        // Set a drag image with the block icon
        if (e.dataTransfer.setDragImage) {
            const dragImage = document.createElement('div');
            dragImage.style.cssText = 'display: flex; align-items: center; gap: 12px; background: #1976d2; color: white; padding: 12px 16px; border-radius: 6px; font-size: 14px; font-weight: bold; box-shadow: 0 2px 8px rgba(0,0,0,0.3);';
            
            const iconDiv = document.createElement('div');
            iconDiv.style.cssText = 'width: 48px; height: 48px; display: flex; align-items: center; justify-content: center;';
            iconDiv.innerHTML = getBlockIconSVG(draggedBlockType);
            
            const label = document.createElement('span');
            label.textContent = e.target.textContent.trim();
            
            dragImage.appendChild(iconDiv);
            dragImage.appendChild(label);
            document.body.appendChild(dragImage);
            
            e.dataTransfer.setDragImage(dragImage, 0, 0);
            setTimeout(() => document.body.removeChild(dragImage), 0);
        }
    }
}, true);

// Clean up after drag
document.addEventListener('dragend', function(e) {
    if (e.target.classList && e.target.classList.contains('block-btn')) {
        draggedBlockType = null;
        draggedBlockElement = null;
    }
}, true);

// Function to setup drop zones in the iframe
window.setupBlockDropZones = function() {
    const iframe = document.getElementById('preview-frame');
    if (!iframe || !iframe.contentDocument) return;
    
    const doc = iframe.contentDocument;
    const container = doc.getElementById('sortable-root');
    if (!container) return;
    
    let blockDropHandled = false; // Flag to prevent container from handling block drops
    
    // Add dragover handler to container
    container.addEventListener('dragover', function(e) {
        if (draggedBlockType) {
            e.preventDefault();
            e.dataTransfer.dropEffect = 'copy';
            container.style.opacity = '0.85';
            
            // Remove drop-zone from all blocks and add to target block
            const blocks = container.querySelectorAll('.editor-block');
            blocks.forEach(b => b.classList.remove('drop-zone'));
            const targetBlock = e.target.closest('.editor-block');
            if (targetBlock) {
                targetBlock.classList.add('drop-zone');
            }
        }
    });
    
    container.addEventListener('dragleave', function(e) {
        if (e.target === container) {
            container.style.opacity = '1';
            const blocks = container.querySelectorAll('.editor-block');
            blocks.forEach(b => b.classList.remove('drop-zone'));
        }
    });
    
    // Handle drops with event delegation
    container.addEventListener('drop', function(e) {
        if (!draggedBlockType) return;
        e.preventDefault();
        e.stopPropagation();
        
        container.style.opacity = '1';
        const blocks = container.querySelectorAll('.editor-block');
        blocks.forEach(b => b.classList.remove('drop-zone'));
        
        // Check if drop was on a block
        const targetBlock = e.target.closest('.editor-block');
        
        if (targetBlock) {
            // Drop was on a block - insert at that position
            const blockId = targetBlock.dataset.id;
            const page = document.getElementById('page-select').value;
            const currentIndex = appData.pages[page].indexOf(blockId);
            
            if (currentIndex >= 0) {
                // Determine if dropping above or below based on Y coordinate
                const rect = targetBlock.getBoundingClientRect();
                const midpoint = rect.top + rect.height / 2;
                const insertIndex = e.clientY < midpoint ? currentIndex : currentIndex + 1;
                addBlock(draggedBlockType, insertIndex);
            } else {
                addBlock(draggedBlockType);
            }
        } else {
            // Drop was on empty container space - add at end
            addBlock(draggedBlockType);
        }
        
        draggedBlockType = null;
    });
};

// Call this after rendering the canvas
const originalRenderCanvas = window.renderCanvas;
window.renderCanvas = function(...args) {
    const result = originalRenderCanvas.apply(window, args);
    // Setup drop zones after canvas is rendered
    setTimeout(setupBlockDropZones, 100);
    return result;
};

// === MULTI-LANGUAGE CONTENT EDITING ===

// Track current content language being edited
window._currentContentLang = ''; // empty = default language
window._langOverlayLoaded = false; // true only after a successful non-default language load

/**
 * Initialize the content language switcher in the toolbar.
 * Called after loading project data.
 */
window.initLanguageSwitcher = async function() {
    const group = document.getElementById('lang-switcher-group');
    const select = document.getElementById('content-lang-select');
    if (!group || !select) return;
    
    try {
        const langs = await eel.get_project_languages()();
        const entries = langs.entries || [];
        const defaultLang = langs.default || 'hun';
        
        // Always show the switcher group (so user can add languages)
        group.style.display = 'flex';
        
        // Build options
        select.innerHTML = '';
        
        // Default language option
        const defaultOpt = document.createElement('option');
        defaultOpt.value = '';
        let defaultLabel = defaultLang.toUpperCase();
        // Try to find label from entries
        for (const e of entries) {
            if (e.code === defaultLang) {
                defaultLabel = e.label || defaultLabel;
                break;
            }
        }
        defaultOpt.textContent = `${defaultLabel} ${t('lang_default_tag')}`;
        select.appendChild(defaultOpt);
        
        // Additional language options
        for (const entry of entries) {
            if (entry.code === defaultLang) continue;
            const opt = document.createElement('option');
            opt.value = entry.code;
            opt.textContent = `${entry.label || entry.code} (${entry.directory || '/'})`;
            select.appendChild(opt);
        }
        
        // Restore current selection
        select.value = window._currentContentLang || '';
        
    } catch (e) {
        console.warn('[Lang] Failed to init language switcher:', e);
        group.style.display = 'none';
    }
};

/**
 * Switch the content language being edited.
 * When switching away from default, loads language overlay data.
 * When switching back to default, loads base project data.
 */
window.switchContentLanguage = async function(langCode) {
    try {
        // Save current state first if dirty
        if (isDirty) {
            await window.saveProject(true);
        }
        
        window._currentContentLang = langCode || '';
        window._langOverlayLoaded = false;
        
        // Tell backend which language we're editing
        await eel.set_current_language(langCode || '')();
        
        if (langCode) {
            // Load merged data for this language
            const langData = await eel.get_language_project_data()();
            if (langData && typeof langData === 'object' && langData.pages && langData.blocks_data) {
                // Preserve non-translatable fields from current appData
                const preserved = ['pages', 'page_order', 'page_hidden', 'page_lang_visibility', 'theme', 'font',
                    'languages', 'custom_scripts', 'google_analytics', 'ignore_files',
                    'project_categories', 'project_order', 'custom_styles', 'border_color',
                    'favicon', 'site_url'];
                for (const key of preserved) {
                    if (appData[key] !== undefined) {
                        langData[key] = appData[key];
                    }
                }
                appData = langData;
                window._langOverlayLoaded = true;
            } else {
                throw new Error('Language overlay data is missing or invalid');
            }
        } else {
            // Switch back to default - reload base data
            appData = await eel.get_project_data()();
            window._langOverlayLoaded = false;
        }
        
        // Remove ai_settings from appData
        if (appData.ai_settings) delete appData.ai_settings;
        
        updatePageSelector();
        renderCanvas();
        setSaveButtonState(true);
        
        // Visual indicator
        const select = document.getElementById('content-lang-select');
        if (select) {
            select.style.borderColor = langCode ? '#ff9800' : '';
            select.title = langCode ? t('lang_editing_overlay').replace('{lang}', langCode) : t('lang_editing_default');
        }
        
        if (langCode) {
            showToast(`${t('lang_switched_to')}${langCode}`, 3000);
        } else {
            showToast(t('lang_switched_default'), 2000);
        }
    } catch (e) {
        console.error('[Lang] Error switching language:', e);
        // Fail-safe: drop back to default language mode to avoid bad overlay saves.
        window._currentContentLang = '';
        window._langOverlayLoaded = false;
        try { await eel.set_current_language('')(); } catch (_) { /* ignore */ }
        alert('Failed to switch language: ' + e);
    }
};

/**
 * Override saveProject to handle language-aware saving.
 * When editing a non-default language, extract text diffs and save as overlay.
 */
const _originalSaveProject = window.saveProject;
window.saveProject = async function(silent) {
    if (window._currentContentLang) {
        // Editing a non-default language - save overlay
        try {
            if (!window._langOverlayLoaded) {
                const msg = 'Language overlay is not fully loaded yet. Save blocked for safety.';
                console.warn('[Lang] ' + msg);
                if (!silent && window.showToast) {
                    showToast(msg, 3500, 'error');
                }
                return;
            }

            const btn = getSaveButton();
            const { defaultLabel } = getSaveLabels();
            if (btn) { btn.disabled = true; btn.textContent = defaultLabel; }
            
            // Extract translatable fields as overrides
            const overrides = {};
            const TEXT_FIELDS = ['site_title', 'site_subtitle', 'site_description', 'footer_text'];
            for (const field of TEXT_FIELDS) {
                if (appData[field] !== undefined) {
                    overrides[field] = appData[field];
                }
            }
            if (appData.page_titles) overrides.page_titles = appData.page_titles;
            if (appData.page_descriptions) overrides.page_descriptions = appData.page_descriptions;
            
            // Extract block text overrides (only text-related properties)
            const BLOCK_TEXT_KEYS = ['title', 'subtitle', 'text', 'description', 'alt', 'caption',
                'button_text', 'html_code', 'overlay_text', 'content'];
            if (appData.blocks_data) {
                overrides.blocks_data = {};
                for (const [blockId, blockData] of Object.entries(appData.blocks_data)) {
                    const blockOverride = {};
                    let hasOverride = false;
                    for (const key of BLOCK_TEXT_KEYS) {
                        if (blockData[key] !== undefined) {
                            blockOverride[key] = blockData[key];
                            hasOverride = true;
                        }
                    }

                    // Backward compatibility: html blocks may store content under 'content'.
                    if (blockData.type === 'html' && blockData.html_code === undefined && blockData.content !== undefined) {
                        blockOverride.html_code = blockData.content;
                        hasOverride = true;
                    }

                    // Also save image alt/caption data in galleries
                    if (blockData.image_titles) {
                        blockOverride.image_titles = blockData.image_titles;
                        hasOverride = true;
                    }
                    if (blockData.image_alts) {
                        blockOverride.image_alts = blockData.image_alts;
                        hasOverride = true;
                    }
                    if (blockData.gallery_titles) {
                        blockOverride.gallery_titles = blockData.gallery_titles;
                        hasOverride = true;
                    }

                    // Container/flex item nested text fields (kept by item index).
                    if (Array.isArray(blockData.container_items)) {
                        const itemTextOverrides = {};
                        blockData.container_items.forEach((item, idx) => {
                            if (!item || typeof item !== 'object') return;
                            const perItem = {};
                            ['title', 'content', 'text', 'description', 'alt', 'caption', 'button_text'].forEach((k) => {
                                if (item[k] !== undefined) {
                                    perItem[k] = item[k];
                                }
                            });
                            if (Object.keys(perItem).length > 0) {
                                itemTextOverrides[String(idx)] = perItem;
                            }
                        });
                        if (Object.keys(itemTextOverrides).length > 0) {
                            blockOverride.container_items_text = itemTextOverrides;
                            hasOverride = true;
                        }
                    }

                    if (hasOverride) {
                        overrides.blocks_data[blockId] = blockOverride;
                    }
                }
            }
            
            // Save OG settings overrides
            if (appData.og_settings) overrides.og_settings = appData.og_settings;
            if (appData.cookie_banner) overrides.cookie_banner = appData.cookie_banner;
            
            const result = await eel.save_language_override_data(overrides)();
            if (result.success) {
                setSaveButtonState(true);
                if (!silent && window.showToast) {
                    showToast(`${window._currentContentLang} ${t('lang_overlay_saved')}`, 2000, 'success');
                }
            } else {
                if (!silent) alert(t('lang_overlay_save_failed') + ': ' + (result.message || 'Unknown error'));
            }
            
            if (btn) btn.disabled = false;
        } catch (e) {
            console.error('[Lang] Save overlay error:', e);
            if (!silent) alert('Save failed: ' + e);
        }
    } else {
        // Default language - use original save
        return _originalSaveProject(silent);
    }
};

/**
 * Open the Add Language dialog.
 */
/**
 * Open the Language Manager dialog.
 */
window.openAddLanguageDialog = async function() {
    const codeInput = document.getElementById('add-lang-code');
    const dirInput = document.getElementById('add-lang-dir');
    codeInput.value = '';
    document.getElementById('add-lang-label').value = '';
    dirInput.value = '';
    document.getElementById('add-lang-error').style.display = 'none';
    
    // Auto-fill directory from code
    codeInput.oninput = function() {
        const v = codeInput.value.trim().toLowerCase();
        dirInput.value = v ? '/' + v : '';
    };
    
    await _renderLangManagerList();
    
    document.getElementById('add-language-overlay').style.display = 'flex';
};

/**
 * Render the language list inside the Language Manager dialog.
 */
async function _renderLangManagerList() {
    const list = document.getElementById('lang-manager-list');
    if (!list) return;
    
    try {
        const langs = await eel.get_project_languages()();
        const entries = langs.entries || [];
        const defaultCode = langs.default || '';
        
        if (entries.length === 0) {
            list.innerHTML = '<p style="color:#888; font-size:13px; margin:0;">' + t('lang_no_languages') + '</p>';
            return;
        }
        
        // Build header row
        let html = `<div style="display:grid; grid-template-columns: 70px 1fr 110px 60px; gap:6px; font-size:11px; color:#888; padding:0 4px; margin-bottom:4px;">
            <span>${t('lang_code_label')}</span>
            <span>${t('lang_label_label')}</span>
            <span>${t('lang_directory_label')}</span>
            <span></span>
        </div>`;
        
        // Sort: default first, then alphabetical
        const sorted = [...entries].sort((a, b) => {
            if (a.code === defaultCode) return -1;
            if (b.code === defaultCode) return 1;
            return (a.label || a.code).localeCompare(b.label || b.code);
        });
        
        for (const entry of sorted) {
            const isDefault = entry.code === defaultCode;
            const borderColor = isDefault ? '#2a4a2a' : '#2a2a4a';
            const bgColor = isDefault ? '#1a2a1a' : '#1a1a2a';
            const dirValue = entry.directory || '/';
            const defaultBadge = isDefault ? `<span style="font-size:10px; color:#4caf50; margin-left:4px;">${t('lang_default_tag')}</span>` : '';
            const actionBtns = isDefault ? '' : `<button onclick="setDefaultLanguage('${entry.code}')" style="background:#1565c0; color:white; border:none; border-radius:3px; padding:2px 5px; cursor:pointer; font-size:10px;" title="${t('lang_set_default')}">★</button><button onclick="removeLanguage('${entry.code}')" style="background:#d32f2f; color:white; border:none; border-radius:3px; padding:2px 5px; cursor:pointer; font-size:11px; margin-left:3px;" title="${t('lang_remove_confirm').replace('{code}', entry.code)}">✕</button>`;
            const dirHint = isDefault ? ` title="${t('lang_default_root_hint')}"` : '';
            
            html += `<div style="display:grid; grid-template-columns: 70px 1fr 110px 60px; gap:6px; align-items:center; padding:5px 4px; background:${bgColor}; border-radius:4px; border:1px solid ${borderColor}; margin-bottom:3px;" data-lang-code="${entry.code}">
                <span style="font-weight:600; font-size:13px;">${entry.code}${defaultBadge}</span>
                <input type="text" value="${(entry.label || '').replace(/"/g, '&quot;')}" 
                    onchange="updateLangEntry('${entry.code}', 'label', this.value)"
                    style="padding:4px 6px; background:#2b2b2b; border:1px solid #444; color:#eee; border-radius:3px; font-size:12px; width:100%; box-sizing:border-box;">
                <input type="text" value="${dirValue}"${dirHint}
                    onchange="updateLangEntry('${entry.code}', 'directory', this.value)"
                    style="padding:4px 6px; background:#2b2b2b; border:1px solid #444; color:#eee; border-radius:3px; font-size:12px; width:100%; box-sizing:border-box;">
                <div style="text-align:center; white-space:nowrap;">${actionBtns}</div>
            </div>`;
        }
        
        list.innerHTML = html;
    } catch (e) {
        console.warn('[Lang] Error rendering language manager list:', e);
        list.innerHTML = '<p style="color:#ef5350;">Error loading languages</p>';
    }
}

window.closeAddLanguageDialog = function() {
    document.getElementById('add-language-overlay').style.display = 'none';
};

/**
 * Update a single field of an existing language entry.
 */
window.updateLangEntry = async function(code, field, value) {
    try {
        const params = {};
        if (field === 'label') params.label = value;
        else if (field === 'directory') params.directory = value;
        
        const result = await eel.update_project_language(code, params.label || null, params.directory || null, null)();
        if (result.success) {
            // Refresh appData languages
            appData.languages = result.languages;
            initLanguageSwitcher();
            showToast(t('lang_updated_success'), 1500, 'success');
        } else {
            showToast(result.message || t('lang_update_failed'), 3000, 'error');
            // Revert by re-rendering
            await _renderLangManagerList();
        }
    } catch (e) {
        console.error('[Lang] Update error:', e);
        showToast('Error: ' + e, 3000, 'error');
        await _renderLangManagerList();
    }
};

window.confirmAddLanguage = async function() {
    const code = document.getElementById('add-lang-code').value.trim().toLowerCase();
    const label = document.getElementById('add-lang-label').value.trim();
    let dir = document.getElementById('add-lang-dir').value.trim();
    const errorEl = document.getElementById('add-lang-error');
    
    if (!code) {
        errorEl.textContent = t('lang_code_required');
        errorEl.style.display = 'block';
        return;
    }
    if (!label) {
        errorEl.textContent = t('lang_label_required');
        errorEl.style.display = 'block';
        return;
    }
    // Auto-fill directory from code if empty
    if (!dir) {
        dir = '/' + code;
    }
    
    try {
        const result = await eel.add_project_language(code, label, dir)();
        if (result.success) {
            errorEl.style.display = 'none';
            document.getElementById('add-lang-code').value = '';
            document.getElementById('add-lang-label').value = '';
            document.getElementById('add-lang-dir').value = '';
            // Refresh appData
            appData.languages = result.languages;
            initLanguageSwitcher();
            await _renderLangManagerList();
            showToast(`"${label}" (${code}) ${t('lang_added_success')}`, 3000, 'success');
        } else {
            errorEl.textContent = result.message || t('lang_add_failed');
            errorEl.style.display = 'block';
        }
    } catch (e) {
        errorEl.textContent = 'Error: ' + e;
        errorEl.style.display = 'block';
    }
};

window.setDefaultLanguage = async function(code) {
    if (!confirm(t('lang_set_default_confirm').replace('{code}', code))) return;
    
    try {
        const result = await eel.set_default_language(code)();
        if (result.success) {
            appData.languages = result.languages;
            initLanguageSwitcher();
            await _renderLangManagerList();
            showToast(t('lang_default_changed').replace('{code}', code), 3000, 'success');
        } else {
            showToast(result.message || t('lang_update_failed'), 3000, 'error');
        }
    } catch (e) {
        console.error('[Lang] Set default error:', e);
        showToast('Error: ' + e, 3000, 'error');
    }
};

window.removeLanguage = async function(code) {
    if (!confirm(t('lang_remove_confirm').replace('{code}', code))) return;
    
    try {
        const result = await eel.remove_project_language(code)();
        if (result.success) {
            if (window._currentContentLang === code) {
                await switchContentLanguage('');
            }
            appData.languages = result.languages;
            initLanguageSwitcher();
            await _renderLangManagerList();
            showToast(`"${code}" ${t('lang_removed_success')}`, 3000);
        } else {
            alert(t('lang_remove_failed') + ': ' + (result.message || 'Unknown error'));
        }
    } catch (e) {
        alert('Error: ' + e);
    }
};
