import { tick } from 'svelte';

import {
	canStartViewTransition,
	prefersReducedMotion,
	startViewTransition,
	supportsViewTransitions
} from './view-transition';

/**
 * 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.
 *
 * A day switch that lands while a navigation transition is still on screen just
 * applies: starting a rival transition would skip the running one and flash the
 * page (see view-transition.ts).
 */
export async function runDayTransition(update: () => void): Promise<void> {
	if (!canStartViewTransition()) {
		update();
		return;
	}

	// Svelte applies the change on the next tick; the transition has to wait for
	// that before it snapshots the new state. `day-switch` scopes which regions
	// take part (see routes/layout.css) and is cleared for us when it ends.
	await startViewTransition(
		async () => {
			update();
			await tick();
		},
		{ rootClass: '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 (supportsViewTransitions() || 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();
		}
	};
}
