<script lang="ts" module>
	// Per-instance id counter so two ConfirmSheets coexisting (e.g. one closing
	// as another opens) don't share the same aria-labelledby target and confuse AT.
	let nextId = 0;
</script>

<script lang="ts">
	import { focusTrap } from '$lib/actions/focus-trap';
	import { scrollLock } from '$lib/actions/scroll-lock';

	interface Props {
		open: boolean;
		title: string;
		message?: string;
		confirmLabel?: string;
		cancelLabel?: string;
		tone?: 'default' | 'danger';
		onConfirm: () => void | Promise<void>;
		onCancel: () => void;
	}

	let {
		open,
		title,
		message,
		confirmLabel = 'Bestätigen',
		cancelLabel = 'Abbrechen',
		tone = 'default',
		onConfirm,
		onCancel
	}: Props = $props();

	const titleId = `confirm-sheet-title-${++nextId}`;
	let busy = $state(false);

	async function handleConfirm() {
		if (busy) return;
		busy = true;
		try {
			await onConfirm();
		} finally {
			busy = false;
		}
	}
</script>

{#if open}
	<!-- Backdrop is a real <button> so keyboard / switch-control users get parity with the mouse path. -->
	<button
		type="button"
		class="fixed inset-0 z-50 bg-black/40"
		aria-label="Schließen"
		onclick={onCancel}
		disabled={busy}
		tabindex="-1"
	></button>
	<!-- Bottom-anchored, so a sheet taller than the viewport loses its TOP first: the title
	     (the only thing naming what you are confirming) slides off-screen while the danger
	     button stays. A long message plus a landscape phone is enough. Cap the height and
	     scroll instead; `overscroll-contain` keeps the page behind from scrolling with it. -->
	<div
		class="fixed inset-x-0 bottom-0 z-50 max-h-[85dvh] overflow-y-auto overscroll-contain rounded-t-2xl bg-white px-5 pb-10 pt-6 dark:bg-gray-900"
		style="padding-bottom: calc(env(safe-area-inset-bottom) + 1.5rem)"
		role="dialog"
		aria-modal="true"
		aria-labelledby={titleId}
		data-testid="confirm-sheet"
		use:focusTrap={{ onclose: onCancel }}
		use:scrollLock
	>
		<div class="mb-4 flex justify-center">
			<div class="h-1 w-10 rounded-full bg-gray-300 dark:bg-gray-600"></div>
		</div>
		<h3 id={titleId} class="mb-1 text-center text-lg font-bold text-gray-900 dark:text-gray-100">
			{title}
		</h3>
		{#if message}
			<p class="mb-6 text-center text-sm text-gray-500 dark:text-gray-400">{message}</p>
		{:else}
			<div class="mb-4"></div>
		{/if}
		<button
			type="button"
			onclick={handleConfirm}
			disabled={busy}
			data-testid="confirm-sheet-confirm"
			class="btn btn-block mb-3 {tone === 'danger' ? 'btn-danger' : 'btn-primary'}"
		>
			{#if busy}
				<span
					class="inline-block h-4 w-4 animate-spin rounded-full border-2 border-white/40 border-t-white"
					aria-hidden="true"
				></span>
			{/if}
			{confirmLabel}
		</button>
		<button
			type="button"
			onclick={onCancel}
			disabled={busy}
			data-testid="confirm-sheet-cancel"
			class="btn btn-secondary btn-block"
		>
			{cancelLabel}
		</button>
	</div>
{/if}
