<script lang="ts">
	import { onMount } from 'svelte';

	import { storedLocation, storedModel, storedTheme } from '$lib/stores/settings';

	import { mapsDomainForModel } from '$lib/utils/maps-domain';

	// Hash piping, both directions:
	// - inbound: a #zoom/lat/lng(/bearing/pitch) hash on OUR url seeds the
	//   map, so positions can be bookmarked/shared via drizzli links
	// - outbound: the (cross-origin) map posts its hash here on every moveend;
	//   we mirror it into our url with replaceState. hashOverride is only set
	//   once on mount, so these mirror updates never reload the iframe.
	let hashOverride = $state<string | null>(null);
	let iframeEl = $state<HTMLIFrameElement | null>(null);
	let mapReady = $state(false);

	const postToMap = (message: Record<string, unknown>) => {
		iframeEl?.contentWindow?.postMessage(message, MAPS_ORIGIN);
	};

	// Local maps dev server (open-meteo/maps); production: https://maps.open-meteo.com
	// Run drizzli on a different port so the map keeps 5173 to itself.
	// const MAPS_ORIGIN = 'http://localhost:5173';
	const MAPS_ORIGIN = 'https://maps.open-meteo.com';

	const MAP_HASH_RE = /^#\d+(\.\d+)?\/-?\d+(\.\d+)?\/-?\d+(\.\d+)?/;

	onMount(() => {
		const initialHash = window.location.hash;
		hashOverride = MAP_HASH_RE.test(initialHash) ? initialHash : null;

		const onMessage = (event: MessageEvent) => {
			if (event.origin !== MAPS_ORIGIN) return;
			const { type, hash, domains } = (event.data ?? {}) as {
				type?: string;
				hash?: string;
				domains?: string[];
			};
			if (type === 'om-maps:hash') {
				if (!hash || !MAP_HASH_RE.test(hash)) return;
				history.replaceState(history.state, '', hash);
			} else if (type === 'om-maps:ready' && Array.isArray(domains)) {
				// The map is loaded and advertises which domains it can render;
				// switch it to the selected model and our theme. The domain is
				// omitted for best_match and models the map does not serve,
				// keeping the map's own default. Re-fires on iframe reloads.
				mapReady = true;
				const domain = mapsDomainForModel($storedModel, new Set(domains));
				postToMap({ type: 'om-maps:set', ...(domain && { domain }), theme: $storedTheme });
			}
		};
		window.addEventListener('message', onMessage);
		return () => window.removeEventListener('message', onMessage);
	});

	// the embedded map understands maplibre's #zoom/lat/lng hash, so the iframe
	// opens focused on the selected location (zoomed out to regional scale);
	// picking a new location while on this page recenters the map
	const iframeSrc = $derived(
		`${MAPS_ORIGIN}/${
			hashOverride ??
			`#6/${$storedLocation.latitude.toFixed(3)}/${$storedLocation.longitude.toFixed(3)}`
		}`
	);

	// forward theme switches live; the initial theme travels with the
	// ready response above (the map ignores no-op updates)
	$effect(() => {
		const theme = $storedTheme;
		if (!mapReady) return;
		postToMap({ type: 'om-maps:set', theme });
	});
</script>

<svelte:head>
	<title>Weather Map | Open-Meteo.com</title>
	<link rel="canonical" href="https://open-meteo.com/weather/maps" />
	<meta name="description" content="Interactive weather map powered by Open-Meteo" />
</svelte:head>

<!-- Full-bleed map: the layout drops its padding for this route. The map
     follows our theme through the color-scheme declared on :root/.dark -->
<div class="h-full w-full bg-background">
	<!-- allow="cross-origin-isolated" delegates SharedArrayBuffer use to the
	     map; it only takes effect when this site itself is served with
	     COOP/COEP headers (see README, Deployment) -->
	<iframe
		bind:this={iframeEl}
		src={iframeSrc}
		title="Open-Meteo Interactive Map"
		loading="lazy"
		allowfullscreen
		allow="cross-origin-isolated"
		referrerpolicy="no-referrer"
		class="block h-full w-full border-0"
		sandbox="allow-scripts allow-same-origin allow-forms allow-popups"
	></iframe>
</div>
