/**
 * Cast Conductor Proprietary License v5
 * SPDX-License-Identifier: LicenseRef-CastConductor-Proprietary-v5
 *
 * (Full multi-paragraph proprietary license header retained; see other modules for full text.)
 * Authoritative EULA: EULA-v5.1.md / https://castconductor.com/eula
 *
 * Module: diagnostics-parity.js
 * Phase 2.7 Step 7 – Parity & Diagnostics extraction
 */

/**
 * Produce human-friendly grouped HTML for parity diffs.
 */
export function buildFriendlyParityHtml(diffs) {
	if (!Array.isArray(diffs) || diffs.length === 0) {
		return '<div class="parity-ok">Parity OK</div>';
	}
	const byContainer = {};
	diffs.forEach(d => {
		const id = String(d.id || 'unknown');
		(byContainer[id] ||= []).push(d);
	});
	const esc = (s) => String(s).replace(/[&<>]/g, c => ({'&':'&amp;','<':'&lt;','>':'&gt;'}[c]));
	const parts = [];
	Object.keys(byContainer).forEach(cid => {
		const items = byContainer[cid];
		const geom = items.filter(x => x.kind === 'geometry');
		const zonesCount = items.filter(x => x.kind === 'zones' && x.field === 'zones_count');
		const zoneRows = items.filter(x => (x.kind || '').startsWith('zone'));
		parts.push(`<div class="parity-container"><h4>Container ${esc(cid)}</h4>`);
		if (geom.length) {
			parts.push('<div class="parity-section"><h5>Geometry</h5><ul>');
			geom.forEach(g => parts.push(`<li>${esc(g.field)}: admin=${esc(g.admin)} vs roku=${esc(g.roku)}</li>`));
			parts.push('</ul></div>');
		}
		if (zonesCount.length) {
			parts.push('<div class="parity-section"><h5>Zones</h5><ul>');
			zonesCount.forEach(z => parts.push(`<li>zones_count: admin=${esc(z.admin)} vs roku=${esc(z.roku)}</li>`));
			parts.push('</ul></div>');
		}
		if (zoneRows.length) {
			const byZone = {};
			zoneRows.forEach(z => { (byZone[String(z.zone_id || 'unknown')] ||= []).push(z); });
			Object.keys(byZone).forEach(zid => {
				parts.push(`<div class="parity-section"><h5>Zone ${esc(zid)}</h5><ul>`);
				byZone[zid].forEach(r => {
					if (r.issue) parts.push(`<li>${esc(r.issue)}${r.block_id?` (block ${esc(r.block_id)})`:''}</li>`);
					else if (r.field) {
						const label = r.block_id ? `${r.field} (block ${esc(r.block_id)})` : r.field;
						parts.push(`<li>${esc(label)}: admin=${esc(r.admin)} vs roku=${esc(r.roku)}</li>`);
					}
				});
				parts.push('</ul></div>');
			});
		}
		parts.push('</div>');
	});
	return `<div class="parity-summary">${parts.join('')}</div>`;
}

/**
 * Validate parity between admin DOM & Roku payload.
 * Adds modal display / summary HTML just like original inline logic.
 */
export async function validateParity(editor) {
	try {
		const url = `${castconductorCanvasAjax.rest_url}castconductor/v5/roku/app-config`;
		const resp = await fetch(url, { headers: { 'Content-Type': 'application/json' } });
		const payload = await resp.json();
		const app = payload?.data || payload;
		const rokuContainers = (app?.containers || []).reduce((m, c) => { m[String(c.id)] = c; return m; }, {});
		const diffs = [];
		const add = (row) => { diffs.push(row); };
		const containers = Array.from(document.querySelectorAll('.canvas-container'));
		for (const el of containers) {
			const id = String(el.getAttribute('data-container-id'));
			const r = rokuContainers[id];
			if (!r) { add({ id, issue:'missing_in_roku' }); continue; }
			const domRect = {
				x_position: parseInt(el.style.left,10) || el.offsetLeft || 0,
				y_position: parseInt(el.style.top,10) || el.offsetTop || 0,
				width: parseInt(el.style.width,10) || el.offsetWidth || 0,
				height: parseInt(el.style.height,10) || el.offsetHeight || 0,
				enabled: (el.getAttribute('data-enabled') === '1')
			};
			['x_position','y_position','width','height'].forEach(k => {
				const a = domRect[k]; const b = parseInt(r[k],10) || 0; if (a!==b) add({ id, kind:'geometry', field:k, admin:a, roku:b });
			});
			if (el.dataset.relative === '1' || r.relative) {
				['x_position','y_position','width','height'].forEach(k => {
					const aRel = parseFloat(el.dataset[k + '_percent'] || '0');
					const bRel = parseFloat(r[k + '_percent'] || '0');
					if (Math.abs(aRel - bRel) > 0.5) add({ id, kind:'geometry', field:k+'_percent', admin:aRel, roku:bRel });
				});
			}
			const adminFit = el.dataset.fitMode || el.getAttribute('data-fit-mode');
			const rokuFit = r.fitMode || r.fit_mode;
			if (adminFit && rokuFit && adminFit !== rokuFit) add({ id, kind:'geometry', field:'fitMode', admin:adminFit, roku:rokuFit });
			const rEnabled = !!r.rotation_enabled || true; // parity with original assumption
			if (domRect.enabled !== rEnabled) add({ id, kind:'geometry', field:'enabled', admin:domRect.enabled, roku:rEnabled });
			// Zones
			const zoneEls = Array.from(el.querySelectorAll('.zones-overlay .zone-rect'));
			const zonesInDom = zoneEls.length;
			const zonesInRoku = Array.isArray(r?.layout?.zones) ? r.layout.zones.length : 0;
			if (zonesInDom !== zonesInRoku) add({ id, kind:'zones', field:'zones_count', admin:zonesInDom, roku:zonesInRoku });
			const rokuZoneMap = r?.zone_assignments || {};
			for (const zEl of zoneEls) {
				const zoneId = String(zEl.getAttribute('data-zone-id') || '');
				if (!zoneId) continue;
				let adminList = [];
				try { adminList = await editor.getZoneAssignments(id, zoneId); } catch { add({ id, zone_id:zoneId, kind:'zone', issue:'admin_zone_fetch_failed' }); continue; }
				const adminEnabled = (adminList||[]).filter(x => String(x.enabled ?? 1) !== '0').map(x => ({
					content_block_id: parseInt(x.content_block_id,10),
					rotation_order: parseInt(x.rotation_order ?? 0,10),
					rotation_percentage: Math.round(parseFloat(x.rotation_percentage ?? 0))
				}));
				const rokRaw = Array.isArray(rokuZoneMap?.[zoneId]) ? rokuZoneMap[zoneId] : [];
				const rokuNorm = rokRaw.map(x => ({
					content_block_id: parseInt(x.id ?? x.content_block_id ?? 0,10),
					rotation_order: parseInt(x.rotation_order ?? 0,10),
					rotation_percentage: Math.round(parseFloat(x.rotation_percentage ?? 0))
				})).filter(x => x.content_block_id > 0);
				const aIds = new Set(adminEnabled.map(x => x.content_block_id));
				const rIds = new Set(rokuNorm.map(x => x.content_block_id));
				for (const bid of aIds) if (!rIds.has(bid)) add({ id, zone_id:zoneId, kind:'zone_assignments', block_id:bid, issue:'present_in_admin_missing_in_roku' });
				for (const bid of rIds) if (!aIds.has(bid)) add({ id, zone_id:zoneId, kind:'zone_assignments', block_id:bid, issue:'present_in_roku_missing_in_admin' });
				const rById = {}; rokuNorm.forEach(x => { rById[String(x.content_block_id)] = x; });
				adminEnabled.forEach(x => {
					const rb = rById[String(x.content_block_id)]; if (!rb) return;
					if ((x.rotation_order||0)!==(rb.rotation_order||0)) add({ id, zone_id:zoneId, kind:'zone_assignments', block_id:x.content_block_id, field:'rotation_order', admin:x.rotation_order, roku:rb.rotation_order });
					if ((x.rotation_percentage||0)!==(rb.rotation_percentage||0)) add({ id, zone_id:zoneId, kind:'zone_assignments', block_id:x.content_block_id, field:'rotation_percentage', admin:x.rotation_percentage, roku:rb.rotation_percentage });
				});
				const aTotal = adminEnabled.reduce((s,x)=>s+(x.rotation_percentage||0),0);
				const rTotal = rokuNorm.reduce((s,x)=>s+(x.rotation_percentage||0),0);
				if (aTotal !== rTotal) add({ id, zone_id:zoneId, kind:'zone_assignments', field:'rotation_total', admin:aTotal, roku:rTotal });
			}
		}
		// Modal wiring (unchanged)
		const modal = document.getElementById('parity-modal');
		const pre = document.getElementById('parity-results');
		const htmlId = 'parity-results-html';
		let htmlBox = document.getElementById(htmlId);
		if (!htmlBox && modal) {
			htmlBox = document.createElement('div');
			htmlBox.id = htmlId;
			(modal.querySelector('.canvas-modal-body') || modal).appendChild(htmlBox);
		}
		if (htmlBox) {
			htmlBox.innerHTML = buildFriendlyParityHtml(diffs);
			if (pre) pre.style.display = 'none';
		} else if (pre) {
			pre.textContent = diffs.length ? JSON.stringify(diffs, null, 2) : 'Parity OK';
		}
		if (modal) modal.style.display = 'block';
		const close = document.getElementById('close-parity');
		if (close) close.onclick = () => { modal.style.display = 'none'; };
		window.addEventListener('keydown', (e) => { if (e.key === 'Escape' && modal && modal.style.display === 'block') modal.style.display = 'none'; }, { once:true });
	} catch (e) {
		console.error('Failed to validate parity', e);
	}
}

export const diagnosticsParityApi = { validateParity, buildFriendlyParityHtml };
