/**
 * Scaled live thumbnail for library cards — mini browser chrome + fit scale.
 * On hover, tall previews slowly auto-scroll in a pause → down → pause → top loop.
 * When a pack has Owner variants, slides through Original → variants in a loop.
 */
import { useEffect, useMemo, useRef, useState } from '@wordpress/element';
import { renderPreview } from './renderPreview';
import { dashiconsStylesheetTag, fontStylesheetTags } from '../previewAssets';
import { hydratePendingInlineIcons } from '../../shared/iconRegistry';

const DESIGN_W = 1280;
const DESIGN_H = 800;
/** Pause at top on first hover before scrolling. */
const FIRST_HOVER_PAUSE_MS = 1000;
/** Pause at bottom / top on subsequent loop cycles. */
const PAUSE_MS = 2500;
/** Viewport pixels per second while scrolling down (slower = lower). */
const SCROLL_PX_PER_SEC = 36;
/** How long each variant stays on screen in the list carousel. */
const VARIANT_DWELL_MS = 3400;

/**
 * List-card zoom profile. Hero/banner stay full-bleed; buttons and compact
 * sections use a tighter design width / zoom so content reads larger.
 *
 * @param {object} block
 * @return {{ kind: string, designW: number, designH: number, zoom: number }}
 */
function resolveListPreviewProfile( block ) {
	const cat = String( block?.categorySlug || block?.category || '' )
		.trim()
		.toLowerCase();
	const cats = Array.isArray( block?.categorySlugs )
		? block.categorySlugs.map( ( s ) => String( s || '' ).toLowerCase() )
		: [];
	const slug = String( block?.slug || block?.systemSlug || '' )
		.trim()
		.toLowerCase();
	const name = String( block?.name || block?.systemName || '' )
		.trim()
		.toLowerCase();
	const markup = String( block?.markup || '' );

	const hasCat = ( key ) => cat === key || cats.includes( key );
	const isButton =
		hasCat( 'buttons' ) ||
		slug.startsWith( 'button' ) ||
		/\bbm-button\b/.test( markup );
	const isHero =
		hasCat( 'hero' ) ||
		slug.startsWith( 'hero' ) ||
		/\bbm-hero\b/.test( markup );
	const isBanner =
		hasCat( 'cta' ) ||
		slug.includes( 'banner' ) ||
		name.includes( 'banner' ) ||
		/\bbm-banner\b|\bbm-cta\b/.test( markup );

	if ( isButton ) {
		// Narrow canvas → larger fit-scale so CTAs aren’t tiny chips.
		return { kind: 'button', designW: 520, designH: 320, zoom: 1 };
	}
	if ( isHero || isBanner ) {
		return {
			kind: isHero ? 'hero' : 'banner',
			designW: DESIGN_W,
			designH: DESIGN_H,
			zoom: 1,
		};
	}
	// Accordion, cards, pricing, tabs, etc. — bump zoom inside the card.
	return { kind: 'other', designW: DESIGN_W, designH: DESIGN_H, zoom: 1.4 };
}

/**
 * Original + Owner variants as carousel slides (no nested variants arrays).
 *
 * @param {object} block
 * @return {object[]}
 */
function resolveVariantSlides( block ) {
	if ( ! block || typeof block !== 'object' ) {
		return [];
	}
	const variants = Array.isArray( block.variants ) ? block.variants : [];
	const original = { ...block, variants: [] };
	if ( ! variants.length ) {
		return [ original ];
	}
	return [
		original,
		...variants
			.filter( ( v ) => v && typeof v === 'object' )
			.map( ( v ) => ( { ...v, variants: [] } ) ),
	];
}

function slideKey( slide, index ) {
	return (
		slide?.__layerId ||
		slide?.name ||
		`${ slide?.slug || 'pack' }:${ slide?.variant || index }`
	);
}

/**
 * Single-pack scaled iframe (no variant carousel).
 *
 * @param {object}  props
 * @param {object}  props.block
 * @param {boolean} [props.active]
 * @param {boolean} [props.hoverPan]
 */
function LibraryCardThumbFrame( { block, active = true, hoverPan = true } ) {
	const viewportRef = useRef( null );
	const scalerRef = useRef( null );
	const iframeRef = useRef( null );
	const scaleRef = useRef( 1 );
	const profileRef = useRef( resolveListPreviewProfile( block ) );
	const contentHRef = useRef( DESIGN_H );
	const panRef = useRef( 0 );
	const animRef = useRef( { raf: 0, timer: 0, running: false } );
	const rootRef = useRef( null );

	const profile = useMemo(
		() => resolveListPreviewProfile( block ),
		[
			block?.categorySlug,
			block?.category,
			block?.categorySlugs,
			block?.slug,
			block?.systemSlug,
			block?.name,
			block?.systemName,
			block?.markup,
		]
	);
	profileRef.current = profile;

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

	const srcDoc = useMemo( () => {
		const body = preview?.__html || '';
		const css = preview?.styleCss || '';
		const isButton = profile.kind === 'button';
		const accordionFill =
			! isButton && body.includes( 'bm-accordion' )
				? '.bm-preview-root>.bm-accordion{min-height:760px;box-sizing:border-box;}'
				: '';
		const rootMin = isButton
			? '.bm-preview-root{min-height:0;}'
			: '.bm-preview-root{min-height:760px;}';
		const buttonBody = isButton
			? `body{display:flex;align-items:center;justify-content:center;min-height:${ profile.designH }px;padding:28px;}`
			: 'body{padding:20px;}';
		const dashicons = dashiconsStylesheetTag();
		const fonts = fontStylesheetTags();
		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 }${ dashicons }${ fonts }<style>
html,body{margin:0;padding:0;background:#fff;overflow:hidden;}
body{pointer-events:none;box-sizing:border-box;}
${ buttonBody }
.bm-preview-root{transform-origin:top left;}
${ rootMin }
${ accordionFill }
#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, profile.kind, profile.designH ] );

	const applyTransform = () => {
		const scaler = scalerRef.current;
		if ( ! scaler ) {
			return;
		}
		const scale = scaleRef.current;
		const pan = panRef.current;
		const animating = animRef.current.running;
		scaler.style.transform = animating
			? `translate3d(0, ${ -pan }px, 0) scale(${ scale })`
			: `translate(0, ${ -pan }px) scale(${ scale })`;
		scaler.style.willChange = animating ? 'transform' : 'auto';
	};

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

	const stopAnim = ( { reset = true } = {} ) => {
		const anim = animRef.current;
		anim.running = false;
		if ( anim.raf ) {
			window.cancelAnimationFrame( anim.raf );
			anim.raf = 0;
		}
		if ( anim.timer ) {
			window.clearTimeout( anim.timer );
			anim.timer = 0;
		}
		if ( reset ) {
			panRef.current = 0;
			applyTransform();
		}
	};

	const startAnim = () => {
		const anim = animRef.current;
		if ( anim.running || ! hoverPan || ! active ) {
			return;
		}
		if (
			typeof window.matchMedia === 'function' &&
			window.matchMedia( '(prefers-reduced-motion: reduce)' ).matches
		) {
			return;
		}
		const limit = maxPan();
		if ( limit < 8 ) {
			return;
		}

		anim.running = true;
		panRef.current = 0;
		applyTransform();

		const wait = ( ms ) =>
			new Promise( ( resolve ) => {
				anim.timer = window.setTimeout( () => {
					anim.timer = 0;
					resolve();
				}, ms );
			} );

		const scrollDown = () =>
			new Promise( ( resolve ) => {
				let last = performance.now();
				const tick = ( now ) => {
					if ( ! anim.running ) {
						resolve();
						return;
					}
					const dt = Math.min( 48, now - last ) / 1000;
					last = now;
					const next = Math.min(
						limit,
						panRef.current + SCROLL_PX_PER_SEC * dt
					);
					panRef.current = next;
					applyTransform();
					if ( next >= limit - 0.5 ) {
						panRef.current = limit;
						applyTransform();
						anim.raf = 0;
						resolve();
						return;
					}
					anim.raf = window.requestAnimationFrame( tick );
				};
				anim.raf = window.requestAnimationFrame( tick );
			} );

		const loop = async () => {
			let firstCycle = true;
			while ( anim.running ) {
				await wait( firstCycle ? FIRST_HOVER_PAUSE_MS : PAUSE_MS );
				firstCycle = false;
				if ( ! anim.running ) {
					break;
				}
				await scrollDown();
				if ( ! anim.running ) {
					break;
				}
				await wait( PAUSE_MS );
				if ( ! anim.running ) {
					break;
				}
				panRef.current = 0;
				applyTransform();
			}
		};

		loop();
	};

	useEffect( () => {
		if ( ! active ) {
			stopAnim( { reset: true } );
			return undefined;
		}

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

		const measureContent = () => {
			const { designH } = profileRef.current;
			try {
				const doc = iframe.contentDocument;
				if ( ! doc?.body ) {
					return;
				}
				const h = Math.max(
					doc.body.scrollHeight,
					doc.documentElement?.scrollHeight || 0,
					designH
				);
				contentHRef.current = h;
				iframe.style.height = `${ h }px`;
				scaler.style.height = `${ h }px`;
			} catch ( e ) {
				contentHRef.current = designH;
				iframe.style.height = `${ designH }px`;
				scaler.style.height = `${ designH }px`;
			}
		};

		const fit = () => {
			const { designW, zoom } = profileRef.current;
			measureContent();
			const w = viewport.clientWidth || 1;
			scaleRef.current = Math.max( 0.05, ( w / designW ) * zoom );
			const limit = maxPan();
			if ( panRef.current > limit ) {
				panRef.current = limit;
			}
			applyTransform();
		};

		iframe.style.width = `${ profileRef.current.designW }px`;
		scaler.style.width = `${ profileRef.current.designW }px`;
		iframe.style.minHeight = `${ profileRef.current.designH }px`;
		contentHRef.current = profileRef.current.designH;

		const onLoad = () => {
			fit();
			try {
				const doc = iframe.contentDocument;
				doc?.querySelectorAll?.( 'img' ).forEach( ( img ) => {
					if ( ! img.complete ) {
						img.addEventListener( 'load', fit );
						img.addEventListener( 'error', fit );
					}
				} );
				hydratePendingInlineIcons( doc ).then( () => {
					fit();
				} );
			} catch ( e ) {
				// ignore
			}
		};

		iframe.addEventListener( 'load', onLoad );
		try {
			if ( iframe.contentDocument?.readyState === 'complete' ) {
				onLoad();
			}
		} catch ( e ) {
			// ignore
		}
		const t = window.setTimeout( onLoad, 80 );
		const t2 = window.setTimeout( fit, 320 );

		const ro = new ResizeObserver( fit );
		ro.observe( viewport );

		return () => {
			iframe.removeEventListener( 'load', onLoad );
			window.clearTimeout( t );
			window.clearTimeout( t2 );
			ro.disconnect();
			stopAnim( { reset: true } );
		};
	}, [ srcDoc, active ] );

	useEffect( () => {
		if ( ! hoverPan || ! active ) {
			stopAnim( { reset: true } );
			return undefined;
		}
		const root = rootRef.current;
		if ( ! root ) {
			return undefined;
		}

		const onEnter = () => startAnim();
		const onLeave = () => stopAnim( { reset: true } );

		root.addEventListener( 'mouseenter', onEnter );
		root.addEventListener( 'mouseleave', onLeave );
		root.addEventListener( 'focusin', onEnter );
		root.addEventListener( 'focusout', ( e ) => {
			if ( ! root.contains( e.relatedTarget ) ) {
				onLeave();
			}
		} );

		return () => {
			root.removeEventListener( 'mouseenter', onEnter );
			root.removeEventListener( 'mouseleave', onLeave );
			root.removeEventListener( 'focusin', onEnter );
			stopAnim( { reset: true } );
		};
	}, [ srcDoc, active, hoverPan ] );

	return (
		<div className="bm-lib-thumb__frame-wrap" ref={ rootRef }>
			<div className="bm-lib-thumb__viewport" ref={ viewportRef }>
				<div
					className="bm-lib-thumb__scaler"
					ref={ scalerRef }
					style={ {
						width: `${ profile.designW }px`,
						height: `${ profile.designH }px`,
					} }
				>
					<iframe
						ref={ iframeRef }
						title=""
						className="bm-lib-thumb__frame"
						tabIndex={ -1 }
						aria-hidden="true"
						srcDoc={ srcDoc }
						style={ {
							width: `${ profile.designW }px`,
							minHeight: `${ profile.designH }px`,
							height: `${ profile.designH }px`,
						} }
					/>
				</div>
			</div>
		</div>
	);
}

export default function LibraryCardThumb( { block } ) {
	const rootRef = useRef( null );
	const [ visible, setVisible ] = useState( false );
	const [ hovering, setHovering ] = useState( false );
	const [ activeIndex, setActiveIndex ] = useState( 0 );

	const slides = useMemo( () => resolveVariantSlides( block ), [ block ] );
	const hasCarousel = slides.length > 1;
	const profile = useMemo(
		() => resolveListPreviewProfile( slides[ 0 ] || block ),
		[ slides, block ]
	);

	useEffect( () => {
		setActiveIndex( 0 );
	}, [ slides.length, block?.id, block?.slug, block?.name ] );

	useEffect( () => {
		const root = rootRef.current;
		if ( ! root || typeof IntersectionObserver !== 'function' ) {
			setVisible( true );
			return undefined;
		}
		const io = new IntersectionObserver(
			( entries ) => {
				const entry = entries[ 0 ];
				setVisible( Boolean( entry?.isIntersecting ) );
			},
			{ root: null, rootMargin: '80px', threshold: 0.15 }
		);
		io.observe( root );
		return () => io.disconnect();
	}, [] );

	useEffect( () => {
		if ( ! hasCarousel || ! visible || hovering ) {
			return undefined;
		}
		if (
			typeof window.matchMedia === 'function' &&
			window.matchMedia( '(prefers-reduced-motion: reduce)' ).matches
		) {
			return undefined;
		}
		const id = window.setInterval( () => {
			setActiveIndex( ( i ) => ( i + 1 ) % slides.length );
		}, VARIANT_DWELL_MS );
		return () => window.clearInterval( id );
	}, [ hasCarousel, visible, hovering, slides.length ] );

	const safeIndex =
		slides.length > 0
			? Math.min( Math.max( 0, activeIndex ), slides.length - 1 )
			: 0;

	return (
		<div
			className={
				'bm-lib-thumb' +
				( profile.kind ? ` bm-lib-thumb--${ profile.kind }` : '' ) +
				( hasCarousel ? ' bm-lib-thumb--carousel' : '' )
			}
			data-bm-thumb-kind={ profile.kind }
			ref={ rootRef }
			onMouseEnter={ () => setHovering( true ) }
			onMouseLeave={ () => setHovering( false ) }
			onFocus={ () => setHovering( true ) }
			onBlur={ ( e ) => {
				if ( ! e.currentTarget.contains( e.relatedTarget ) ) {
					setHovering( false );
				}
			} }
		>
			<div className="bm-lib-thumb__chrome" aria-hidden="true">
				<span className="bm-lib-thumb__dot" />
				<span className="bm-lib-thumb__dot" />
				<span className="bm-lib-thumb__dot" />
			</div>
			{ hasCarousel ? (
				<div className="bm-lib-thumb__carousel-viewport">
					<div
						className="bm-lib-thumb__track"
						style={ {
							width: `${ slides.length * 100 }%`,
							transform: `translateX(-${
								( safeIndex * 100 ) / slides.length
							}%)`,
						} }
					>
						{ slides.map( ( slide, index ) => {
							const near = Math.abs( index - safeIndex ) <= 1;
							return (
								<div
									key={ slideKey( slide, index ) }
									className={
										'bm-lib-thumb__slide' +
										( index === safeIndex
											? ' is-active'
											: '' )
									}
									style={ {
										width: `${ 100 / slides.length }%`,
									} }
								>
									{ near ? (
										<LibraryCardThumbFrame
											block={ slide }
											active={ index === safeIndex }
											hoverPan={
												index === safeIndex && hovering
											}
										/>
									) : (
										<div
											className="bm-lib-thumb__slide-placeholder"
											aria-hidden="true"
										/>
									) }
								</div>
							);
						} ) }
					</div>
					<div
						className="bm-lib-thumb__pips"
						aria-hidden="true"
					>
						{ slides.map( ( slide, index ) => (
							<span
								key={ slideKey( slide, index ) }
								className={
									'bm-lib-thumb__pip' +
									( index === safeIndex ? ' is-active' : '' )
								}
							/>
						) ) }
					</div>
				</div>
			) : (
				<LibraryCardThumbFrame
					block={ slides[ 0 ] || block }
					active
					hoverPan
				/>
			) }
		</div>
	);
}
