import { lerpView, type PanZoomState } from '$lib/person/genealogy/panZoom';

/** Fit / recentre animation duration (US-PAN-004 AC2: ≤ 300 ms). */
export const VIEW_ANIM_MS = 300;

const easeOutCubic = (t: number) => 1 - Math.pow(1 - t, 3);

/** Snapshot of the user's reduced-motion preference (non-reactive, browser-only). */
export function prefersReducedMotion(): boolean {
	return typeof window !== 'undefined' && typeof window.matchMedia === 'function'
		? window.matchMedia('(prefers-reduced-motion: reduce)').matches
		: false;
}

/**
 * Tween the view from `from` to `to`, calling `onFrame` with each interpolated
 * state. Honors reduced motion by snapping straight to the target (NFR-A11Y-003,
 * REQ-PAN-005). Returns a cancel function.
 */
export function animateView(
	from: PanZoomState,
	to: PanZoomState,
	onFrame: (state: PanZoomState) => void,
	opts: { reducedMotion?: boolean; durationMs?: number } = {}
): () => void {
	if (opts.reducedMotion) {
		onFrame(to);
		return () => {};
	}
	const duration = opts.durationMs ?? VIEW_ANIM_MS;
	const start = performance.now();
	let raf = requestAnimationFrame(function step(now: number) {
		const t = Math.min(1, (now - start) / duration);
		onFrame(lerpView(from, to, easeOutCubic(t)));
		if (t < 1) raf = requestAnimationFrame(step);
	});
	return () => cancelAnimationFrame(raf);
}
