/**
 * Cast Conductor Proprietary License v5
 * SPDX-License-Identifier: LicenseRef-CastConductor-Proprietary-v5
 * 
 * Copyright (c) 2025 CastConductor.com. All Rights Reserved.
 * 
 * This file is part of Cast Conductor ("Software"). The Software and its source
 * code constitute proprietary, confidential, and trade secret information of
 * CastConductor.com ("Company"). Any access or use is governed strictly by the
 * Cast Conductor Proprietary License v5 ("License"). By installing, copying,
 * accessing, compiling, or otherwise using the Software you agree to be bound by
 * all terms of the License. If you do not agree, you must cease use immediately.
 * 
 * Key Terms (Summary – see full License for binding terms):
 *  1. No Redistribution: You may not publish, distribute, sublicense, rent,
 *     lease, transfer, sell, or otherwise make the Software (or any derivative)
 *     available to any third party without prior written consent of Company.
 *  2. No Modification: Modification, reverse engineering, decompilation, or
 *     disassembly is prohibited except to the limited extent expressly permitted
 *     by applicable law that cannot be contractually waived.
 *  3. Confidentiality: Treat all source code and related artifacts as Company
 *     Confidential Information. Maintain at least the same degree of care as for
 *     your own confidential materials, and not less than reasonable care.
 *  4. No Patent License: No express or implied patent rights are granted. Future
 *     patents (if any) are fully reserved.
 *  5. No Trademark License: Company names, marks, and logos may not be used
 *     without prior written permission.
 *  6. Limited Internal Use: Use is limited solely to internal evaluation and
 *     operation of licensed Cast Conductor deployments. Commercial hosting or
 *     resale as a service requires a separate written agreement.
 *  7. Telemetry & License Validation: The Software may periodically transmit a
 *     hashed installation identifier, domain (or site ID), plugin/app version,
 *     and a truncated (non-reversible) fragment of the license key solely to
 *     validate activation status and enforce licensing. This minimal "phone home"
 *     check contains no personal or content data. If optional telemetry is later
 *     introduced it will be limited to aggregate operational metrics (no PII),
 *     fully documented, and optionally disableable per published instructions.
 *  8. Third-Party Components: The Software may include open source components
 *     covered by their own licenses. See THIRD-PARTY-NOTICES.md. Those licenses
 *     govern their respective components; this License governs all remaining code.
 *  9. Export Compliance: You are responsible for compliance with all applicable
 *     export control and sanctions laws.
 * 10. Warranty Disclaimer: THE SOFTWARE IS PROVIDED "AS IS" WITHOUT WARRANTY OF
 *     ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO MERCHANTABILITY,
 *     FITNESS FOR A PARTICULAR PURPOSE, TITLE, AND NON-INFRINGEMENT.
 * 11. Limitation of Liability: IN NO EVENT WILL COMPANY OR AUTHORS BE LIABLE FOR
 *     ANY INDIRECT, SPECIAL, INCIDENTAL, CONSEQUENTIAL, EXEMPLARY, OR PUNITIVE
 *     DAMAGES, OR LOST PROFITS, EVEN IF ADVISED OF THE POSSIBILITY.
 * 12. Acceptance: Use of the Software constitutes acceptance of the License.
 * 13. Enforcement: Unauthorized reproduction or distribution may result in civil
 *     and criminal penalties and will be prosecuted to the maximum extent allowed
 *     by law.
 * 
 * Authoritative EULA: EULA-v5.1.md (repository root – private) and https://castconductor.com/eula
 * Precedence: If this summary conflicts with the EULA, the EULA governs.
 * Revision: Current EULA revision v5.1 (subject to update; check EULA for current enterprise thresholds).
 * 
 * Full License text available from: licensing@castconductor.com
 * Security reports: security@castconductor.com
 * Commercial inquiries: licensing@castconductor.com
 * 
 * END OF HEADER
 * 
 * Content Block CRUD & Listing API (Phase 2.7 Step 3 extraction)
 */

export async function loadContentBlock(editor, contentBlockId) {
	try {
		console.debug('[CC] loadContentBlock:start', contentBlockId);
		editor.showLoader && editor.showLoader('Loading content block...');
		const knownVersion = editor.blockConfigVersionIndex[String(contentBlockId)];
		const cached = editor.configCache[String(contentBlockId)];
		if (knownVersion && cached && String(cached.version) === String(knownVersion)) {
			console.debug('[CC] loadContentBlock:using cache', contentBlockId);
			// Normalize cached config (ensure token_layers realized) to survive hard refreshes
			const cfg = cached.config || {};
			try {
				const tl = (cfg?.visual_config && Array.isArray(cfg.visual_config.token_layers)) ? cfg.visual_config.token_layers : [];
				if (tl.length) {
					if (!Array.isArray(cfg.layers) || cfg.layers.length === 0) {
						cfg.layers = tl.map(l => ({ ...l }));
					} else {
						const byId = new Map(cfg.layers.filter(l=>l&&l.id).map(l=>[l.id,l]));
						tl.forEach(l => { if (l && l.id && !byId.has(l.id)) cfg.layers.push({ ...l }); });
					}
				}
			} catch(_) { /* non-fatal */ }
			editor.currentConfig = cfg;
			// Reset baseline guides flag so they recalculate for new container bounds
			editor._baselineGuidesAdded = false;
			editor._guidesHorizontal = []; editor._guidesVertical = [];
			editor.currentContentBlockId = contentBlockId;
			editor.currentContentBlockType = cached.content_block_type || 'track_info';
			// Persist last selected block id to avoid unintended auto-switch to first item
			try { localStorage.setItem('ccveLastBlockId', String(contentBlockId)); } catch(_) {}
			editor.populateControls(editor.currentConfig);
			// Auto-populate typography from first text layer to show layer-specific colors
			try {
				const firstTextLayer = Array.isArray(cfg.layers) ? cfg.layers.find(l => l && l.kind === 'token-text' && l.visible !== false) : null;
				if (firstTextLayer && firstTextLayer.id && editor.populateTypographyFromLayer) {
					editor.populateTypographyFromLayer(firstTextLayer.id);
				}
			} catch(_) {}
			// Ensure auto-preview watchers present before first immediate preview
			try { editor.attachAutoPreviewControlWatchers && editor.attachAutoPreviewControlWatchers(); } catch(e){ console.warn('[CC] attach watchers (cache) failed', e); }
			// Ensure preview mode is active (normal-mode rendering)
			try { editor._editorMode = 'preview'; editor._suppressPreviewForBackground = false; } catch(_){ }
			editor.triggerPreview(true); // immediate preview (server path inside generatePreview)
			// Also explicitly fetch server preview for robustness (mirrors prior working flow)
			try { await fetchServerPreview(editor, contentBlockId); } catch(_) {}
			try { editor._renderTokenLayers && editor._renderTokenLayers(); } catch(_) {}
			try { editor.refreshLiveTokens && editor.refreshLiveTokens(); } catch(_) {}
			editor.unsavedChanges = false; editor.updateSaveButton && editor.updateSaveButton();
			try { const dup = document.getElementById('canvas-duplicate-as-new'); if (dup) dup.disabled = false; } catch(_){}
			return;
		}
		const url = `${castconductorCanvasAjax.rest_url}castconductor/v5/canvas-editor/config/${contentBlockId}`;
		const response = await fetch(url, { method:'GET', headers:{ 'X-WP-Nonce': castconductorCanvasAjax.nonce } });
		const text = await response.text();
		let data = null; try { data = JSON.parse(text); } catch(e) { console.error('Non-JSON config response', text); throw new Error(`Invalid JSON (${response.status})`); }
		if (data.success) {
			console.debug('[CC] loadContentBlock:server config ok', data);
			// DEBUG: Log overlay config received from server
			console.log('[CC][LOAD DEBUG] overlay from server:', JSON.stringify(data.config?.overlay));
			console.log('[CC][LOAD DEBUG] background.layers from server:', JSON.stringify(data.config?.background?.layers));
			// DEBUG: Log layers from server config to diagnose static-image not rendering
			console.log('[CC][LOAD DEBUG] config.layers from server:', JSON.stringify(data.config?.layers));
			console.log('[CC][LOAD DEBUG] visual_config.layers from server:', JSON.stringify(data.config?.visual_config?.layers));
			console.log('[CC][LOAD DEBUG] visual_config.token_layers from server:', JSON.stringify(data.config?.visual_config?.token_layers));
			// DEBUG: Log layer groups from server
			console.log('[CC][LOAD DEBUG] layerGroups from server:', JSON.stringify(data.config?.layerGroups));
			console.log('[CC][LOAD DEBUG] layers with groupId:', data.config?.layers?.filter(l => l?.groupId)?.map(l => ({ id: l.id, groupId: l.groupId })));
			// Normalize/repair minimal expected config structure to avoid downstream hiding (artwork disappeared regression)
			const cfg = data.config || {};
			cfg.artwork = cfg.artwork || { enabled:true, size:{ width:240, height:240 }, position:'left', gap:12, border_radius:0 };
			if (typeof cfg.artwork.enabled === 'undefined') cfg.artwork.enabled = true;
			if (typeof cfg.artwork.gap === 'undefined') cfg.artwork.gap = 12;
			if (!cfg.artwork.size) cfg.artwork.size = { width:240, height:240 };
			// Merge legacy_overrides from visual_config side-channel so converted legacy layers don't rehydrate
			try {
				const ov = cfg?.visual_config?.legacy_overrides;
				if (ov && typeof ov === 'object') {
					cfg.legacy_overrides = { ...(cfg.legacy_overrides||{}), ...ov };
				}
			} catch(_) {}
			editor.currentConfig = cfg;
			// Reset baseline guides flag so they recalculate for new container bounds
			editor._baselineGuidesAdded = false;
			editor._guidesHorizontal = []; editor._guidesVertical = [];
			// If server provided token_layers side-channel, ensure they are realized into currentConfig.layers
			try {
				const tl = (cfg?.visual_config && Array.isArray(cfg.visual_config.token_layers)) ? cfg.visual_config.token_layers : [];
				if (tl.length) {
					// If layers missing or empty, adopt token_layers directly. Otherwise, merge by id/kind without duplicates.
					if (!Array.isArray(cfg.layers) || cfg.layers.length === 0) {
						cfg.layers = tl.map(l => ({ ...l }));
					} else {
						const byId = new Map(cfg.layers.filter(l=>l&&l.id).map(l=>[l.id,l]));
						tl.forEach(l => { if (l && l.id && !byId.has(l.id)) cfg.layers.push({ ...l }); });
					}
				}
			} catch(_) { /* non-fatal */ }
			editor.currentContentBlockId = contentBlockId;
			editor.currentContentBlockType = data.content_block_type || 'track_info';
			// Persist last selected block id to avoid unintended auto-switch to first item
			try { localStorage.setItem('ccveLastBlockId', String(contentBlockId)); } catch(_) {}
			editor.populateControls(editor.currentConfig);
			// Auto-populate typography from first text layer to show layer-specific colors
			try {
				const firstTextLayer = Array.isArray(cfg.layers) ? cfg.layers.find(l => l && l.kind === 'token-text' && l.visible !== false) : null;
				if (firstTextLayer && firstTextLayer.id && editor.populateTypographyFromLayer) {
					editor.populateTypographyFromLayer(firstTextLayer.id);
				}
			} catch(_) {}
			try { editor.attachAutoPreviewControlWatchers && editor.attachAutoPreviewControlWatchers(); } catch(e){ console.warn('[CC] attach watchers (fresh) failed', e); }
			// Ensure preview mode is active (normal-mode rendering)
			try { editor._editorMode = 'preview'; editor._suppressPreviewForBackground = false; } catch(_){ }
			editor.triggerPreview(true);
			// Also explicitly fetch server preview for robustness
			try { await fetchServerPreview(editor, contentBlockId); } catch(_) {}
			// Ensure token layers are rendered and live data applied
			try { editor._renderTokenLayers && editor._renderTokenLayers(); } catch(_) {}
			try { editor.refreshLiveTokens && editor.refreshLiveTokens(); } catch(_) {}
			editor.unsavedChanges = false; editor.updateSaveButton && editor.updateSaveButton();
			try { const dup = document.getElementById('canvas-duplicate-as-new'); if (dup) dup.disabled = false; } catch(_){}
			if (typeof data.config_version !== 'undefined') {
				// Cache the normalized config (cfg) so token_layers persist on cached loads
				editor.configCache[String(contentBlockId)] = { version:data.config_version, config:cfg, content_block_type:data.content_block_type };
			}
		} else {
			editor.showNotification && editor.showNotification('Failed to load content block configuration','error',true);
		}
	} catch (e) {
		console.error('Failed to load content block', e);
		editor.showNotification && editor.showNotification('Failed to load content block','error',true);
	} finally { editor.hideLoader && editor.hideLoader(); }
}

/**
 * Fetch server-rendered live preview (HTML+CSS) using /canvas-editor/preview
 * and apply it via preview display pipeline.
 */
export async function fetchServerPreview(editor, blockId) {
	try {
		if (!blockId) return;
		const endpoint = `${castconductorCanvasAjax.rest_url}castconductor/v5/canvas-editor/preview`;
		// Include override_config snapshot when available so server preview reflects unsaved UI changes
		let snapshot = null; try { if (editor?.currentConfig) snapshot = JSON.parse(JSON.stringify(editor.currentConfig)); } catch(_) { snapshot = null; }
		const payload = snapshot ? { block_id: parseInt(blockId,10), override_config: snapshot } : { block_id: parseInt(blockId,10) };
		const resp = await fetch(endpoint, { method:'POST', headers:{ 'Content-Type':'application/json','X-WP-Nonce':castconductorCanvasAjax.nonce }, body: JSON.stringify(payload) });
		const text = await resp.text();
		let data = null; try { data = JSON.parse(text); } catch(e){ console.warn('[CC] preview non-JSON', text); return; }
		if (!resp.ok || !data.success) { console.warn('[CC] preview fetch failed', data); return; }
		const preview = data.preview || {};
		if (preview.html) {
			if (editor.previewEngine) {
				// If preview engine exists, feed it raw payload
				try { editor.previewEngine.applyServerPreview(preview); return; } catch(e) { console.warn('[CC] previewEngine applyServerPreview failed', e); }
			}
			// Fallback: directly apply
			try { editor._applyPreviewHtml({ html: preview.html, css: preview.css || '' }); } catch(e) { console.warn('[CC] apply server preview failed', e); }
		}
	} catch(e) {
		console.warn('[CC] fetchServerPreview error', e);
	}
}

export async function loadLiveContentBlocks(editor) {
	try {
		const resp = await fetch(`${castconductorCanvasAjax.rest_url}castconductor/v5/content-blocks`, { method:'GET', headers:{ 'Content-Type':'application/json','X-WP-Nonce':castconductorCanvasAjax.nonce } });
		if (!resp.ok) throw new Error(`HTTP ${resp.status}`);
		const blocks = await resp.json();
		editor.displayLiveContentBlocks(blocks);
	} catch(e) {
		console.error('Error loading live content blocks', e);
		editor.showNotification && editor.showNotification('Error loading content blocks','error',true);
	}
}

export async function showLivePreview(editor, blockId) {
	try {
		const resp = await fetch(`${castconductorCanvasAjax.rest_url}castconductor/v5/content-blocks/${blockId}/live-data`, { method:'GET', headers:{ 'Content-Type':'application/json','X-WP-Nonce':castconductorCanvasAjax.nonce } });
		if (!resp.ok) throw new Error(`HTTP ${resp.status}`);
		const liveData = await resp.json();
		editor.displayLiveDataPreview(liveData);
	} catch(e) {
		console.error('Error loading live data', e);
		editor.showNotification && editor.showNotification('Error loading live data: '+(e.message||'fail'),'error');
	}
}
