/**
 * 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
 */
// PreviewEngine now emits lifecycle events via global ccveBus (defined in core-event-bus.js)
// Events: preview:requested {hash}, preview:stale {hash}, preview:live {html,css,first,hash}, preview:error {message,status?}
export class PreviewEngine {
	constructor(opts = {}) {
		this.fetchImpl = opts.fetchImpl || (typeof fetch !== 'undefined' ? fetch.bind(window) : null);
		this.getNonce = opts.getNonce || (() => (window.castconductorCanvasAjax ? window.castconductorCanvasAjax.nonce : ''));
		this.getRestBase = opts.getRestBase || (() => (window.castconductorCanvasAjax ? window.castconductorCanvasAjax.rest_url : ''));
		this.onPreviewHtml = opts.onPreviewHtml || (() => {}); // (preview) => void
		this.onPreviewError = opts.onPreviewError || (() => {}); // (message) => void
		this.onStateChange = opts.onStateChange || (() => {}); // ({ status, stale })
		this.getContentType = opts.getContentType || (() => 'track_info');
		this.getConfig = opts.getConfig || (() => ({}));
		this.getContainerContext = opts.getContainerContext || (() => null); // returns { geometry, fitMode }
		this._lastHash = '';
		this._firstRendered = false;
	}

	computeHash(obj) {
		try { return JSON.stringify(obj || {}); } catch { return ''; }
	}

	markStaleIfNeeded() {
		const cfg = this.getConfig();
		const h = this.computeHash(cfg);
		if (this._lastHash && h !== this._lastHash) {
			this.onStateChange({ status: 'stale', stale: true });
			try { window.ccveBus && window.ccveBus.emit('preview:stale', { hash: h }); } catch {}
		}
	}

	async generate() {
		const cfgOriginal = this.getConfig();
		if (!cfgOriginal) return;
		this.markStaleIfNeeded();
		try { window.ccveBus && window.ccveBus.emit('preview:requested', { }); } catch {}
		// Deep clone (JSON safe) then augment for preview context
		let cfg;
		try { cfg = JSON.parse(JSON.stringify(cfgOriginal)); } catch { cfg = cfgOriginal; }
		const ctx = this.getContainerContext();
		if (ctx && ctx.geometry) {
			cfg.layout = { ...(cfg.layout || {}), ...ctx.geometry };
		}
		if (ctx && ctx.fitMode) {
			cfg.layout = { ...(cfg.layout || {}), fitMode: ctx.fitMode };
		}
		const body = {
			...cfg,
			content_type: this.getContentType(),
			behavior: {
				...(cfg.behavior || {}),
				shuffle_items: !!(cfg.behavior && cfg.behavior.shuffle_items)
			}
		};
		const rest = this.getRestBase();
		if (!rest || !this.fetchImpl) { this.onPreviewError('Preview unavailable: REST base or fetch missing'); try { window.ccveBus && window.ccveBus.emit('preview:error', { message: 'rest-missing' }); } catch {}; return; }
		let res;
		try {
			res = await this.fetchImpl(`${rest}castconductor/v5/canvas-editor/preview`, {
				method: 'POST',
				headers: {
					'Content-Type': 'application/json',
					'X-WP-Nonce': this.getNonce()
				},
				body: JSON.stringify(body)
			});
		} catch (e) { this.onPreviewError(e.message || 'Network error'); try { window.ccveBus && window.ccveBus.emit('preview:error', { message: 'network' }); } catch {}; return; }
		if (!res.ok) { const msg = (res.status === 401 || res.status === 403) ? 'Preview failed: session expired (401/403). Please refresh.' : `Preview failed: HTTP ${res.status}`; this.onPreviewError(msg); try { window.ccveBus && window.ccveBus.emit('preview:error', { message: 'http', status: res.status }); } catch {}; return; }
		let data;
		try { data = await res.json(); } catch { this.onPreviewError('Invalid JSON in preview response'); try { window.ccveBus && window.ccveBus.emit('preview:error', { message:'json' }); } catch {}; return; }
		if (!data || !data.success || !data.preview || !data.preview.html) { this.onPreviewError('Preview unavailable: malformed payload'); try { window.ccveBus && window.ccveBus.emit('preview:error', { message: 'payload' }); } catch {}; return; }
		this._lastHash = this.computeHash(cfgOriginal);
		const first = !this._firstRendered;
		this._firstRendered = true;
		this.onPreviewHtml({ ...data.preview, first });
	this.onStateChange({ status: 'live', stale: false });
	try { window.ccveBus && window.ccveBus.emit('preview:live', { html: data.preview.html, css: data.preview.css, first, hash:this._lastHash }); } catch {}
	}
}

// Simple factory that wires to existing globals (progressive integration into monolith)
export function createDefaultPreviewEngine(hooks) {
	return new PreviewEngine(hooks || {});
}

// Register in global namespace for incremental adoption by legacy monolith
if (typeof window !== 'undefined') {
	window.ccveModules = window.ccveModules || {};
	window.ccveModules.PreviewEngine = PreviewEngine;
}
