import { tick } from 'svelte';

const prefersReducedMotion = () =>
	typeof window !== 'undefined' && window.matchMedia('(prefers-reduced-motion: reduce)').matches;

/**
 * Runs a day change inside a view transition, so the outgoing day is still on
 * screen while the incoming one fades in - a real cross-fade rather than the
 * old content vanishing and the new one fading up from nothing.
 *
 * Only the regions that actually change carry a `view-transition-name` (see
 * `.day-region-*` in routes/layout.css); everything else - the strip, the
 * header, the page chrome - is pinned by the `day-switch` class so it stays
 * completely still.
 */
export async function runDayTransition(update: () => void): Promise<void> {
	if (prefersReducedMotion() || !document.startViewTransition) {
		update();
		return;
	}

	const root = document.documentElement;
	root.classList.add('day-switch');
	try {
		// Svelte applies the change on the next tick; the transition has to wait
		// for that before it snapshots the new state.
		await document.startViewTransition(async () => {
			update();
			await tick();
		}).finished;
	} catch {
		/* a superseded transition is fine - the DOM is already up to date */
	} finally {
		root.classList.remove('day-switch');
	}
}

/**
 * Fallback for browsers without view transitions: fade the block back in when
 * the value passed to it changes. Animates the existing node rather than
 * remounting it, so the canvas charts keep their zoom state.
 */
export function daySwap(node: HTMLElement, key: unknown) {
	let current = key;

	const play = () => {
		// view transitions handle it properly where they exist
		if (typeof document.startViewTransition === 'function' || prefersReducedMotion()) return;
		node.animate([{ opacity: 0.1 }, { opacity: 1 }], {
			duration: 460,
			easing: 'cubic-bezier(0.22, 0.61, 0.36, 1)'
		});
	};

	return {
		update(next: unknown) {
			if (next === current) return;
			current = next;
			play();
		}
	};
}
