import { JSX } from 'preact'
import { useSignal } from '@preact/signals'

function RenderError(
	{ error, fallback }: { error: Error; fallback: Fallback | undefined },
) {
	if (fallback) {
		return fallback({ error })
	}

	return (
		<output>
			<pre>{String(error)}</pre>
		</output>
	)
}

export type Fallback = ({ error }: { error: Error }) => JSX.Element

export default function Suspense(
	{ loader, fallback, signal, children }: {
		loader: JSX.Element
		fallback?: Fallback
		signal?: AbortSignal
		children: Promise<JSX.Element>
	},
) {
	const displayed = useSignal(loader)
	let loaded = false

	signal?.addEventListener('abort', () => {
		if (loaded) return
		try {
			signal.throwIfAborted()
		} catch (error) {
			displayed.value = RenderError({ error, fallback })
		}
	})

	children
		.then((element) => {
			if (signal?.aborted) return
			displayed.value = element
			loaded = true
		})
		.catch((error) => {
			if (signal?.aborted) return
			displayed.value = RenderError({ error, fallback })
		})

	return <>{displayed}</>
}
