<script lang="ts" module>
	// Action shape used by every long-press / kebab menu in the app. Defined in `module`
	// so importers don't pay for instance state.
	export interface ContextAction {
		label: string;
		icon?: string; // optional emoji or unicode glyph
		tone?: 'default' | 'danger';
		disabled?: boolean;
		onClick: () => void | Promise<void>;
	}
</script>

<script lang="ts">
	// Reusable bottom-sheet for context menus. Consumers pass `open`, an array of
	// `actions`, and a close callback. Mobile: long-press → open. Desktop: kebab
	// icon → open. The sheet itself is platform-agnostic.

	interface Props {
		open: boolean;
		actions: ContextAction[];
		onClose: () => void;
		title?: string;
	}

	import { scrollLock } from '$lib/actions/scroll-lock';

	let { open, actions, onClose, title }: Props = $props();

	let sheet = $state<HTMLDivElement | null>(null);
	let returnFocus: HTMLElement | null = null;

	function onKeyDown(e: KeyboardEvent) {
		if (e.key === 'Escape') {
			e.preventDefault();
			onClose();
			return;
		}
		if (e.key !== 'Tab' || !sheet) return;
		const list = Array.from(sheet.querySelectorAll<HTMLElement>('button:not([disabled])'));
		if (list.length === 0) return;
		const first = list[0];
		const last = list[list.length - 1];
		const active = document.activeElement as HTMLElement | null;
		if (e.shiftKey && (active === first || !sheet.contains(active))) {
			e.preventDefault();
			last.focus();
		} else if (!e.shiftKey && active === last) {
			e.preventDefault();
			first.focus();
		}
	}

	// Focus the first action and attach a global keydown listener only while open.
	// The sheet stays mounted (translate-y animation), so this is wired via $effect
	// rather than use:focusTrap (which would activate on first mount).
	$effect(() => {
		if (open) {
			returnFocus = (document.activeElement as HTMLElement | null) ?? null;
			requestAnimationFrame(() => {
				const first = sheet?.querySelector<HTMLButtonElement>('button:not([disabled])');
				first?.focus({ preventScroll: true });
			});
			window.addEventListener('keydown', onKeyDown);
			return () => window.removeEventListener('keydown', onKeyDown);
		} else if (returnFocus) {
			try {
				returnFocus.focus({ preventScroll: true });
			} catch {
				/* element gone */
			}
			returnFocus = null;
		}
	});

	async function handle(action: ContextAction) {
		if (action.disabled) return;
		try {
			await action.onClick();
		} finally {
			onClose();
		}
	}
</script>

<!-- This sheet stays mounted (translate-y animation), so a sentinel that mounts only
     while open drives the shared body scroll-lock. -->
{#if open}
	<div use:scrollLock class="hidden"></div>
{/if}

<!-- Backdrop — real <button> so keyboard / switch-control users get parity. -->
<button
	type="button"
	class="fixed inset-0 z-40 bg-black/50 transition-opacity duration-200"
	class:opacity-0={!open}
	class:pointer-events-none={!open}
	class:opacity-100={open}
	onclick={onClose}
	tabindex="-1"
	aria-label="Schließen"
></button>

<!-- Sheet -->
<div
	bind:this={sheet}
	class="fixed inset-x-0 bottom-0 z-50 rounded-t-2xl bg-white transition-transform duration-200 dark:bg-gray-900"
	class:translate-y-full={!open}
	class:translate-y-0={open}
	style="padding-bottom: env(safe-area-inset-bottom)"
	role={open ? 'dialog' : undefined}
	aria-modal={open ? 'true' : undefined}
	aria-hidden={!open}
	inert={!open}
	tabindex="-1"
	data-testid="context-sheet"
>
	<div class="flex justify-center pt-3 pb-1">
		<div class="h-1 w-10 rounded-full bg-gray-300 dark:bg-gray-600"></div>
	</div>

	{#if title}
		<p
			class="px-5 pt-1 pb-2 text-xs font-semibold uppercase tracking-wide text-gray-500 dark:text-gray-400"
		>
			{title}
		</p>
	{/if}

	<div class="space-y-1 px-3 pb-3 pt-1">
		{#each actions as action (action.label)}
			<button
				type="button"
				disabled={action.disabled}
				onclick={() => handle(action)}
				class="flex w-full items-center gap-3 rounded-xl px-4 py-3 text-left text-base transition active:bg-gray-100 disabled:opacity-50 dark:active:bg-gray-700"
				class:text-gray-900={action.tone !== 'danger'}
				class:dark:text-gray-100={action.tone !== 'danger'}
				class:text-red-600={action.tone === 'danger'}
				class:dark:text-red-400={action.tone === 'danger'}
				class:hover:bg-gray-50={!action.disabled}
				class:dark:hover:bg-gray-800={!action.disabled}
			>
				{#if action.icon}
					<span class="text-xl leading-none">{action.icon}</span>
				{/if}
				<span class="font-medium">{action.label}</span>
			</button>
		{/each}

		<button type="button" onclick={onClose} class="btn btn-secondary btn-block mt-2">
			Abbrechen
		</button>
	</div>
</div>
