/**
 * Asset Management System for Site Builder
 * Handles upload, storage, browsing, and integration with editor blocks.
 *
 * Storage strategy:
 *   - When server API is available (server.py running): files are uploaded to
 *     /storage/assets/ on disk and referenced by URL. No base64 in localStorage.
 *   - When server API is unavailable: falls back to external URL references only.
 *     File uploads (which would produce base64) show an error asking to start the server.
 */
(function() {
    'use strict';

    const ASSETS_KEY = 'sitebuilder-assets';
    const DEPLOY_STATUS_KEY = 'sitebuilder-deploy-status';

    // WHP mode detection
    const isWHP = !!window.WHP_CONFIG;

    // Server API base URL (same origin)
    const API_BASE = '';

    // ==========================================
    // Asset Manager
    // ==========================================
    class AssetManager {
        constructor(editor) {
            this.editor = editor;
            this.assets = [];
            this.modal = null;
            this.resolveSelection = null;
            this.serverAvailable = false;

            // Check server availability, then load assets
            this._checkServer().then(() => {
                this.load();
                this.initUI();
                this.initDragDrop();
                this.initAssetBrowser();
                this.patchImageTraits();
                this.patchVideoTraits();
                this.patchFileEmbedTraits();
                this.initDeployButton();
            });
        }

        // -- Server availability check --
        async _checkServer() {
            if (isWHP) {
                // In WHP mode the server is always available (served by WHP)
                this.serverAvailable = true;
                return;
            }

            try {
                const resp = await fetch(API_BASE + '/api/health', { method: 'GET' });
                if (resp.ok) {
                    const data = await resp.json();
                    this.serverAvailable = data.status === 'ok';
                }
            } catch (e) {
                this.serverAvailable = false;
            }
        }

        // -- Persistence --
        async load() {
            if (isWHP) {
                // WHP mode: load assets from WHP API
                try {
                    const resp = await fetch(WHP_CONFIG.apiUrl + '?action=list_assets&site_id=' + WHP_CONFIG.siteId);
                    if (resp.ok) {
                        const data = await resp.json();
                        if (data.success && Array.isArray(data.assets)) {
                            this.assets = data.assets;
                            this.renderAssetsPanel();
                            return;
                        }
                    }
                } catch (e) {
                    console.warn('Failed to load assets from WHP API:', e.message);
                }
                this.assets = [];
                this.renderAssetsPanel();
                return;
            }

            if (this.serverAvailable) {
                try {
                    const resp = await fetch(API_BASE + '/api/assets');
                    if (resp.ok) {
                        const data = await resp.json();
                        if (data.success && Array.isArray(data.assets)) {
                            this.assets = data.assets;
                            // Also save metadata index to localStorage (no file contents)
                            this._saveMetadataIndex();
                            this.renderAssetsPanel();
                            return;
                        }
                    }
                } catch (e) {
                    console.warn('Failed to load assets from server, using localStorage fallback:', e.message);
                }
            }

            // Fallback: load from localStorage metadata index
            try {
                this.assets = JSON.parse(localStorage.getItem(ASSETS_KEY) || '[]');
                // Filter out any old base64 data URLs from localStorage to prevent quota issues
                this.assets = this.assets.filter(a => !a.url || !a.url.startsWith('data:'));
            } catch(e) { this.assets = []; }
            this.renderAssetsPanel();
        }

        _saveMetadataIndex() {
            // Save only metadata (no file contents) to localStorage as a lightweight index
            try {
                const metadata = this.assets.map(a => ({
                    id: a.id,
                    name: a.name,
                    url: a.url,
                    type: a.type,
                    size: a.size,
                    added: a.added
                }));
                localStorage.setItem(ASSETS_KEY, JSON.stringify(metadata));
            } catch (e) {
                // If localStorage is full, just skip caching
                console.warn('Could not save asset metadata to localStorage:', e.message);
            }
        }

        save() {
            // For server-based storage, save is handled per-operation (upload/delete).
            // We still maintain a lightweight metadata index in localStorage.
            this._saveMetadataIndex();
        }

        async addAsset(name, fileOrDataUrl, type, fileSize) {
            // If it's a File object and server is available, upload to server
            if (fileOrDataUrl instanceof File) {
                return this._uploadFileToServer(fileOrDataUrl, type);
            }

            // If it's a data URL string and server is available, convert to file upload
            if (typeof fileOrDataUrl === 'string' && fileOrDataUrl.startsWith('data:') && this.serverAvailable) {
                try {
                    const blob = await this._dataUrlToBlob(fileOrDataUrl);
                    const file = new File([blob], name, { type: blob.type });
                    return this._uploadFileToServer(file, type);
                } catch (e) {
                    console.warn('Failed to convert data URL to file for upload:', e.message);
                }
            }

            // If it's a data URL but server is NOT available, warn the user
            if (typeof fileOrDataUrl === 'string' && fileOrDataUrl.startsWith('data:') && !this.serverAvailable) {
                this._showServerRequiredError();
                return null;
            }

            // For non-data-URL strings (external URLs), store as reference
            const asset = {
                id: 'asset_' + Date.now() + '_' + Math.random().toString(36).substr(2,6),
                name,
                url: fileOrDataUrl,
                type,
                size: fileSize || 0,
                added: Date.now()
            };
            this.assets.push(asset);
            this.save();
            this.renderAssetsPanel();
            return asset;
        }

        _dataUrlToBlob(dataUrl) {
            return new Promise((resolve, reject) => {
                try {
                    const parts = dataUrl.split(',');
                    const mimeMatch = parts[0].match(/:(.*?);/);
                    const mime = mimeMatch ? mimeMatch[1] : 'application/octet-stream';
                    const byteString = atob(parts[1]);
                    const bytes = new Uint8Array(byteString.length);
                    for (let i = 0; i < byteString.length; i++) {
                        bytes[i] = byteString.charCodeAt(i);
                    }
                    resolve(new Blob([bytes], { type: mime }));
                } catch (e) {
                    reject(e);
                }
            });
        }

        async _uploadFileToServer(file, type) {
            const formData = new FormData();
            formData.append('file', file);

            try {
                let uploadUrl;
                const headers = {};

                if (isWHP) {
                    uploadUrl = WHP_CONFIG.apiUrl + '?action=upload_asset&site_id=' + WHP_CONFIG.siteId;
                    headers['X-CSRF-Token'] = WHP_CONFIG.csrfToken;
                } else {
                    uploadUrl = API_BASE + '/api/assets/upload';
                }

                const resp = await fetch(uploadUrl, {
                    method: 'POST',
                    headers: headers,
                    body: formData
                });

                if (!resp.ok) {
                    const errData = await resp.json().catch(() => ({ error: 'Upload failed' }));
                    throw new Error(errData.error || 'Upload failed with status ' + resp.status);
                }

                const data = await resp.json();
                if (data.success) {
                    // WHP API returns a single asset object; standalone returns an array
                    let serverAsset;
                    if (data.assets && data.assets.length > 0) {
                        serverAsset = data.assets[0];
                    } else if (data.url) {
                        serverAsset = { url: data.url, name: data.name || file.name, type: data.type || 'file' };
                    }

                    if (serverAsset) {
                        // Normalize type for GrapesJS
                        const mimeType = serverAsset.type || '';
                        if (mimeType.startsWith('image/') || ['image','jpg','jpeg','png','gif','webp','svg'].includes(mimeType)) {
                            serverAsset.type = 'image';
                        }

                        this.assets.push(serverAsset);
                        this.save();
                        this.renderAssetsPanel();

                        if (serverAsset.type === 'image') {
                            this.editor.AssetManager.add({ src: serverAsset.url, name: serverAsset.name });
                        }

                        return serverAsset;
                    }
                }
                throw new Error('No asset returned from server');
            } catch (e) {
                console.error('Asset upload failed:', e.message);
                this._showUploadError(e.message);
                return null;
            }
        }

        _showServerRequiredError() {
            if (isWHP) {
                alert('File upload is temporarily unavailable. Please try again.');
            } else {
                const msg = 'File upload requires the development server (server.py).\n\n' +
                            'Start it with: python3 server.py\n\n' +
                            'You can still add assets by pasting external URLs.';
                alert(msg);
            }
        }

        _showUploadError(message) {
            if (isWHP) {
                alert('Asset upload failed: ' + message);
            } else {
                alert('Asset upload failed: ' + message + '\n\nPlease check that server.py is running.');
            }
        }

        addAssetUrl(url) {
            const name = url.split('/').pop().split('?')[0] || 'asset';
            const ext = name.split('.').pop().toLowerCase();
            let type = 'file';
            if (['jpg','jpeg','png','gif','webp','svg','ico'].includes(ext)) type = 'image';
            else if (['mp4','webm','ogg','mov'].includes(ext)) type = 'video';
            else if (ext === 'css') type = 'css';
            else if (ext === 'js') type = 'js';

            const asset = {
                id: 'asset_' + Date.now() + '_' + Math.random().toString(36).substr(2,6),
                name, url, type, size: 0, added: Date.now()
            };
            this.assets.push(asset);
            this.save();
            this.renderAssetsPanel();
            return asset;
        }

        async removeAsset(id) {
            const asset = this.assets.find(a => a.id === id);

            if (isWHP) {
                // WHP mode: delete via WHP API
                if (asset) {
                    try {
                        const filename = asset.name || asset.url.split('/').pop();
                        const resp = await fetch(
                            WHP_CONFIG.apiUrl + '?action=delete_asset&site_id=' + WHP_CONFIG.siteId + '&filename=' + encodeURIComponent(filename),
                            {
                                method: 'DELETE',
                                headers: {
                                    'X-CSRF-Token': WHP_CONFIG.csrfToken
                                }
                            }
                        );
                        if (!resp.ok) {
                            console.warn('WHP asset deletion failed');
                        }
                    } catch (e) {
                        console.warn('Failed to delete asset from WHP:', e.message);
                    }
                }
            } else {
                // Standalone mode: delete from server if applicable
                if (asset && this.serverAvailable && asset.url && asset.url.startsWith('/storage/assets/')) {
                    try {
                        const filename = asset.id || asset.filename || asset.url.split('/').pop();
                        const resp = await fetch(API_BASE + '/api/assets/' + encodeURIComponent(filename), {
                            method: 'DELETE'
                        });
                        if (!resp.ok) {
                            console.warn('Server-side asset deletion failed');
                        }
                    } catch (e) {
                        console.warn('Failed to delete asset from server:', e.message);
                    }
                }
            }

            this.assets = this.assets.filter(a => a.id !== id);
            this.save();
            this.renderAssetsPanel();
        }

        getByType(type) {
            return this.assets.filter(a => a.type === type);
        }

        // -- Assets Panel (left sidebar) --
        initUI() {
            this.renderAssetsPanel();
        }

        renderAssetsPanel() {
            const grid = document.getElementById('assets-grid');
            if (!grid) return;
            grid.innerHTML = '';

            if (this.assets.length === 0) {
                grid.innerHTML = '<div style="grid-column:1/-1;text-align:center;color:#71717a;padding:24px;font-size:13px;">No assets yet. Upload files or paste URLs above.</div>';
                return;
            }

            // Group by category
            const groups = { image: [], video: [], file: [] };
            this.assets.forEach(a => {
                const cat = this._assetCategory(a);
                (groups[cat] || groups.file).push(a);
            });

            // Render images/videos as grid thumbnails
            [...groups.image, ...groups.video].forEach(asset => {
                const item = this._createGridItem(asset);
                grid.appendChild(item);
            });

            // Render other files as list items
            [...groups.file].forEach(asset => {
                const item = this._createListItem(asset);
                grid.appendChild(item);
            });
        }

        _createGridItem(asset) {
            const item = document.createElement('div');
            item.className = 'asset-grid-item';
            item.dataset.assetId = asset.id;

            if (this._assetCategory(asset) === 'image') {
                const img = document.createElement('img');
                img.src = asset.url;
                img.alt = asset.name || '';
                img.style.cssText = 'width:100%;height:100%;object-fit:cover;';
                item.appendChild(img);
            } else {
                const icon = document.createElement('div');
                icon.className = 'asset-icon';
                icon.textContent = '🎬';
                icon.style.cssText = 'font-size:28px;display:flex;align-items:center;justify-content:center;height:100%;';
                item.appendChild(icon);
            }

            // Name label
            const label = document.createElement('div');
            label.className = 'asset-label';
            label.textContent = asset.name;
            item.appendChild(label);

            // Delete btn
            const del = document.createElement('button');
            del.className = 'asset-delete-btn';
            del.textContent = '\u00d7';
            del.addEventListener('click', (e) => {
                e.stopPropagation();
                this.removeAsset(asset.id);
            });
            item.appendChild(del);

            // Click to copy URL
            item.addEventListener('click', () => {
                navigator.clipboard.writeText(asset.url).then(() => {
                    item.style.outline = '2px solid #3b82f6';
                    setTimeout(() => item.style.outline = '', 800);
                });
            });

            return item;
        }

        _createListItem(asset) {
            const item = document.createElement('div');
            item.className = 'asset-list-item';
            item.dataset.assetId = asset.id;
            item.style.gridColumn = '1 / -1';

            const icon = asset.type === 'css' ? '🎨' : asset.type === 'js' ? '⚡' : '📄';

            item.innerHTML = `
                <span class="asset-list-icon">${icon}</span>
                <span class="asset-list-name">${asset.name}</span>
            `;

            const del = document.createElement('button');
            del.className = 'asset-delete-btn';
            del.textContent = '\u00d7';
            del.style.cssText = 'position:static;width:20px;height:20px;flex-shrink:0;';
            del.addEventListener('click', (e) => {
                e.stopPropagation();
                this.removeAsset(asset.id);
            });
            item.appendChild(del);

            item.addEventListener('click', () => {
                navigator.clipboard.writeText(asset.url).then(() => {
                    item.style.outline = '2px solid #3b82f6';
                    setTimeout(() => item.style.outline = '', 800);
                });
            });

            return item;
        }

        // -- Drag & Drop --
        initDragDrop() {
            const container = document.getElementById('assets-container');
            if (!container) return;

            // Add drop zone
            const dropZone = document.createElement('div');
            dropZone.id = 'asset-drop-zone';
            dropZone.className = 'asset-drop-zone';
            dropZone.innerHTML = '<div class="drop-zone-text"><div style="font-size:32px;margin-bottom:8px;">📁</div>Drop files here</div>';
            dropZone.style.display = 'none';
            container.insertBefore(dropZone, container.firstChild);

            container.addEventListener('dragover', (e) => {
                e.preventDefault();
                dropZone.style.display = 'flex';
            });

            container.addEventListener('dragleave', (e) => {
                if (!container.contains(e.relatedTarget)) {
                    dropZone.style.display = 'none';
                }
            });

            container.addEventListener('drop', (e) => {
                e.preventDefault();
                dropZone.style.display = 'none';
                this._handleFiles(e.dataTransfer.files);
            });

            // Also allow drag-and-drop onto the editor canvas
            this._initCanvasDragDrop();
        }

        _initCanvasDragDrop() {
            const self = this;
            // Wait for the GrapesJS canvas iframe to be ready
            const checkCanvas = setInterval(() => {
                const frame = document.querySelector('.gjs-frame');
                if (!frame || !frame.contentDocument) return;
                clearInterval(checkCanvas);

                const canvasDoc = frame.contentDocument;

                canvasDoc.addEventListener('dragover', (e) => {
                    e.preventDefault();
                    e.dataTransfer.dropEffect = 'copy';
                });

                canvasDoc.addEventListener('drop', async (e) => {
                    e.preventDefault();
                    const files = e.dataTransfer.files;
                    if (!files || files.length === 0) return;

                    for (const file of Array.from(files)) {
                        if (!file.type.startsWith('image/')) continue;

                        if (self.serverAvailable) {
                            const asset = await self._uploadFileToServer(file, 'image');
                            if (asset && asset.url) {
                                // Insert image at drop position
                                const selected = self.editor.getSelected();
                                const parent = selected || self.editor.getWrapper();
                                parent.append(`<img src="${asset.url}" alt="${asset.name || file.name}" style="max-width:100%;height:auto;" />`);
                            }
                        } else {
                            self._showServerRequiredError();
                        }
                    }
                });
            }, 500);
        }

        _handleFiles(files) {
            Array.from(files).forEach(file => {
                let type = 'file';
                if (file.type.startsWith('image/')) type = 'image';
                else if (file.type.startsWith('video/')) type = 'video';
                else if (file.name.endsWith('.css')) type = 'css';
                else if (file.name.endsWith('.js')) type = 'js';

                if (this.serverAvailable) {
                    // Upload file directly to server (no base64 conversion)
                    this._uploadFileToServer(file, type);
                } else {
                    // No server: show error for file uploads
                    this._showServerRequiredError();
                }
            });
        }

        // -- Asset Browser Modal --
        initAssetBrowser() {
            // Create modal element
            const modal = document.createElement('div');
            modal.id = 'asset-browser-modal';
            modal.className = 'modal-overlay';
            modal.innerHTML = `
                <div class="modal" style="max-width:720px;width:90%;">
                    <div class="modal-header">
                        <h3 class="modal-title">Browse Assets</h3>
                        <button class="modal-close" id="asset-browser-close">
                            <svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
                                <line x1="18" y1="6" x2="6" y2="18"></line>
                                <line x1="6" y1="6" x2="18" y2="18"></line>
                            </svg>
                        </button>
                    </div>
                    <div class="modal-body" style="padding:0;">
                        <div class="asset-browser-tabs">
                            <button class="asset-tab active" data-type="image">Images</button>
                            <button class="asset-tab" data-type="video">Videos</button>
                            <button class="asset-tab" data-type="file">Files</button>
                            <button class="asset-tab" data-type="all">All</button>
                        </div>
                        <div class="asset-browser-upload" style="padding:12px 16px;border-bottom:1px solid #2d2d3a;">
                            <input type="file" id="asset-browser-file" multiple accept="image/*,video/*,.pdf,.doc,.docx,.xls,.xlsx,.ppt,.pptx,.txt,.csv,.css,.js" style="display:none;">
                            <button id="asset-browser-upload-btn" class="add-page-btn" style="width:100%;">
                                <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
                                    <path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"></path>
                                    <polyline points="17 8 12 3 7 8"></polyline>
                                    <line x1="12" y1="3" x2="12" y2="15"></line>
                                </svg>
                                Upload New File
                            </button>
                        </div>
                        <div id="asset-browser-grid" class="asset-browser-grid"></div>
                    </div>
                </div>
            `;
            document.body.appendChild(modal);
            this.modal = modal;

            // Close handler
            modal.querySelector('#asset-browser-close').addEventListener('click', () => this.closeBrowser());
            modal.addEventListener('click', (e) => { if (e.target === modal) this.closeBrowser(); });

            // Tab switching
            modal.querySelectorAll('.asset-tab').forEach(tab => {
                tab.addEventListener('click', () => {
                    modal.querySelectorAll('.asset-tab').forEach(t => t.classList.remove('active'));
                    tab.classList.add('active');
                    this.renderBrowserGrid(tab.dataset.type);
                });
            });

            // Upload inside browser
            const fileInput = modal.querySelector('#asset-browser-file');
            modal.querySelector('#asset-browser-upload-btn').addEventListener('click', () => fileInput.click());
            fileInput.addEventListener('change', async (e) => {
                const files = Array.from(e.target.files);
                fileInput.value = '';
                // Upload files and re-render after all complete
                const uploads = files.map(file => {
                    let type = 'file';
                    if (file.type.startsWith('image/')) type = 'image';
                    else if (file.type.startsWith('video/')) type = 'video';
                    else if (file.name.endsWith('.css')) type = 'css';
                    else if (file.name.endsWith('.js')) type = 'js';
                    if (this.serverAvailable) {
                        return this._uploadFileToServer(file, type);
                    } else {
                        this._showServerRequiredError();
                        return Promise.resolve(null);
                    }
                });
                await Promise.all(uploads);
                const activeTab = modal.querySelector('.asset-tab.active');
                this.renderBrowserGrid(activeTab?.dataset.type || 'image');
            });
        }

        async openBrowser(filterType) {
            if (!this.modal) return Promise.reject('No modal');

            // Refresh assets from server to pick up uploads from other code paths
            if (isWHP) {
                try {
                    const resp = await fetch(WHP_CONFIG.apiUrl + '?action=list_assets&site_id=' + WHP_CONFIG.siteId);
                    if (resp.ok) {
                        const data = await resp.json();
                        if (data.success && Array.isArray(data.assets)) {
                            this.assets = data.assets;
                        }
                    }
                } catch (e) {
                    // Use existing cached assets on failure
                }
            } else if (this.serverAvailable) {
                try {
                    const resp = await fetch(API_BASE + '/api/assets');
                    if (resp.ok) {
                        const data = await resp.json();
                        if (data.success && Array.isArray(data.assets)) {
                            this.assets = data.assets;
                            this._saveMetadataIndex();
                        }
                    }
                } catch (e) {
                    // Use existing cached assets on failure
                }
            }

            // Set active tab
            const tabs = this.modal.querySelectorAll('.asset-tab');
            tabs.forEach(t => t.classList.remove('active'));
            const targetTab = this.modal.querySelector(`.asset-tab[data-type="${filterType || 'image'}"]`) || tabs[0];
            targetTab.classList.add('active');

            this.renderBrowserGrid(filterType || 'image');
            this.modal.classList.add('visible');

            return new Promise((resolve) => {
                this.resolveSelection = resolve;
            });
        }

        closeBrowser(selectedAsset) {
            if (this.modal) this.modal.classList.remove('visible');
            if (this.resolveSelection) {
                this.resolveSelection(selectedAsset || null);
                this.resolveSelection = null;
            }
        }

        // Determine the simple asset category from type/name
        _assetCategory(asset) {
            const t = (asset.type || '').toLowerCase();
            const n = (asset.name || '').toLowerCase();
            if (t === 'image' || t.startsWith('image/') || n.match(/\.(jpg|jpeg|png|gif|webp|svg|ico)$/)) return 'image';
            if (t === 'video' || t.startsWith('video/') || n.match(/\.(mp4|webm|ogg|mov|avi)$/)) return 'video';
            return 'file';
        }

        renderBrowserGrid(type) {
            const grid = this.modal.querySelector('#asset-browser-grid');
            let items;
            if (type === 'all') {
                items = this.assets;
            } else {
                items = this.assets.filter(a => this._assetCategory(a) === type);
            }

            if (items.length === 0) {
                grid.textContent = '';
                const emptyZone = this._createDropZone(type);
                grid.appendChild(emptyZone);
                return;
            }

            grid.textContent = '';

            // Drop zone hint at top
            const dropHint = document.createElement('div');
            dropHint.id = 'browser-drop-zone';
            dropHint.style.cssText = 'grid-column:1/-1;text-align:center;padding:16px;color:#71717a;border:2px dashed #3f3f46;border-radius:8px;margin:0 0 8px;font-size:12px;transition:border-color 0.2s,background 0.2s;';
            dropHint.textContent = 'Drop files here to upload';
            grid.appendChild(dropHint);
            this._attachDropHandlers(grid);

            items.forEach(asset => {
                const cat = this._assetCategory(asset);
                const item = document.createElement('div');
                item.className = 'asset-browser-item';

                if (cat === 'image') {
                    const img = document.createElement('img');
                    img.src = asset.url;
                    img.alt = asset.name || '';
                    img.style.cssText = 'width:100%;height:100%;object-fit:cover;';
                    img.onerror = function() {
                        this.replaceWith(Object.assign(document.createElement('div'), {
                            className: 'asset-icon', textContent: '🖼️',
                            style: 'font-size:36px;display:flex;align-items:center;justify-content:center;height:100%;'
                        }));
                    };
                    item.appendChild(img);
                } else if (cat === 'video') {
                    const icon = document.createElement('div');
                    icon.className = 'asset-icon';
                    icon.style.cssText = 'font-size:36px;display:flex;align-items:center;justify-content:center;height:100%;';
                    icon.textContent = '🎬';
                    item.appendChild(icon);
                } else {
                    const ext = (asset.name || '').split('.').pop().toLowerCase();
                    const iconChar = ext === 'pdf' ? '📕' : ext === 'css' ? '🎨' : ext === 'js' ? '⚡' : '📄';
                    const icon = document.createElement('div');
                    icon.className = 'asset-icon';
                    icon.style.cssText = 'font-size:36px;display:flex;align-items:center;justify-content:center;height:100%;';
                    icon.textContent = iconChar;
                    item.appendChild(icon);
                }

                const nameDiv = document.createElement('div');
                nameDiv.className = 'asset-browser-item-name';
                nameDiv.textContent = asset.name;
                item.appendChild(nameDiv);

                item.addEventListener('click', () => {
                    this.closeBrowser(asset);
                });

                grid.appendChild(item);
            });
        }

        _createDropZone(type) {
            const zone = document.createElement('div');
            zone.id = 'browser-drop-zone';
            zone.style.cssText = 'grid-column:1/-1;text-align:center;padding:48px 24px;color:#71717a;border:2px dashed #3f3f46;border-radius:8px;margin:16px;cursor:pointer;transition:border-color 0.2s,background 0.2s;';
            const iconEl = document.createElement('div');
            iconEl.style.cssText = 'font-size:36px;margin-bottom:12px;';
            iconEl.textContent = '📁';
            zone.appendChild(iconEl);
            const msgEl = document.createElement('div');
            msgEl.textContent = 'No ' + (type === 'all' ? '' : type + ' ') + 'assets yet.';
            zone.appendChild(msgEl);
            const hintEl = document.createElement('div');
            hintEl.style.cssText = 'font-size:12px;margin-top:4px;';
            hintEl.textContent = 'Drag files here or click Upload above.';
            zone.appendChild(hintEl);

            // Attach drop handlers to the zone's parent after it's added
            setTimeout(() => {
                const grid = zone.parentElement;
                if (grid) this._attachDropHandlers(grid);
            }, 0);

            return zone;
        }

        _attachDropHandlers(grid) {
            // Avoid attaching multiple times
            if (grid._dropAttached) return;
            grid._dropAttached = true;
            const self = this;

            grid.addEventListener('dragover', (e) => {
                e.preventDefault();
                e.dataTransfer.dropEffect = 'copy';
                const zone = grid.querySelector('#browser-drop-zone');
                if (zone) {
                    zone.style.borderColor = '#3b82f6';
                    zone.style.background = 'rgba(59,130,246,0.1)';
                }
            });

            grid.addEventListener('dragleave', (e) => {
                if (!grid.contains(e.relatedTarget)) {
                    const zone = grid.querySelector('#browser-drop-zone');
                    if (zone) {
                        zone.style.borderColor = '#3f3f46';
                        zone.style.background = '';
                    }
                }
            });

            grid.addEventListener('drop', async (e) => {
                e.preventDefault();
                const zone = grid.querySelector('#browser-drop-zone');
                if (zone) {
                    zone.style.borderColor = '#3f3f46';
                    zone.style.background = '';
                    zone.textContent = 'Uploading...';
                }

                const files = e.dataTransfer.files;
                if (!files || files.length === 0) return;

                const uploads = Array.from(files).map(file => {
                    const ftype = file.type.startsWith('image/') ? 'image' : file.type.startsWith('video/') ? 'video' : 'file';
                    if (self.serverAvailable) {
                        return self._uploadFileToServer(file, ftype);
                    }
                    self._showServerRequiredError();
                    return Promise.resolve(null);
                });

                await Promise.all(uploads);
                const activeTab = self.modal.querySelector('.asset-tab.active');
                self.renderBrowserGrid(activeTab?.dataset.type || 'image');
            });
        }

        // -- Patch Image Block Traits --
        patchImageTraits() {
            const editor = this.editor;
            const self = this;

            // Override default image component to add "Browse Assets" button
            const origImageType = editor.DomComponents.getType('image');
            const origModel = origImageType ? origImageType.model : null;

            editor.DomComponents.addType('image', {
                model: {
                    defaults: {
                        traits: [
                            { type: 'text', label: 'Image URL', name: 'src', placeholder: 'https://...' },
                            { type: 'text', label: 'Alt Text', name: 'alt' },
                            { type: 'text', label: 'Title', name: 'title' },
                            {
                                type: 'button',
                                label: '',
                                text: '📁 Browse Assets',
                                full: true,
                                command: () => {
                                    self.openBrowser('image').then(asset => {
                                        if (!asset) return;
                                        const selected = editor.getSelected();
                                        if (selected) {
                                            selected.addAttributes({ src: asset.url });
                                            const el = selected.getEl();
                                            if (el) el.src = asset.url;
                                        }
                                    });
                                }
                            }
                        ]
                    }
                }
            });
        }

        // -- Video traits are defined directly in editor.js component types --
        // Browse Video Assets buttons reference window.assetManager.openBrowser()
        patchVideoTraits() {
            // No-op: browse buttons are now built into video-wrapper and video-section types
        }

        // -- Patch File/PDF Block Traits --
        patchFileEmbedTraits() {
            const editor = this.editor;
            const self = this;

            const browseFileTrait = {
                type: 'button',
                label: '',
                text: '📁 Browse File Assets',
                full: true,
                command: () => {
                    self.openBrowser('file').then(asset => {
                        if (!asset) return;
                        const selected = editor.getSelected();
                        if (!selected) return;

                        selected.addAttributes({ fileUrl: asset.url });

                        const url = asset.url;
                        const height = selected.getAttributes().frameHeight || 600;
                        const isPdf = url.match(/\.pdf(\?.*)?$/i);

                        const iframe = selected.components().find(c => c.getClasses().includes('file-embed-frame'));
                        const placeholder = selected.components().find(c => c.getClasses().includes('file-embed-placeholder'));
                        const downloadCard = selected.components().find(c => c.getClasses().includes('file-download-card'));

                        if (isPdf) {
                            // PDF: embed in iframe
                            if (iframe) {
                                iframe.addAttributes({ src: url });
                                iframe.addStyle({ display: 'block', height: height + 'px' });
                                const el = iframe.getEl();
                                if (el) { el.src = url; el.style.display = 'block'; el.style.height = height + 'px'; }
                            }
                            if (downloadCard) {
                                downloadCard.addStyle({ display: 'none' });
                                const el = downloadCard.getEl();
                                if (el) el.style.display = 'none';
                            }
                        } else {
                            // Non-PDF: show download card
                            if (iframe) {
                                iframe.addStyle({ display: 'none' });
                                const el = iframe.getEl();
                                if (el) el.style.display = 'none';
                            }
                            if (downloadCard) {
                                const fileName = decodeURIComponent(url.split('/').pop().split('?')[0]) || 'File';
                                downloadCard.addAttributes({ href: url });
                                downloadCard.addStyle({ display: 'flex' });
                                const cardEl = downloadCard.getEl();
                                if (cardEl) {
                                    cardEl.href = url;
                                    cardEl.style.display = 'flex';
                                    const nameNode = cardEl.querySelector('.file-download-name');
                                    if (nameNode) nameNode.textContent = fileName;
                                }
                            }
                        }
                        if (placeholder) {
                            placeholder.addStyle({ display: 'none' });
                            const el = placeholder.getEl();
                            if (el) el.style.display = 'none';
                        }
                    });
                }
            };

            const fileEmbedType = editor.DomComponents.getType('file-embed');
            if (fileEmbedType) {
                const origDefaults = fileEmbedType.model.prototype.defaults;
                const origTraits = Array.isArray(origDefaults.traits) ? origDefaults.traits : [];

                editor.DomComponents.addType('file-embed', {
                    model: {
                        defaults: {
                            traits: [
                                ...origTraits.filter(t => t.type !== 'button'),
                                browseFileTrait,
                                ...origTraits.filter(t => t.type === 'button')
                            ]
                        }
                    }
                });
            }
        }

        // -- Deploy Button (standalone) / Go Live button (WHP) --
        initDeployButton() {
            const navRight = document.querySelector('.nav-right');
            if (!navRight) return;

            if (isWHP) {
                // In WHP mode: hide Export button and add Go Live / Coming Soon toggle
                const exportBtn = document.getElementById('btn-export');
                if (exportBtn) exportBtn.style.display = 'none';

                const previewBtn = document.getElementById('btn-preview');
                const liveBtn = document.createElement('button');
                liveBtn.id = 'btn-go-live';
                liveBtn.className = 'nav-btn';
                liveBtn.title = 'Toggle site visibility';
                this._siteIsLive = true; // default: live (files are in docroot)

                const updateLiveBtn = (isLive) => {
                    this._siteIsLive = isLive;
                    if (isLive) {
                        liveBtn.innerHTML = `
                            <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
                                <circle cx="12" cy="12" r="10"></circle>
                                <polyline points="12 6 12 12 16 14"></polyline>
                            </svg>
                            <span>Coming Soon</span>
                        `;
                        liveBtn.title = 'Switch site to Coming Soon mode';
                        liveBtn.classList.remove('nav-btn-live');
                        liveBtn.classList.add('nav-btn-coming-soon');
                    } else {
                        liveBtn.innerHTML = `
                            <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
                                <path d="M22 11.08V12a10 10 0 1 1-5.93-9.14"></path>
                                <polyline points="22 4 12 14.01 9 11.01"></polyline>
                            </svg>
                            <span>Go Live</span>
                        `;
                        liveBtn.title = 'Publish site and make it live';
                        liveBtn.classList.remove('nav-btn-coming-soon');
                        liveBtn.classList.add('nav-btn-live');
                    }
                };

                // Check current status from API
                fetch(WHP_CONFIG.apiUrl + '?action=site_status&site_id=' + WHP_CONFIG.siteId)
                    .then(r => r.json())
                    .then(data => {
                        if (data.success) {
                            updateLiveBtn(!data.coming_soon);
                        }
                    })
                    .catch(() => updateLiveBtn(true));

                liveBtn.addEventListener('click', async () => {
                    const newIsLive = !this._siteIsLive;
                    liveBtn.disabled = true;

                    try {
                        const resp = await fetch(WHP_CONFIG.apiUrl + '?action=set_site_status', {
                            method: 'POST',
                            headers: {
                                'Content-Type': 'application/json',
                                'X-CSRF-Token': WHP_CONFIG.csrfToken
                            },
                            body: JSON.stringify({
                                site_id: WHP_CONFIG.siteId,
                                coming_soon: !newIsLive
                            })
                        });
                        const data = await resp.json();
                        if (data.success) {
                            updateLiveBtn(newIsLive);
                            const msg = newIsLive
                                ? 'Site is now live!'
                                : 'Site set to Coming Soon mode.';
                            if (window.whpInt) window.whpInt.showNotification(msg, 'success');
                        } else {
                            if (window.whpInt) window.whpInt.showNotification(data.error || 'Failed to update status', 'error');
                        }
                    } catch (e) {
                        if (window.whpInt) window.whpInt.showNotification('Failed to update status: ' + e.message, 'error');
                    }
                    liveBtn.disabled = false;
                });

                navRight.insertBefore(liveBtn, previewBtn);
                return; // Skip standalone deploy setup
            }

            // Standalone mode: add deploy button
            const previewBtn = document.getElementById('btn-preview');
            const deployBtn = document.createElement('button');
            deployBtn.id = 'btn-deploy';
            deployBtn.className = 'nav-btn';
            deployBtn.title = 'Deploy to Server';
            deployBtn.innerHTML = `
                <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
                    <path d="M22 2L11 13"></path>
                    <path d="M22 2L15 22L11 13L2 9L22 2Z"></path>
                </svg>
                <span>Deploy</span>
            `;
            navRight.insertBefore(deployBtn, previewBtn);

            deployBtn.addEventListener('click', () => this.showDeployModal());
            this.initDeployModal();
        }

        initDeployModal() {
            const modal = document.createElement('div');
            modal.id = 'deploy-modal';
            modal.className = 'modal-overlay';
            modal.innerHTML = `
                <div class="modal" style="max-width:560px;">
                    <div class="modal-header">
                        <h3 class="modal-title">Deploy Site</h3>
                        <button class="modal-close" id="deploy-close">
                            <svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
                                <line x1="18" y1="6" x2="6" y2="18"></line>
                                <line x1="6" y1="6" x2="18" y2="18"></line>
                            </svg>
                        </button>
                    </div>
                    <div class="modal-body">
                        <p class="export-description">Deploy your site to <strong>abc.streamers.channel</strong> via SFTP.</p>
                        <div class="deploy-info">
                            <div class="deploy-info-row"><span>Host:</span><span>whp01.cloud-hosting.io</span></div>
                            <div class="deploy-info-row"><span>User:</span><span>shadowdao</span></div>
                            <div class="deploy-info-row"><span>Directory:</span><span>wildcard.streamers.channel/</span></div>
                            <div class="deploy-info-row"><span>Pages:</span><span id="deploy-page-count">0</span></div>
                            <div class="deploy-info-row"><span>Assets:</span><span id="deploy-asset-count">0</span></div>
                        </div>
                        <div class="export-options">
                            <label class="checkbox-label">
                                <input type="checkbox" id="deploy-extract-css" checked> Extract CSS to external files
                            </label>
                            <label class="checkbox-label">
                                <input type="checkbox" id="deploy-include-fonts" checked> Include Google Fonts
                            </label>
                        </div>
                        <div id="deploy-status" class="deploy-status" style="display:none;">
                            <div class="deploy-progress-bar"><div class="deploy-progress-fill" id="deploy-progress"></div></div>
                            <div id="deploy-status-text" class="deploy-status-text">Preparing files...</div>
                        </div>
                    </div>
                    <div class="modal-footer">
                        <button class="modal-btn modal-btn-secondary" id="deploy-cancel">Cancel</button>
                        <button class="modal-btn modal-btn-secondary" id="deploy-export-zip">
                            <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
                                <path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"></path>
                                <polyline points="7 10 12 15 17 10"></polyline>
                                <line x1="12" y1="15" x2="12" y2="3"></line>
                            </svg>
                            Export ZIP
                        </button>
                        <button class="modal-btn modal-btn-primary" id="deploy-start">
                            <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
                                <path d="M22 2L11 13"></path>
                                <path d="M22 2L15 22L11 13L2 9L22 2Z"></path>
                            </svg>
                            Deploy Now
                        </button>
                    </div>
                </div>
            `;
            document.body.appendChild(modal);
            this.deployModal = modal;

            modal.querySelector('#deploy-close').addEventListener('click', () => this.closeDeployModal());
            modal.querySelector('#deploy-cancel').addEventListener('click', () => this.closeDeployModal());
            modal.addEventListener('click', (e) => { if (e.target === modal) this.closeDeployModal(); });

            modal.querySelector('#deploy-export-zip').addEventListener('click', () => this.exportDeployZip());
            modal.querySelector('#deploy-start').addEventListener('click', () => this.startDeploy());
        }

        showDeployModal() {
            // Save current page
            if (window.editor) {
                const pages = window.sitePages;
                const currentPageId = pages?.find(p => true)?.id; // simple fallback
            }

            const pageCount = window.sitePages?.length || 0;
            const assetCount = this.assets.length;

            this.deployModal.querySelector('#deploy-page-count').textContent = pageCount;
            this.deployModal.querySelector('#deploy-asset-count').textContent = assetCount;
            this.deployModal.querySelector('#deploy-status').style.display = 'none';
            this.deployModal.classList.add('visible');
        }

        closeDeployModal() {
            this.deployModal.classList.remove('visible');
        }

        // Generate deployment files structure
        generateDeployFiles() {
            const pages = window.sitePages || [];
            const extractCss = this.deployModal.querySelector('#deploy-extract-css').checked;
            const includeFonts = this.deployModal.querySelector('#deploy-include-fonts').checked;
            const headCode = localStorage.getItem('sitebuilder-head-code') || '';
            const sitewideCss = localStorage.getItem('sitebuilder-sitewide-css') || '';
            const files = {};

            // Collect all page CSS into one file if extracting
            let combinedCss = '';
            const fontsLink = includeFonts
                ? '<link rel="preconnect" href="https://fonts.googleapis.com">\n    <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>\n    <link href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&family=Roboto:wght@300;400;500;700&family=Open+Sans:wght@300;400;500;600;700&family=Poppins:wght@300;400;500;600;700&family=Montserrat:wght@300;400;500;600;700&family=Playfair+Display:wght@400;500;600;700&family=Merriweather:wght@300;400;700&family=Source+Code+Pro:wght@400;500;600&display=swap" rel="stylesheet">'
                : '';

            const baseCss = `/* Reset & Base */
* { margin: 0; padding: 0; box-sizing: border-box; }
body { font-family: Inter, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; line-height: 1.5; -webkit-font-smoothing: antialiased; }
img, video { max-width: 100%; height: auto; }
img { display: block; }
a { color: inherit; }
:focus-visible { outline: 2px solid #3b82f6; outline-offset: 2px; }
:focus:not(:focus-visible) { outline: none; }
a, button, input, select, textarea { min-height: 44px; }
button, [type="submit"] { cursor: pointer; touch-action: manipulation; }
@media (max-width: 768px) {
    .row { flex-direction: column !important; }
    .row .cell { flex-basis: 100% !important; width: 100% !important; }
    section { padding-left: 16px !important; padding-right: 16px !important; }
}
@media (max-width: 480px) {
    .row { flex-direction: column !important; }
    .row .cell { flex-basis: 100% !important; width: 100% !important; }
    h1 { font-size: 32px !important; }
    h2 { font-size: 28px !important; }
}
@media (prefers-reduced-motion: reduce) {
    *, *::before, *::after { animation-duration: 0.01ms !important; transition-duration: 0.01ms !important; }
}`;

            if (extractCss) {
                combinedCss = baseCss + '\n\n/* Site-wide Styles */\n' + sitewideCss + '\n';
            }

            pages.forEach(page => {
                const pageCss = page.css || '';

                if (extractCss) {
                    combinedCss += `\n/* Page: ${page.name} */\n${pageCss}\n`;

                    // HTML without inline CSS
                    const html = `<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta name="color-scheme" content="light">
    <title>${page.name}</title>
    ${fontsLink}
    <link rel="stylesheet" href="assets/css/styles.css">
    ${headCode}
</head>
<body>
<a href="#main-content" style="position:absolute;left:-9999px;top:auto;width:1px;height:1px;overflow:hidden;">Skip to main content</a>
<main id="main-content">
${page.html || ''}
</main>
</body>
</html>`;
                    files[page.slug + '.html'] = html;
                } else {
                    // Inline CSS (use existing generatePageHtml pattern)
                    const html = `<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta name="color-scheme" content="light">
    <title>${page.name}</title>
    ${fontsLink}
    ${headCode}
    <style>
        ${baseCss}
        /* Site-wide Styles */
        ${sitewideCss}
        /* Page Styles */
        ${pageCss}
    </style>
</head>
<body>
<a href="#main-content" style="position:absolute;left:-9999px;top:auto;width:1px;height:1px;overflow:hidden;">Skip to main content</a>
<main id="main-content">
${page.html || ''}
</main>
</body>
</html>`;
                    files[page.slug + '.html'] = html;
                }
            });

            if (extractCss) {
                files['assets/css/styles.css'] = combinedCss;
            }

            // For server-stored assets, they are actual files on disk referenced by URL.
            // For deploy, we note them so the ZIP export can fetch and include them.
            this.assets.forEach(asset => {
                if (asset.url && asset.url.startsWith('/storage/assets/')) {
                    // Server-stored asset: include as a fetchable file reference
                    const subfolder = asset.type === 'image' ? 'images' :
                                     asset.type === 'video' ? 'videos' :
                                     asset.type === 'css' ? 'css' :
                                     asset.type === 'js' ? 'js' : 'files';
                    files[`assets/${subfolder}/${asset.name}`] = { fetchUrl: asset.url, type: 'fetch' };
                } else if (asset.url && !asset.url.startsWith('data:')) {
                    // External URL assets - referenced directly, no file needed
                } else if (asset.url && asset.url.startsWith('data:')) {
                    // Legacy data URL assets (should not exist with new system)
                    const subfolder = asset.type === 'image' ? 'images' :
                                     asset.type === 'video' ? 'videos' :
                                     asset.type === 'css' ? 'css' :
                                     asset.type === 'js' ? 'js' : 'files';
                    files[`assets/${subfolder}/${asset.name}`] = asset.url;
                }
            });

            return files;
        }

        async exportDeployZip() {
            // Load JSZip if needed
            if (typeof JSZip === 'undefined') {
                await new Promise((resolve, reject) => {
                    const s = document.createElement('script');
                    s.src = 'https://cdnjs.cloudflare.com/ajax/libs/jszip/3.10.1/jszip.min.js';
                    s.onload = resolve;
                    s.onerror = reject;
                    document.head.appendChild(s);
                });
            }

            const files = this.generateDeployFiles();
            const zip = new JSZip();

            for (const [path, content] of Object.entries(files)) {
                if (content && typeof content === 'object' && content.type === 'fetch') {
                    // Fetch file from server and add to ZIP
                    try {
                        const resp = await fetch(content.fetchUrl);
                        if (resp.ok) {
                            const blob = await resp.blob();
                            zip.file(path, blob);
                        }
                    } catch (e) {
                        console.warn('Could not fetch asset for ZIP:', content.fetchUrl, e.message);
                    }
                } else if (typeof content === 'string' && content.startsWith('data:')) {
                    // Convert data URL to binary (legacy)
                    const parts = content.split(',');
                    const byteString = atob(parts[1]);
                    const bytes = new Uint8Array(byteString.length);
                    for (let i = 0; i < byteString.length; i++) {
                        bytes[i] = byteString.charCodeAt(i);
                    }
                    zip.file(path, bytes);
                } else if (typeof content === 'string') {
                    zip.file(path, content);
                }
            }

            const blob = await zip.generateAsync({ type: 'blob' });
            const url = URL.createObjectURL(blob);
            const a = document.createElement('a');
            a.href = url;
            a.download = 'site-deploy.zip';
            document.body.appendChild(a);
            a.click();
            document.body.removeChild(a);
            URL.revokeObjectURL(url);
        }

        async startDeploy() {
            const statusDiv = this.deployModal.querySelector('#deploy-status');
            const progressBar = this.deployModal.querySelector('#deploy-progress');
            const statusText = this.deployModal.querySelector('#deploy-status-text');
            statusDiv.style.display = 'block';

            const updateProgress = (pct, msg) => {
                progressBar.style.width = pct + '%';
                statusText.textContent = msg;
            };

            updateProgress(10, 'Generating files...');

            const files = this.generateDeployFiles();
            const fileList = Object.keys(files);

            updateProgress(20, `Prepared ${fileList.length} files. Creating deploy package...`);

            // Export as ZIP for manual SFTP upload
            try {
                await this.exportDeployZip();
                updateProgress(100, 'Deploy package downloaded! Upload via SFTP to complete deployment.');
                statusText.innerHTML = `ZIP downloaded!<br><br>
                    <code style="font-size:11px;color:#a1a1aa;display:block;margin-top:8px;">
                    # Upload via terminal:<br>
                    cd ~/Downloads<br>
                    unzip site-deploy.zip -d site-deploy<br>
                    sftp shadowdao@whp01.cloud-hosting.io<br>
                    cd wildcard.streamers.channel<br>
                    put -r site-deploy/*<br>
                    </code>`;
            } catch (err) {
                updateProgress(100, 'Error: ' + err.message);
            }
        }
    }

    // Initialize when editor is ready
    function waitForEditor() {
        if (window.editor) {
            window.assetManager = new AssetManager(window.editor);
        } else {
            setTimeout(waitForEditor, 200);
        }
    }
    waitForEditor();

})();
