// Form Builder Library - Core API
// State management
const state = { 
    schema: null, 
    formRoot: null, 
    resourceIndex: new Map(),
    version: '1.0.0',
    config: {
        // File upload configuration
        uploadFile: null,
        downloadFile: null,
        getThumbnail: null,
        getDownloadUrl: null,
        // Default implementations
        enableFilePreview: true,
        maxPreviewSize: '200px',
        readonly: false
    }
};

// Utility functions
function isPlainObject(obj) {
    return obj && typeof obj === 'object' && obj.constructor === Object;
}

function pathJoin(base, key) {
    return base ? `${base}.${key}` : key;
}

function pretty(obj) {
    return JSON.stringify(obj, null, 2);
}

function clear(node) {
    while (node.firstChild) node.removeChild(node.firstChild);
}

// Schema validation
function validateSchema(schema) {
    const errors = [];
    
    if (!schema || typeof schema !== 'object') {
        errors.push('Schema must be an object');
        return errors;
    }
    
    if (!schema.version) {
        errors.push('Schema missing version');
    }
    
    if (!Array.isArray(schema.elements)) {
        errors.push('Schema missing elements array');
        return errors;
    }
    
    function validateElements(elements, path) {
        elements.forEach((element, index) => {
            const elementPath = `${path}[${index}]`;
            
            if (!element.type) {
                errors.push(`${elementPath}: missing type`);
            }
            
            if (!element.key) {
                errors.push(`${elementPath}: missing key`);
            }
            
            if (element.type === 'group' && element.elements) {
                validateElements(element.elements, `${elementPath}.elements`);
            }
            
            if (element.type === 'select' && element.options) {
                const defaultValue = element.default;
                if (defaultValue !== undefined && defaultValue !== null && defaultValue !== '') {
                    const hasMatchingOption = element.options.some(opt => opt.value === defaultValue);
                    if (!hasMatchingOption) {
                        errors.push(`${elementPath}: default "${defaultValue}" not in options`);
                    }
                }
            }
        });
    }
    
    if (Array.isArray(schema.elements)) validateElements(schema.elements, 'elements');
    return errors;
}

// Form rendering
function renderForm(schema, prefill) {
    console.log('🔧 FormBuilder.renderForm called with:', { schema, prefill });
    
    const errors = validateSchema(schema);
    if (errors.length > 0) {
        console.error('Schema validation errors:', errors);
        return;
    }
    console.log('✅ Schema validation passed');
    
    state.schema = schema;
    if (!state.formRoot) {
        console.error('No form root element set. Call setFormRoot() first.');
        return;
    }
    console.log('✅ FormRoot is set:', state.formRoot);
    
    clear(state.formRoot);
    
    const formEl = document.createElement('div');
    formEl.className = 'space-y-6';
    
    console.log(`🔧 Processing ${schema.elements.length} schema elements`);
    schema.elements.forEach((element, index) => {
        console.log(`🔧 Rendering element ${index}:`, element);
        const block = renderElement(element, { 
            path: '', 
            prefill: prefill || {} 
        });
        console.log(`✅ Element ${index} rendered:`, block);
        formEl.appendChild(block);
    });

    state.formRoot.appendChild(formEl);
    console.log(`✅ Form rendered with ${schema.elements.length} elements`);
}

function renderElement(element, ctx) {
    console.log(`🔧 renderElement called:`, { element, ctx });
    
    const wrapper = document.createElement('div');
    wrapper.className = 'mb-6';
    
    const label = document.createElement('div');
    label.className = 'flex items-center mb-2';
    const title = document.createElement('label');
    title.className = 'text-sm font-medium text-gray-900';
    title.textContent = element.label || element.key;
    if (element.required) {
        const req = document.createElement('span');
        req.className = 'text-red-500 ml-1';
        req.textContent = '*';
        title.appendChild(req);
    }
    label.appendChild(title);
    
    // Add info button if there's description or hint
    if (element.description || element.hint) {
        const infoBtn = document.createElement('button');
        infoBtn.type = 'button';
        infoBtn.className = 'ml-2 text-gray-400 hover:text-gray-600';
        infoBtn.innerHTML = '<svg class="w-4 h-4" fill="currentColor" viewBox="0 0 24 24"><path d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm1 15h-2v-6h2v6zm0-8h-2V7h2v2z"/></svg>';
        
        // Create tooltip
        const tooltipId = `tooltip-${element.key}-${Math.random().toString(36).substr(2, 9)}`;
        const tooltip = document.createElement('div');
        tooltip.id = tooltipId;
        tooltip.className = 'hidden absolute z-50 bg-gray-200 text-gray-900 text-sm rounded-lg p-3 max-w-sm border border-gray-300 shadow-lg';
        tooltip.style.position = 'fixed';
        tooltip.textContent = element.description || element.hint || 'Field information';
        document.body.appendChild(tooltip);
        
        infoBtn.onclick = (e) => {
            e.preventDefault();
            e.stopPropagation();
            showTooltip(tooltipId, infoBtn);
        };
        
        label.appendChild(infoBtn);
    }
    
    wrapper.appendChild(label);

    const pathKey = pathJoin(ctx.path, element.key);

    switch (element.type) {
        case 'text':
            renderTextElement(element, ctx, wrapper, pathKey);
            break;

        case 'textarea':
            renderTextareaElement(element, ctx, wrapper, pathKey);
            break;

        case 'number':
            renderNumberElement(element, ctx, wrapper, pathKey);
            break;

        case 'select':
            renderSelectElement(element, ctx, wrapper, pathKey);
            break;

        case 'file':
            // TODO: Extract to renderFileElement() function
            console.log(`🔧 Rendering file element '${element.key}':`, { readonly: state.config.readonly, prefill: ctx.prefill[element.key], element });
            if (state.config.readonly) {
                // Readonly mode: use common preview function
                const initial = ctx.prefill[element.key] || element.default;
                console.log(`🔧 File initial value:`, initial);
                if (initial) {
                    const filePreview = renderFilePreviewReadonly(initial);
                    wrapper.appendChild(filePreview);
                    console.log(`✅ File preview rendered for '${element.key}'`);
                } else {
                    const emptyState = document.createElement('div');
                    emptyState.className = 'aspect-video bg-gray-100 rounded-lg flex items-center justify-center text-gray-500';
                    emptyState.innerHTML = '<div class="text-center">Нет файла</div>';
                    wrapper.appendChild(emptyState);
                    console.log(`❌ No file data for '${element.key}' - showing empty state`);
                }
            } else {
                // Edit mode: normal file input
                const fileWrapper = document.createElement('div');
                fileWrapper.className = 'space-y-2';
                
                const picker = document.createElement('input');
                picker.type = 'file';
                picker.name = pathKey;
                picker.style.display = 'none'; // Hide default input
                if (element.accept) {
                    if (element.accept.extensions) {
                        picker.accept = element.accept.extensions.map(ext => `.${ext}`).join(',');
                    }
                }
                
                const fileContainer = document.createElement('div');
                fileContainer.className = 'file-preview-container w-full aspect-square max-w-xs bg-gray-100 rounded-lg overflow-hidden relative group cursor-pointer';
                
                const initial = ctx.prefill[element.key] || element.default;
                if (initial) {
                    renderFilePreview(fileContainer, initial, initial, '', false).catch(console.error);
                } else {
                    fileContainer.innerHTML = `
                        <div class="flex flex-col items-center justify-center h-full text-gray-400">
                            <svg class="w-6 h-6 mb-2" fill="currentColor" viewBox="0 0 24 24">
                                <path d="M21 19V5c0-1.1-.9-2-2-2H5c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h14c1.1 0 2-.9 2-2zM8.5 13.5l2.5 3.01L14.5 12l4.5 6H5l3.5-4.5z"/>
                            </svg>
                            <div class="text-sm text-center">Нажмите или перетащите файл</div>
                        </div>
                    `;
                }
                
                fileContainer.onclick = () => picker.click();
                setupDragAndDrop(fileContainer, (files) => {
                    if (files.length > 0) {
                        handleFileSelect(files[0], fileContainer, pathKey);
                    }
                });
                
                picker.onchange = () => {
                    if (picker.files.length > 0) {
                        handleFileSelect(picker.files[0], fileContainer, pathKey);
                    }
                };
                
                fileWrapper.appendChild(fileContainer);
                fileWrapper.appendChild(picker);
                
                // Add upload text
                const uploadText = document.createElement('p');
                uploadText.className = 'text-xs text-gray-600 mt-2 text-center';
                uploadText.innerHTML = `<span class="underline cursor-pointer">Загрузите</span> или перетащите файл`;
                uploadText.querySelector('span').onclick = () => picker.click();
                fileWrapper.appendChild(uploadText);
                
                // Add hint
                const fileHint = document.createElement('p');
                fileHint.className = 'text-xs text-gray-500 mt-1 text-center';
                fileHint.textContent = makeFieldHint(element);
                fileWrapper.appendChild(fileHint);
                
                wrapper.appendChild(fileWrapper);
            }
            break;

        case 'files':
            // TODO: Extract to renderFilesElement() function
            if (state.config.readonly) {
                // Readonly mode: render as results list like in workflow-preview.html
                const resultsWrapper = document.createElement('div');
                resultsWrapper.className = 'space-y-4';
                
                const initialFiles = ctx.prefill[element.key] || [];
                
                if (initialFiles.length > 0) {
                    initialFiles.forEach(resourceId => {
                        const filePreview = renderFilePreviewReadonly(resourceId);
                        resultsWrapper.appendChild(filePreview);
                    });
                } else {
                    resultsWrapper.innerHTML = `<div class="aspect-video bg-gray-100 rounded-lg flex items-center justify-center text-gray-500"><div class="text-center">Нет файлов</div></div>`;
                }
                
                wrapper.appendChild(resultsWrapper);
            } else {
                // Edit mode: normal files input
                const filesWrapper = document.createElement('div');
                filesWrapper.className = 'space-y-2';
                
                const filesPicker = document.createElement('input');
                filesPicker.type = 'file';
                filesPicker.name = pathKey;
                filesPicker.multiple = true;
                filesPicker.style.display = 'none'; // Hide default input
                if (element.accept) {
                    if (element.accept.extensions) {
                        filesPicker.accept = element.accept.extensions.map(ext => `.${ext}`).join(',');
                    }
                }
                
                // Create container with border like in workflow-preview
                const filesContainer = document.createElement('div');
                filesContainer.className = 'border-2 border-dashed border-gray-300 rounded-lg p-3 hover:border-gray-400 transition-colors';
                
                const list = document.createElement('div');
                list.className = 'files-list';
                
                const initialFiles = ctx.prefill[element.key] || [];
                
                function updateFilesList() {
                    renderResourcePills(list, initialFiles, (ridToRemove) => {
                        const index = initialFiles.indexOf(ridToRemove);
                        if (index > -1) {
                            initialFiles.splice(index, 1);
                        }
                        updateFilesList(); // Re-render after removal
                    });
                }
                
                // Initial render
                updateFilesList();
                
                setupDragAndDrop(filesContainer, async (files) => {
                    const arr = Array.from(files);
                    for (const file of arr) {
                        let rid;
                        
                        // If uploadHandler is configured, use it to upload the file
                        if (state.config.uploadFile) {
                            try {
                                rid = await state.config.uploadFile(file);
                                if (typeof rid !== 'string') {
                                    throw new Error('Upload handler must return a string resource ID');
                                }
                            } catch (error) {
                                throw new Error(`File upload failed: ${error.message}`);
                            }
                        } else {
                            throw new Error('No upload handler configured. Set uploadHandler via FormBuilder.setUploadHandler()');
                        }
                        
                        state.resourceIndex.set(rid, {
                            name: file.name,
                            type: file.type,
                            size: file.size,
                            file: null // Files are always uploaded, never stored locally
                        });
                        initialFiles.push(rid);
                    }
                    updateFilesList();
                });
                
                filesPicker.onchange = async () => {
                    for (const file of Array.from(filesPicker.files)) {
                        let rid;
                        
                        // If uploadHandler is configured, use it to upload the file
                        if (state.config.uploadFile) {
                            try {
                                rid = await state.config.uploadFile(file);
                                if (typeof rid !== 'string') {
                                    throw new Error('Upload handler must return a string resource ID');
                                }
                            } catch (error) {
                                throw new Error(`File upload failed: ${error.message}`);
                            }
                        } else {
                            throw new Error('No upload handler configured. Set uploadHandler via FormBuilder.setUploadHandler()');
                        }
                        
                        state.resourceIndex.set(rid, {
                            name: file.name,
                            type: file.type,
                            size: file.size,
                            file: null // Files are always uploaded, never stored locally
                        });
                        initialFiles.push(rid);
                    }
                    updateFilesList();
                    // Clear the file input
                    filesPicker.value = '';
                };
                
                filesContainer.appendChild(list);
                
                filesWrapper.appendChild(filesContainer);
                filesWrapper.appendChild(filesPicker);
                
                // Add hint
                const filesHint = document.createElement('p');
                filesHint.className = 'text-xs text-gray-500 mt-1 text-center';
                filesHint.textContent = makeFieldHint(element);
                filesWrapper.appendChild(filesHint);
                
                wrapper.appendChild(filesWrapper);
            }
            break;

        case 'group':
            // TODO: Extract to renderGroupElement() function
            const groupWrap = document.createElement('div');
            groupWrap.className = 'border border-gray-200 rounded-lg p-4 bg-gray-50';
            
            const header = document.createElement('div');
            header.className = 'flex justify-between items-center mb-4';
            
            const left = document.createElement('div');
            left.className = 'flex-1';
            
            const right = document.createElement('div');
            right.className = 'flex gap-2';
            
            const itemsWrap = document.createElement('div');
            itemsWrap.className = 'space-y-4';
            
            groupWrap.appendChild(header);
            header.appendChild(left);
            header.appendChild(right);
            
            if (element.repeat && isPlainObject(element.repeat)) {
                const min = element.repeat.min ?? 0;
                const max = element.repeat.max ?? Infinity;
                const pre = Array.isArray(ctx.prefill?.[element.key]) ? ctx.prefill[element.key] : null;
                
                header.appendChild(right);

                const countItems = () => itemsWrap.querySelectorAll(':scope > .groupItem').length;

                const addItem = (prefillObj) => {
                    const item = document.createElement('div');
                    item.className = 'groupItem border border-dashed border-slate-300 dark:border-slate-600 rounded-lg p-3 mb-3 bg-blue-50/30 dark:bg-blue-900/10';
                    const subCtx = { 
                        path: pathJoin(ctx.path, element.key + `[${countItems()}]`), 
                        prefill: prefillObj || {} 
                    };
                    element.elements.forEach(child => item.appendChild(renderElement(child, subCtx)));
                    
                    const rem = document.createElement('button');
                    rem.type = 'button';
                    rem.className = 'bg-red-500 hover:bg-red-600 text-white px-2 py-1 rounded text-xs font-medium transition-colors';
                    rem.textContent = 'Удалить';
                    rem.addEventListener('click', () => {
                        if (countItems() <= (element.repeat.min ?? 0)) return;
                        itemsWrap.removeChild(item);
                        refreshControls();
                    });
                    item.appendChild(rem);
                    itemsWrap.appendChild(item);
                    refreshControls();
                };

                groupWrap.appendChild(itemsWrap);
                
                // Add button after items
                const addBtn = document.createElement('button');
                addBtn.type = 'button';
                addBtn.className = 'w-full py-2 px-4 border border-gray-300 rounded-lg text-gray-700 hover:bg-gray-50 flex items-center justify-center mt-3';
                addBtn.innerHTML = '<svg class="w-4 h-4 mr-2" fill="currentColor" viewBox="0 0 24 24"><path d="M19 13h-6v6h-2v-6H5v-2h6V5h2v6h6v2z"/></svg>Добавить элемент';
                groupWrap.appendChild(addBtn);
                
                const refreshControls = () => {
                    const n = countItems();
                    addBtn.disabled = n >= max;
                    left.innerHTML = `<span>${element.label || element.key}</span> <span class="text-slate-500 dark:text-slate-400 text-xs">[${n} / ${max === Infinity ? '∞' : max}, min=${min}]</span>`;
                };
                
                if (pre && pre.length) {
                    const n = Math.min(max, Math.max(min, pre.length));
                    for (let i = 0; i < n; i++) addItem(pre[i]);
                } else {
                    const n = Math.max(min, 0);
                    for (let i = 0; i < n; i++) addItem(null);
                }
                
                addBtn.addEventListener('click', () => addItem(null));
            } else {
                // Single object group
                const subCtx = { 
                    path: pathJoin(ctx.path, element.key), 
                    prefill: ctx.prefill?.[element.key] || {} 
                };
                element.elements.forEach(child => itemsWrap.appendChild(renderElement(child, subCtx)));
                groupWrap.appendChild(itemsWrap);
                left.innerHTML = `<span>${element.label || element.key}</span>`;
            }
            
            wrapper.appendChild(groupWrap);
            break;

        default:
            const unsupported = document.createElement('div');
            unsupported.className = 'text-red-500 text-sm';
            unsupported.textContent = `Unsupported field type: ${element.type}`;
            wrapper.appendChild(unsupported);
    }

    return wrapper;
}

function makeFieldHint(element) {
    const parts = [];
    
    if (element.required) {
        parts.push('required');
    } else {
        parts.push('optional');
    }
    
    if (element.minLength != null || element.maxLength != null) {
        if (element.minLength != null && element.maxLength != null) {
            parts.push(`length=${element.minLength}-${element.maxLength} characters`);
        } else if (element.maxLength != null) {
            parts.push(`max=${element.maxLength} characters`);
        } else if (element.minLength != null) {
            parts.push(`min=${element.minLength} characters`);
        }
    }
    
    if (element.min != null || element.max != null) {
        if (element.min != null && element.max != null) {
            parts.push(`range=${element.min}-${element.max}`);
        } else if (element.max != null) {
            parts.push(`max=${element.max}`);
        } else if (element.min != null) {
            parts.push(`min=${element.min}`);
        }
    }
    
    if (element.maxSizeMB) {
        parts.push(`max_size=${element.maxSizeMB}MB`);
    }
    
    if (element.accept && element.accept.extensions) {
        parts.push(`formats=${element.accept.extensions.map(ext => ext.toUpperCase()).join(',')}`);
    }
    
    if (element.pattern && !element.pattern.includes('А-Я')) {
        parts.push('plain text only');
    } else if (element.pattern && element.pattern.includes('А-Я')) {
        parts.push('text with punctuation');
    }
    
    return parts.join(' • ');
}

async function renderFilePreview(container, resourceId, fileName, fileType, isReadonly = false) {
    // Don't change container className - preserve max-w-xs and other styling
    
    // Clear container content first
    clear(container);
    
    if (isReadonly) {
        container.classList.add('cursor-pointer');
    }
    
    const img = document.createElement('img');
    img.className = 'w-full h-full object-contain';
    img.alt = fileName || 'Preview';
    
    // Use stored file from resourceIndex if available, or try getThumbnail
    const meta = state.resourceIndex.get(resourceId);
    if (meta && meta.file && meta.file instanceof File) {
        // For local files, use FileReader to display preview
        if (meta.type && meta.type.startsWith('image/')) {
            const reader = new FileReader();
            reader.onload = (e) => {
                img.src = e.target.result;
            };
            reader.readAsDataURL(meta.file);
            container.appendChild(img);
        } else {
            // Non-image file
            container.innerHTML = '<div class="flex flex-col items-center justify-center h-full text-gray-400"><div class="text-2xl mb-2">📁</div><div class="text-sm">' + fileName + '</div></div>';
        }
        
        // Add delete button for edit mode
        if (!isReadonly) {
            addDeleteButton(container, () => {
                // Clear the file
                state.resourceIndex.delete(resourceId);
                // Update hidden input
                const hiddenInput = container.parentElement.querySelector('input[type="hidden"]');
                if (hiddenInput) {
                    hiddenInput.value = '';
                }
                // Clear preview and show placeholder
                container.innerHTML = `
                    <div class="flex flex-col items-center justify-center h-full text-gray-400">
                        <svg class="w-6 h-6 mb-2" fill="currentColor" viewBox="0 0 24 24">
                            <path d="M21 19V5c0-1.1-.9-2-2-2H5c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h14c1.1 0 2-.9 2-2zM8.5 13.5l2.5 3.01L14.5 12l4.5 6H5l3.5-4.5z"/>
                        </svg>
                        <div class="text-sm text-center">Нажмите или перетащите файл</div>
                    </div>
                `;
            });
        }
    } else if (state.config.getThumbnail) {
        // Try to get thumbnail from config for uploaded files
        try {
            const thumbnailUrl = await state.config.getThumbnail(resourceId);
            if (thumbnailUrl) {
                img.src = thumbnailUrl;
                container.appendChild(img);
            } else {
                // Fallback to file icon
                container.innerHTML = '<div class="flex flex-col items-center justify-center h-full text-gray-400"><div class="text-2xl mb-2">🖼️</div><div class="text-sm">' + fileName + '</div></div>';
            }
        } catch (error) {
            console.warn('Thumbnail loading failed:', error);
            container.innerHTML = '<div class="flex flex-col items-center justify-center h-full text-gray-400"><div class="text-2xl mb-2">📁</div><div class="text-sm">' + fileName + '</div></div>';
        }
        
        // Add delete button for edit mode
        if (!isReadonly) {
            addDeleteButton(container, () => {
                // Clear the file
                state.resourceIndex.delete(resourceId);
                // Update hidden input
                const hiddenInput = container.parentElement.querySelector('input[type="hidden"]');
                if (hiddenInput) {
                    hiddenInput.value = '';
                }
                // Clear preview and show placeholder
                container.innerHTML = `
                    <div class="flex flex-col items-center justify-center h-full text-gray-400">
                        <svg class="w-6 h-6 mb-2" fill="currentColor" viewBox="0 0 24 24">
                            <path d="M21 19V5c0-1.1-.9-2-2-2H5c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h14c1.1 0 2-.9 2-2zM8.5 13.5l2.5 3.01L14.5 12l4.5 6H5l3.5-4.5z"/>
                        </svg>
                        <div class="text-sm text-center">Нажмите или перетащите файл</div>
                    </div>
                `;
            });
        }
    } else {
        // No file and no getThumbnail config - fallback
        container.innerHTML = '<div class="flex flex-col items-center justify-center h-full text-gray-400"><div class="text-2xl mb-2">🖼️</div><div class="text-sm">' + fileName + '</div></div>';
    }
    
    // Add click handler for download in readonly mode
    if (isReadonly && state.config.downloadFile) {
        container.style.cursor = 'pointer';
        container.onclick = () => {
            if (state.config.downloadFile) {
                state.config.downloadFile(resourceId, fileName);
            }
        };
    }
}
        
function renderResourcePills(container, rids, onRemove) {
    clear(container);
    
    // Show initial placeholder only if this is the first render (no previous grid)
    // Check if container already has grid class to determine if this is initial render
    const isInitialRender = !container.classList.contains('grid');
    
    if ((!rids || rids.length === 0) && isInitialRender) {
        // Create grid container
        const gridContainer = document.createElement('div');
        gridContainer.className = 'grid grid-cols-4 gap-3 mb-3';
        
        // Create 4 placeholder slots
        for (let i = 0; i < 4; i++) {
            const slot = document.createElement('div');
            slot.className = 'aspect-square bg-gray-100 border-2 border-dashed border-gray-300 rounded flex items-center justify-center cursor-pointer hover:border-gray-400 transition-colors';
            
            const svg = document.createElementNS('http://www.w3.org/2000/svg', 'svg');
            svg.setAttribute('class', 'w-12 h-12 text-gray-400');
            svg.setAttribute('fill', 'currentColor');
            svg.setAttribute('viewBox', '0 0 24 24');
            
            const path = document.createElementNS('http://www.w3.org/2000/svg', 'path');
            path.setAttribute('d', 'M21 19V5c0-1.1-.9-2-2-2H5c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h14c1.1 0 2-.9 2-2zM8.5 13.5l2.5 3.01L14.5 12l4.5 6H5l3.5-4.5z');
            
            svg.appendChild(path);
            slot.appendChild(svg);
            
            // Add click handler to each slot
            slot.onclick = () => {
                // Look for file input in the files wrapper (go up from list -> filesContainer -> filesWrapper)
                const filesWrapper = container.closest('.space-y-2');
                const fileInput = filesWrapper?.querySelector('input[type="file"]');
                if (fileInput) fileInput.click();
            };
            
            gridContainer.appendChild(slot);
        }
        
        // Create text container
        const textContainer = document.createElement('div');
        textContainer.className = 'text-center text-xs text-gray-600';
        
        const uploadLink = document.createElement('span');
        uploadLink.className = 'underline cursor-pointer';
        uploadLink.textContent = 'Загрузите';
        uploadLink.onclick = (e) => {
            e.stopPropagation();
            // Look for file input in the files wrapper (go up from list -> filesContainer -> filesWrapper)
            const filesWrapper = container.closest('.space-y-2');
            const fileInput = filesWrapper?.querySelector('input[type="file"]');
            if (fileInput) fileInput.click();
        };
        
        textContainer.appendChild(uploadLink);
        textContainer.appendChild(document.createTextNode(' или перетащите файлы'));
        
        // Clear and append
        container.appendChild(gridContainer);
        container.appendChild(textContainer);
        return;
    }
    
    // Always show files grid if we have files OR if this was already a grid
    // This prevents shrinking when deleting the last file
    container.className = 'grid grid-cols-4 gap-3 mt-2';
    
    // Calculate how many slots we need (at least 4, then expand by rows of 4)
    const currentImagesCount = rids ? rids.length : 0;
    // Calculate rows needed: always show an extra slot for adding next file
    // 0-3 files → 1 row (4 slots), 4-7 files → 2 rows (8 slots), 8-11 files → 3 rows (12 slots)
    const rowsNeeded = Math.floor(currentImagesCount / 4) + 1;
    const slotsNeeded = rowsNeeded * 4;

    
    // Add all slots (filled and empty)
    for (let i = 0; i < slotsNeeded; i++) {
        const slot = document.createElement('div');
        
        if (rids && i < rids.length) {
            // Filled slot with image preview
            const rid = rids[i];
            const meta = state.resourceIndex.get(rid);
            slot.className = 'aspect-square bg-gray-100 rounded-lg overflow-hidden relative group border border-gray-300';
            
            // Add image or file content
            if (meta && meta.type?.startsWith('image/')) {
                if (meta.file && meta.file instanceof File) {
                    // Use FileReader for local files
                    const img = document.createElement('img');
                    img.className = 'w-full h-full object-contain';
                    img.alt = meta.name;
                    
                    const reader = new FileReader();
                    reader.onload = (e) => {
                        img.src = e.target.result;
                    };
                    reader.readAsDataURL(meta.file);
                    slot.appendChild(img);
                } else if (state.config.getThumbnail) {
                    // Use getThumbnail for uploaded files
                    const img = document.createElement('img');
                    img.className = 'w-full h-full object-contain';
                    img.alt = meta.name;
                    
                    const url = state.config.getThumbnail(rid);
                    if (url) {
                        img.src = url;
                        slot.appendChild(img);
                    } else {
                        slot.innerHTML = `<div class="flex flex-col items-center justify-center h-full text-gray-400">
                            <svg class="w-12 h-12" fill="currentColor" viewBox="0 0 24 24">
                                <path d="M21 19V5c0-1.1-.9-2-2-2H5c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h14c1.1 0 2-.9 2-2zM8.5 13.5l2.5 3.01L14.5 12l4.5 6H5l3.5-4.5z"/>
                            </svg>
                        </div>`;
                    }
                } else {
                    slot.innerHTML = `<div class="flex flex-col items-center justify-center h-full text-gray-400">
                        <svg class="w-12 h-12" fill="currentColor" viewBox="0 0 24 24">
                            <path d="M21 19V5c0-1.1-.9-2-2-2H5c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h14c1.1 0 2-.9 2-2zM8.5 13.5l2.5 3.01L14.5 12l4.5 6H5l3.5-4.5z"/>
                        </svg>
                    </div>`;
                }
            } else {
                slot.innerHTML = `<div class="flex flex-col items-center justify-center h-full text-gray-400">
                    <div class="text-2xl mb-1">📁</div>
                    <div class="text-xs">${meta?.name || 'File'}</div>
                </div>`;
            }
            
            // Add remove button overlay (similar to file field)
            if (onRemove) {
                const overlay = document.createElement('div');
                overlay.className = 'absolute inset-0 bg-black bg-opacity-50 opacity-0 group-hover:opacity-100 transition-opacity flex items-center justify-center';
                
                const removeBtn = document.createElement('button');
                removeBtn.className = 'bg-red-600 text-white px-2 py-1 rounded text-xs';
                removeBtn.textContent = 'Удалить';
                removeBtn.onclick = (e) => {
                    e.stopPropagation();
                    onRemove(rid);
                };
                
                overlay.appendChild(removeBtn);
                slot.appendChild(overlay);
            }
        } else {
            // Empty slot placeholder
            slot.className = 'aspect-square bg-gray-100 border-2 border-dashed border-gray-300 rounded-lg flex items-center justify-center cursor-pointer hover:border-gray-400 transition-colors';
            slot.innerHTML = '<svg class="w-12 h-12 text-gray-400" fill="currentColor" viewBox="0 0 24 24"><path d="M21 19V5c0-1.1-.9-2-2-2H5c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h14c1.1 0 2-.9 2-2zM8.5 13.5l2.5 3.01L14.5 12l4.5 6H5l3.5-4.5z"/></svg>';
            slot.onclick = () => {
                // Look for file input in the files wrapper (go up from list -> filesContainer -> filesWrapper)
                const filesWrapper = container.closest('.space-y-2');
                const fileInput = filesWrapper?.querySelector('input[type="file"]');
                if (fileInput) fileInput.click();
            };
        }
        
        container.appendChild(slot);
    }
}

function formatFileSize(bytes) {
    if (bytes === 0) return '0 B';
    const k = 1024;
    const sizes = ['B', 'KB', 'MB', 'GB'];
    const i = Math.floor(Math.log(bytes) / Math.log(k));
    return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i];
}

function generateResourceId() {
    return 'res_' + Math.random().toString(36).substr(2, 9) + Date.now().toString(36);
}

async function handleFileSelect(file, container, fieldName) {
    let rid;
    
    // If uploadHandler is configured, use it to upload the file
    if (state.config.uploadFile) {
        try {
            rid = await state.config.uploadFile(file);
            if (typeof rid !== 'string') {
                throw new Error('Upload handler must return a string resource ID');
            }
        } catch (error) {
            throw new Error(`File upload failed: ${error.message}`);
        }
    } else {
        throw new Error('No upload handler configured. Set uploadHandler via FormBuilder.setUploadHandler()');
    }
    
    state.resourceIndex.set(rid, {
        name: file.name,
        type: file.type,
        size: file.size,
        file: null // Files are always uploaded, never stored locally
    });
    
    // Create hidden input to store the resource ID
    let hiddenInput = container.parentElement.querySelector('input[type="hidden"]');
    if (!hiddenInput) {
        hiddenInput = document.createElement('input');
        hiddenInput.type = 'hidden';
        hiddenInput.name = fieldName;
        container.parentElement.appendChild(hiddenInput);
    }
    hiddenInput.value = rid;
    
    renderFilePreview(container, rid, file.name, file.type, false).catch(console.error);
}

function setupDragAndDrop(element, dropHandler) {
    element.addEventListener('dragover', (e) => {
        e.preventDefault();
        element.classList.add('border-blue-500', 'bg-blue-50');
    });

    element.addEventListener('dragleave', (e) => {
        e.preventDefault();
        element.classList.remove('border-blue-500', 'bg-blue-50');
    });

    element.addEventListener('drop', (e) => {
        e.preventDefault();
        element.classList.remove('border-blue-500', 'bg-blue-50');
        dropHandler(e.dataTransfer.files);
    });
}

function addDeleteButton(container, onDelete) {
    // Remove existing overlay if any
    const existingOverlay = container.querySelector('.delete-overlay');
    if (existingOverlay) {
        existingOverlay.remove();
    }
    
    // Create overlay with center delete button (like in files)
    const overlay = document.createElement('div');
    overlay.className = 'delete-overlay absolute inset-0 bg-black bg-opacity-50 opacity-0 group-hover:opacity-100 transition-opacity flex items-center justify-center';
    
    const deleteBtn = document.createElement('button');
    deleteBtn.className = 'bg-red-600 text-white px-3 py-1 rounded text-sm hover:bg-red-700 transition-colors';
    deleteBtn.textContent = 'Удалить';
    deleteBtn.onclick = (e) => {
        e.stopPropagation();
        onDelete();
    };
    
    overlay.appendChild(deleteBtn);
    container.appendChild(overlay);
}

function showTooltip(tooltipId, button) {
    const tooltip = document.getElementById(tooltipId);
    const isCurrentlyVisible = !tooltip.classList.contains('hidden');
    
    // Hide all tooltips first
    document.querySelectorAll('[id^="tooltip-"]').forEach(t => {
        t.classList.add('hidden');
    });
    
    // If the tooltip was already visible, keep it hidden (toggle behavior)
    if (isCurrentlyVisible) {
        return;
    }
    
    // Position the tooltip intelligently
    const rect = button.getBoundingClientRect();
    const viewportWidth = window.innerWidth;
    const viewportHeight = window.innerHeight;
    
    // Ensure tooltip is appended to body for proper positioning
    if (tooltip && tooltip.parentElement !== document.body) {
        document.body.appendChild(tooltip);
    }
    
    // Show tooltip temporarily to measure its size
    tooltip.style.visibility = 'hidden';
    tooltip.style.position = 'fixed';
    tooltip.classList.remove('hidden');
    const tooltipRect = tooltip.getBoundingClientRect();
    tooltip.classList.add('hidden');
    tooltip.style.visibility = 'visible';
    
    let left = rect.left;
    let top = rect.bottom + 5;
    
    // Adjust horizontal position if tooltip would go off-screen
    if (left + tooltipRect.width > viewportWidth) {
        left = rect.right - tooltipRect.width;
    }
    
    // Adjust vertical position if tooltip would go off-screen
    if (top + tooltipRect.height > viewportHeight) {
        top = rect.top - tooltipRect.height - 5;
    }
    
    // Ensure tooltip doesn't go off the left edge
    if (left < 10) {
        left = 10;
    }
    
    // Ensure tooltip doesn't go off the top edge
    if (top < 10) {
        top = rect.bottom + 5;
    }
    
    tooltip.style.left = left + 'px';
    tooltip.style.top = top + 'px';
    
    // Show the tooltip
    tooltip.classList.remove('hidden');
    
    // Hide after 25 seconds
    setTimeout(() => {
        tooltip.classList.add('hidden');
    }, 25000);
}

// Close tooltips when clicking outside
document.addEventListener('click', function(e) {
    const isInfoButton = e.target.closest('button') && e.target.closest('button').onclick;
    const isTooltip = e.target.closest('[id^="tooltip-"]');
    
    if (!isInfoButton && !isTooltip) {
        document.querySelectorAll('[id^="tooltip-"]').forEach(tooltip => {
            tooltip.classList.add('hidden');
        });
    }
});

// Form validation and data extraction
function validateForm(skipValidation = false) {
    if (!state.schema || !state.formRoot) return { valid: true, data: {} };
    
    const errors = [];
    const data = {};
    
    function markValidity(input, errorMessage) {
        if (!input) return;
        if (errorMessage) {
            input.classList.add('invalid');
            input.title = errorMessage;
        } else {
            input.classList.remove('invalid');
            input.title = '';
        }
    }
    
    function validateElement(element, ctx) {
        const key = element.key;
        const scopeRoot = state.formRoot;
        
        switch (element.type) {
            case 'text':
            case 'textarea': {
                const input = scopeRoot.querySelector(`[name$="${key}"]`);
                const val = input?.value ?? '';
                if (!skipValidation && element.required && val === '') {
                    errors.push(`${key}: required`);
                    markValidity(input, 'required');
                    return '';
                }
                if (!skipValidation && val) {
                    if (element.minLength != null && val.length < element.minLength) {
                        errors.push(`${key}: minLength=${element.minLength}`);
                        markValidity(input, `minLength=${element.minLength}`);
                    }
                    if (element.maxLength != null && val.length > element.maxLength) {
                        errors.push(`${key}: maxLength=${element.maxLength}`);
                        markValidity(input, `maxLength=${element.maxLength}`);
                    }
                    if (element.pattern) {
                        try {
                            const re = new RegExp(element.pattern);
                            if (!re.test(val)) {
                                errors.push(`${key}: pattern mismatch`);
                                markValidity(input, 'pattern mismatch');
                            }
                        } catch {
                            errors.push(`${key}: invalid pattern`);
                            markValidity(input, 'invalid pattern');
                        }
                    }
                } else if (skipValidation) {
                    markValidity(input, null);
                } else {
                    markValidity(input, null);
                }
                return val;
            }
            case 'number': {
                const input = scopeRoot.querySelector(`[name$="${key}"]`);
                const raw = input?.value ?? '';
                if (!skipValidation && element.required && raw === '') {
                    errors.push(`${key}: required`);
                    markValidity(input, 'required');
                    return null;
                }
                if (raw === '') {
                    markValidity(input, null);
                    return null;
                }
                const v = parseFloat(raw);
                if (!skipValidation && !Number.isFinite(v)) {
                    errors.push(`${key}: not a number`);
                    markValidity(input, 'not a number');
                    return null;
                }
                if (!skipValidation && element.min != null && v < element.min) {
                    errors.push(`${key}: < min=${element.min}`);
                    markValidity(input, `< min=${element.min}`);
                }
                if (!skipValidation && element.max != null && v > element.max) {
                    errors.push(`${key}: > max=${element.max}`);
                    markValidity(input, `> max=${element.max}`);
                }
                const d = Number.isInteger(element.decimals ?? 0) ? element.decimals : 0;
                markValidity(input, null);
                return Number(v.toFixed(d));
            }
            case 'select': {
                const input = scopeRoot.querySelector(`[name$="${key}"]`);
                const val = input?.value ?? '';
                if (!skipValidation && element.required && val === '') {
                    errors.push(`${key}: required`);
                    markValidity(input, 'required');
                    return '';
                }
                markValidity(input, null);
                return val;
            }
            case 'file': {
                const input = scopeRoot.querySelector(`input[name$="${key}"][type="hidden"]`);
                const rid = input?.value ?? '';
                if (!skipValidation && element.required && rid === '') {
                    errors.push(`${key}: required`);
                    return null;
                }
                return rid || null;
            }
            case 'files': {
                // For files, we need to collect all resource IDs
                const container = scopeRoot.querySelector(`[name$="${key}"]`)?.parentElement?.querySelector('.files-list');
                const rids = [];
                if (container) {
                    // Extract resource IDs from the current state
                    // This is a simplified approach - in practice you'd track this better
                }
                return rids;
            }
            case 'group': {
                if (element.repeat && isPlainObject(element.repeat)) {
                    const items = [];
                    const itemElements = scopeRoot.querySelectorAll(`[name^="${key}["]`);
                    const itemCount = Math.max(0, Math.floor(itemElements.length / element.elements.length));
                    
                    for (let i = 0; i < itemCount; i++) {
                        const itemData = {};
                        element.elements.forEach(child => {
                            const childKey = `${key}[${i}].${child.key}`;
                            itemData[child.key] = validateElement({...child, key: childKey}, ctx);
                        });
                        items.push(itemData);
                    }
                    return items;
                } else {
                    const groupData = {};
                    element.elements.forEach(child => {
                        const childKey = `${key}.${child.key}`;
                        groupData[child.key] = validateElement({...child, key: childKey}, ctx);
                    });
                    return groupData;
                }
            }
            default:
                return null;
        }
    }
    
    state.schema.elements.forEach(element => {
        data[element.key] = validateElement(element, { path: '' });
    });
    
    return {
        valid: errors.length === 0,
        errors,
        data
    };
}

// Element rendering functions
function renderTextElement(element, ctx, wrapper, pathKey) {
    const textInput = document.createElement('input');
    textInput.type = 'text';
    textInput.className = 'w-full px-3 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500';
    textInput.name = pathKey;
    textInput.placeholder = element.placeholder || 'Введите текст';
    textInput.value = ctx.prefill[element.key] || element.default || '';
    textInput.readOnly = state.config.readonly;
    wrapper.appendChild(textInput);
    
    // Add hint
    const textHint = document.createElement('p');
    textHint.className = 'text-xs text-gray-500 mt-1';
    textHint.textContent = makeFieldHint(element);
    wrapper.appendChild(textHint);
}

function renderTextareaElement(element, ctx, wrapper, pathKey) {
    const textareaInput = document.createElement('textarea');
    textareaInput.className = 'w-full px-3 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500 resize-none';
    textareaInput.name = pathKey;
    textareaInput.placeholder = element.placeholder || 'Введите текст';
    textareaInput.rows = element.rows || 4;
    textareaInput.value = ctx.prefill[element.key] || element.default || '';
    textareaInput.readOnly = state.config.readonly;
    wrapper.appendChild(textareaInput);
    
    // Add hint
    const textareaHint = document.createElement('p');
    textareaHint.className = 'text-xs text-gray-500 mt-1';
    textareaHint.textContent = makeFieldHint(element);
    wrapper.appendChild(textareaHint);
}

function renderNumberElement(element, ctx, wrapper, pathKey) {
    const numberInput = document.createElement('input');
    numberInput.type = 'number';
    numberInput.className = 'w-full px-3 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500';
    numberInput.name = pathKey;
    numberInput.placeholder = element.placeholder || '0';
    if (element.min !== undefined) numberInput.min = element.min;
    if (element.max !== undefined) numberInput.max = element.max;
    if (element.step !== undefined) numberInput.step = element.step;
    numberInput.value = ctx.prefill[element.key] || element.default || '';
    numberInput.readOnly = state.config.readonly;
    wrapper.appendChild(numberInput);
    
    // Add hint
    const numberHint = document.createElement('p');
    numberHint.className = 'text-xs text-gray-500 mt-1';
    numberHint.textContent = makeFieldHint(element);
    wrapper.appendChild(numberHint);
}

function renderSelectElement(element, ctx, wrapper, pathKey) {
    const selectInput = document.createElement('select');
    selectInput.className = 'w-full px-3 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500';
    selectInput.name = pathKey;
    selectInput.disabled = state.config.readonly;
    
    (element.options || []).forEach(option => {
        const optionEl = document.createElement('option');
        optionEl.value = option.value;
        optionEl.textContent = option.label;
        if ((ctx.prefill[element.key] || element.default) === option.value) {
            optionEl.selected = true;
        }
        selectInput.appendChild(optionEl);
    });
    
    wrapper.appendChild(selectInput);
    
    // Add hint
    const selectHint = document.createElement('p');
    selectHint.className = 'text-xs text-gray-500 mt-1';
    selectHint.textContent = makeFieldHint(element);
    wrapper.appendChild(selectHint);
}

// Common file preview rendering function for readonly mode
function renderFilePreviewReadonly(resourceId, fileName) {
    const meta = state.resourceIndex.get(resourceId);
    const actualFileName = fileName || meta?.name || 'file';
    
    // Individual file result container
    const fileResult = document.createElement('div');
    fileResult.className = 'space-y-3';
    
    // Large preview container
    const previewContainer = document.createElement('div');
    previewContainer.className = 'bg-gray-100 rounded-lg overflow-hidden cursor-pointer hover:opacity-90 transition-opacity';
    
    // Check file type and render appropriate preview
    if (meta?.type?.startsWith('image/') || actualFileName.toLowerCase().match(/\.(jpg|jpeg|png|gif|webp)$/)) {
        // Image preview
        if (state.config.getThumbnail) {
            const thumbnailUrl = state.config.getThumbnail(resourceId);
            if (thumbnailUrl) {
                previewContainer.innerHTML = `<img src="${thumbnailUrl}" alt="${actualFileName}" class="w-full h-auto">`;
            } else {
                previewContainer.innerHTML = `<div class="aspect-video flex items-center justify-center text-gray-400"><div class="text-center"><div class="text-4xl mb-2">🖼️</div><div class="text-sm">${actualFileName}</div></div></div>`;
            }
        } else {
            previewContainer.innerHTML = `<div class="aspect-video flex items-center justify-center text-gray-400"><div class="text-center"><div class="text-4xl mb-2">🖼️</div><div class="text-sm">${actualFileName}</div></div></div>`;
        }
    } else if (meta?.type?.startsWith('video/') || actualFileName.toLowerCase().match(/\.(mp4|webm|avi|mov)$/)) {
        // Video preview
        if (state.config.getThumbnail) {
            const thumbnailUrl = state.config.getThumbnail(resourceId);
            if (thumbnailUrl) {
                previewContainer.innerHTML = `
                    <div class="relative group">
                        <video class="w-full h-auto" controls preload="auto" muted>
                            <source src="${thumbnailUrl}" type="${meta?.type || 'video/mp4'}">
                            Ваш браузер не поддерживает видео.
                        </video>
                        <div class="absolute inset-0 bg-black bg-opacity-20 opacity-0 group-hover:opacity-100 transition-opacity flex items-center justify-center pointer-events-none">
                            <div class="bg-white bg-opacity-90 rounded-full p-3">
                                <svg class="w-8 h-8 text-gray-800" fill="currentColor" viewBox="0 0 24 24">
                                    <path d="M8 5v14l11-7z"/>
                                </svg>
                            </div>
                        </div>
                    </div>
                `;
            } else {
                previewContainer.innerHTML = `<div class="aspect-video flex items-center justify-center text-gray-400"><div class="text-center"><div class="text-4xl mb-2">🎥</div><div class="text-sm">${actualFileName}</div></div></div>`;
            }
        } else {
            previewContainer.innerHTML = `<div class="aspect-video flex items-center justify-center text-gray-400"><div class="text-center"><div class="text-4xl mb-2">🎥</div><div class="text-sm">${actualFileName}</div></div></div>`;
        }
    } else {
        // Other file types
        previewContainer.innerHTML = `<div class="aspect-video flex items-center justify-center text-gray-400"><div class="text-center"><div class="text-4xl mb-2">📁</div><div class="text-sm">${actualFileName}</div></div></div>`;
    }
    
    // File name
    const fileNameElement = document.createElement('p');
    fileNameElement.className = 'text-sm font-medium text-gray-900 text-center';
    fileNameElement.textContent = actualFileName;
    
    // Download button
    const downloadButton = document.createElement('button');
    downloadButton.className = 'w-full px-3 py-2 text-sm bg-blue-600 text-white rounded-lg hover:bg-blue-700 transition-colors';
    downloadButton.textContent = 'Скачать';
    downloadButton.onclick = (e) => {
        e.preventDefault();
        e.stopPropagation();
        if (state.config.downloadFile) {
            state.config.downloadFile(resourceId, actualFileName);
        } else {
            forceDownload(resourceId, actualFileName);
        }
    };
    
    fileResult.appendChild(previewContainer);
    fileResult.appendChild(fileNameElement);
    fileResult.appendChild(downloadButton);
    
    return fileResult;
}

// TODO: Extract large file, files, and group rendering logic to separate functions:
// - renderFileElement(element, ctx, wrapper, pathKey)  
// - renderFilesElement(element, ctx, wrapper, pathKey)
// - renderGroupElement(element, ctx, wrapper, pathKey)

// Force download helper function
function forceDownload(resourceId, fileName) {
    // Try to get URL from thumbnail handler first
    let fileUrl = null;
    if (state.config.getThumbnail) {
        fileUrl = state.config.getThumbnail(resourceId);
    }
    
    if (fileUrl) {
        // Always try to fetch and create blob for true download behavior
        // This prevents files from opening in browser
        const finalUrl = fileUrl.startsWith('http') ? fileUrl : new URL(fileUrl, window.location.href).href;
        
        fetch(finalUrl)
            .then(response => {
                if (!response.ok) {
                    throw new Error(`HTTP error! status: ${response.status}`);
                }
                return response.blob();
            })
            .then(blob => {
                downloadBlob(blob, fileName);
            })
            .catch(error => {
                throw new Error(`File download failed: ${error.message}`);
            });
    } else {
        console.warn('No download URL available for resource:', resourceId);
    }
}

// Helper to download blob with proper cleanup
function downloadBlob(blob, fileName) {
    try {
        const blobUrl = URL.createObjectURL(blob);
        const link = document.createElement('a');
        link.href = blobUrl;
        link.download = fileName;
        link.style.display = 'none';
        
        // Important: add to DOM, click, then remove
        document.body.appendChild(link);
        link.click();
        document.body.removeChild(link);
        
        // Clean up blob URL after download
        setTimeout(() => {
            URL.revokeObjectURL(blobUrl);
        }, 100);
        
    } catch (error) {
        throw new Error(`Blob download failed: ${error.message}`);
    }
}

// Public API
function setFormRoot(element) {
    state.formRoot = element;
}

function configure(config) {
    Object.assign(state.config, config);
}

function setUploadHandler(uploadFn) {
    state.config.uploadFile = uploadFn;
}

function setDownloadHandler(downloadFn) {
    state.config.downloadFile = downloadFn;
}

function setThumbnailHandler(thumbnailFn) {
    state.config.getThumbnail = thumbnailFn;
}

function setMode(mode) {
    if (mode === 'readonly') {
        state.config.readonly = true;
    } else {
        state.config.readonly = false;
    }
}

function getFormData() {
    return validateForm(false);
}

function submitForm() {
    const result = validateForm(false);
    if (result.valid) {
        // Emit event for successful submission
        if (typeof window !== 'undefined' && window.parent) {
            window.parent.postMessage({
                type: 'formSubmit',
                data: result.data,
                schema: state.schema
            }, '*');
        }
    }
    return result;
}

function saveDraft() {
    const result = validateForm(true); // Skip validation for drafts
    // Emit event for draft save
    if (typeof window !== 'undefined' && window.parent) {
        window.parent.postMessage({
            type: 'formDraft',
            data: result.data,
            schema: state.schema
        }, '*');
    }
    return result;
}

function clearForm() {
    if (state.formRoot) {
        clear(state.formRoot);
    }
}

// Export the API
if (typeof window !== 'undefined') {
    window.FormBuilder = {
        setFormRoot,
        renderForm,
        configure,
        setUploadHandler,
        setDownloadHandler,
        setThumbnailHandler,
        setMode,
        getFormData,
        submitForm,
        saveDraft,
        clearForm,
        validateSchema,
        pretty,
        state
    };
}
