<script lang="ts">
	import { untrack } from "svelte";
	import { File, type FileOptions } from "@pierre/diffs";
	import { pierreFileOptions } from "./pierre";

	interface Props {
		// used for the header and to infer the highlighting language
		name: string;
		contents: string;
		// pierre's native overflow: wrap keeps highlighting instead of scrolling
		wrap?: boolean;
		// server-prerendered shadow dom, only set for the initial ssr
		prerenderedHTML?: string;
		// forwarded to pierre, fires after every (async-safe) render pass
		onPostRender?: FileOptions<undefined>["onPostRender"];
	}

	let { name, contents, wrap = false, prerenderedHTML, onPostRender }: Props = $props();

	let element: HTMLElement | undefined = $state();

	let instance: File | undefined;

	// same manual updates as PierreDiff, this effect owns the instance,
	// a contents swap re-renders in place instead of a shadow dom teardown
	$effect(() => {
		const host = element;
		if (!host) return;
		// scroll and wrap lay out differently enough in grid to re-hydrate on
		// toggle, and the toggle is rare
		const options: FileOptions<undefined> = {
			...pierreFileOptions(wrap),
			onPostRender
		};

		// sveltekit re-runs the load on hydration, the prop never reaches the
		// client. pierre marks its ssr output with data-dehydrated
		const dehydrated = host.shadowRoot?.querySelector("[data-dehydrated]") != null;
		if (prerenderedHTML === undefined && !dehydrated) host.shadowRoot?.replaceChildren();
		const created = new File(options, undefined, true);
		// untracked, the render effect below owns updates
		created.hydrate({
			file: untrack(() => ({ name, contents })),
			fileContainer: host,
			prerenderedHTML
		});
		instance = created;

		return () => {
			created.cleanUp();
			instance = undefined;
		};
	});

	// hydrate paints the initial file, this pass covers later name/contents
	// changes
	let firstRender = true;
	$effect(() => {
		const file = { name, contents };
		if (firstRender) {
			firstRender = false;
			return;
		}
		instance?.render({ file, forceRender: true });
	});
</script>

<diffs-container bind:this={element} class="diffs-container">
	{#if prerenderedHTML}
		<!-- pierre's own renderer output, token text is escaped by its hast builder -->
		<template shadowrootmode="open">{@html prerenderedHTML}</template>
	{/if}
</diffs-container>
