/**
 * Compact live preview thumb for variant layer cards.
 * Same fit-scale approach as LibraryCardThumb; optional wheel pan for tall blocks.
 */
import { useEffect, useMemo, useRef, useState } from '@wordpress/element';
import { renderPreview } from './renderPreview';
import {
	dashiconsStylesheetTag,
	fontStylesheetTags,
	themeAgnosticStylesheetTag,
} from '../previewAssets';
import {
	hydratePendingInlineIcons,
	prefetchInlineIconsFromValue,
} from '../../shared/iconRegistry';

const DESIGN_W = 1280;
const DESIGN_H = 800;

/**
 * Escape for embedding into an HTML srcDoc string.
 *
 * @param {string} value
 * @return {string}
 */
function escapeSrcDoc( value ) {
	return String( value || '' )
		.replace( /<\/(script|style|iframe)/gi, '<\\/$1' )
		.replace( /<\/(body|html)/gi, '<\\/$1' );
}

/**
 * @param {object}  props
 * @param {object}  props.block
 * @param {string}  [props.label]
 * @param {boolean} [props.scrollable] Enable wheel pan for tall content (insert modal).
 */
export default function VariantLayerThumb( {
	block,
	label,
	scrollable = false,
} ) {
	const viewportRef = useRef( null );
	const scalerRef = useRef( null );
	const iframeRef = useRef( null );
	const scaleRef = useRef( 1 );
	const contentHRef = useRef( DESIGN_H );
	const panRef = useRef( 0 );
	const [ iconEpoch, setIconEpoch ] = useState( 0 );

	const preview = useMemo(
		() =>
			renderPreview(
				block?.markup,
				block?.defaultAttrs || {},
				block?.cssVars || {},
				block?.fieldSchema || []
			),
		[
			block?.markup,
			block?.defaultAttrs,
			block?.cssVars,
			block?.fieldSchema,
			iconEpoch,
		]
	);

	useEffect( () => {
		let cancelled = false;
		prefetchInlineIconsFromValue( block?.defaultAttrs ).then( ( count ) => {
			if ( ! cancelled && count > 0 ) {
				setIconEpoch( ( n ) => n + 1 );
			}
		} );
		return () => {
			cancelled = true;
		};
	}, [ block?.defaultAttrs ] );

	const srcDoc = useMemo( () => {
		const body = escapeSrcDoc( preview?.__html || '' );
		const css = escapeSrcDoc( preview?.styleCss || '' );
		const dashicons = dashiconsStylesheetTag();
		const fonts = fontStylesheetTags();
		const theme = themeAgnosticStylesheetTag();
		const pluginUrl = String(
			( typeof window !== 'undefined' &&
				( window.jocbmAdmin?.pluginUrl ||
					window.jocbmEditor?.pluginUrl ) ) ||
				''
		).replace( /\/?$/, '/' );
		const base =
			pluginUrl && pluginUrl !== '/'
				? `<base href="${ pluginUrl.replace( /"/g, '' ) }">`
				: '';
		return `<!DOCTYPE html><html><head><meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1">${ base }${ theme }${ dashicons }${ fonts }<style>
html,body{margin:0;padding:0;background:#fff;overflow:hidden;}
body{pointer-events:none;padding:16px;box-sizing:border-box;}
.bm-preview-root{transform-origin:top left;}
#bm-preview-vars{display:contents;}
.dashicons{display:inline-block;line-height:1;text-decoration:inherit;vertical-align:top;}
</style><style id="bm-preview-vars">${ css }</style></head><body>${ body }</body></html>`;
	}, [ preview ] );

	useEffect( () => {
		const viewport = viewportRef.current;
		const scaler = scalerRef.current;
		const iframe = iframeRef.current;
		if ( ! viewport || ! scaler || ! iframe ) {
			return undefined;
		}

		const applyTransform = () => {
			const scale = scaleRef.current;
			const pan = panRef.current;
			scaler.style.transform = `translate(0, ${ -pan }px) scale(${ scale })`;
		};

		const maxPan = () => {
			const scaledH = contentHRef.current * scaleRef.current;
			return Math.max( 0, scaledH - viewport.clientHeight );
		};

		const apply = () => {
			const w = viewport.clientWidth || 1;
			const scale = Math.max( 0.04, w / DESIGN_W );
			scaleRef.current = scale;
			scaler.style.width = `${ DESIGN_W }px`;
			try {
				const doc = iframe.contentDocument;
				const h = Math.max(
					doc?.body?.scrollHeight || 0,
					doc?.documentElement?.scrollHeight || 0,
					DESIGN_H
				);
				contentHRef.current = h;
				iframe.style.height = `${ h }px`;
				scaler.style.height = `${ h }px`;
			} catch ( e ) {
				contentHRef.current = DESIGN_H;
				iframe.style.height = `${ DESIGN_H }px`;
				scaler.style.height = `${ DESIGN_H }px`;
			}
			panRef.current = Math.min( panRef.current, maxPan() );
			applyTransform();
		};

		const onWheel = ( event ) => {
			if ( ! scrollable ) {
				return;
			}
			const limit = maxPan();
			if ( limit < 4 ) {
				return;
			}
			event.preventDefault();
			const next = Math.min(
				limit,
				Math.max( 0, panRef.current + event.deltaY )
			);
			if ( next === panRef.current ) {
				return;
			}
			panRef.current = next;
			applyTransform();
		};

		const onLoad = () => {
			panRef.current = 0;
			apply();
			try {
				iframe.contentDocument
					?.querySelectorAll?.( 'img' )
					?.forEach( ( img ) => {
						if ( ! img.complete ) {
							img.addEventListener( 'load', apply );
							img.addEventListener( 'error', apply );
						}
					} );
				hydratePendingInlineIcons( iframe.contentDocument ).then(
					() => {
						apply();
					}
				);
			} catch ( e ) {
				// ignore cross-origin
			}
		};

		iframe.addEventListener( 'load', onLoad );
		if ( scrollable ) {
			viewport.addEventListener( 'wheel', onWheel, { passive: false } );
		}
		try {
			if ( iframe.contentDocument?.readyState === 'complete' ) {
				onLoad();
			}
		} catch ( e ) {
			// ignore
		}
		const t1 = window.setTimeout( onLoad, 60 );
		const t2 = window.setTimeout( apply, 280 );
		const ro = new ResizeObserver( apply );
		ro.observe( viewport );

		return () => {
			iframe.removeEventListener( 'load', onLoad );
			if ( scrollable ) {
				viewport.removeEventListener( 'wheel', onWheel );
			}
			window.clearTimeout( t1 );
			window.clearTimeout( t2 );
			ro.disconnect();
		};
	}, [ srcDoc, scrollable ] );

	return (
		<span
			className={
				'bm-preview-layers__frame' +
				( scrollable ? ' is-scrollable' : '' )
			}
			ref={ viewportRef }
		>
			<span className="bm-preview-layers__scaler" ref={ scalerRef }>
				<iframe
					ref={ iframeRef }
					title={ label || 'Variant preview' }
					className="bm-preview-layers__iframe"
					tabIndex={ -1 }
					srcDoc={ srcDoc }
				/>
			</span>
		</span>
	);
}
