/**
 * Context-based confirmation service. Provides an async `confirm()` function
 * that any component can call without managing its own modal state.
 *
 * ## Setup
 * Mount `<ConfirmDialog>` once in the root `+layout.svelte` — it sets up the context
 * automatically. Then call `getConfirmService()` from any descendant component.
 *
 * ## Usage in event handlers
 * ```typescript
 * import { getConfirmService } from '$lib/shared/services/confirm.svelte.js';
 * const { confirm } = getConfirmService();
 *
 * async function handleDelete() {
 *   const ok = await confirm({ title: m.confirm_delete_title(), destructive: true });
 *   if (ok) doDelete();
 * }
 * ```
 *
 * ## Usage with use:enhance
 * ```svelte
 * <form use:enhance={async ({ cancel }) => {
 *   const ok = await confirm({ title: m.confirm_delete_title(), destructive: true });
 *   if (!ok) cancel();
 * }}>
 * ```
 */
import { getContext, setContext } from 'svelte';
import { browser } from '$app/environment';

export const CONFIRM_KEY = Symbol('confirm');

export interface ConfirmOptions {
	title: string;
	body?: string;
	/** Defaults to m.btn_confirm() ("Bestätigen") */
	confirmLabel?: string;
	/** Defaults to m.btn_cancel() ("Abbrechen") */
	cancelLabel?: string;
	/** Uses danger color for confirm button. Defaults to false. */
	destructive?: boolean;
	/** Close when clicking outside the dialog. Defaults to !destructive. */
	closeOnBackdrop?: boolean;
}

export interface ConfirmService {
	confirm(opts: ConfirmOptions): Promise<boolean>;
	/** Read by ConfirmDialog to render the current dialog. Internal use only. */
	readonly options: ConfirmOptions | null;
	/** Called by ConfirmDialog when the user makes a choice. Internal use only. */
	settle(value: boolean): void;
}

export function createConfirmService(): ConfirmService {
	let resolveRef: ((value: boolean) => void) | null = null;
	let options: ConfirmOptions | null = $state(null);

	return {
		confirm(opts: ConfirmOptions): Promise<boolean> {
			if (!browser) return Promise.resolve(false);
			// Concurrent call while dialog is already open — reject immediately.
			if (resolveRef !== null) return Promise.resolve(false);
			options = opts;
			return new Promise((r) => {
				resolveRef = r;
			});
		},

		get options() {
			return options;
		},

		settle(value: boolean): void {
			options = null;
			const r = resolveRef;
			resolveRef = null;
			r?.(value);
		}
	};
}

export function provideConfirmService(): ConfirmService {
	const service = createConfirmService();
	setContext(CONFIRM_KEY, service);
	return service;
}

export function getConfirmService(): ConfirmService {
	// Outside component init, getContext either returns undefined or throws a Svelte error.
	// Either way, map it to our descriptive developer error.
	let service: ConfirmService | undefined;
	try {
		service = getContext<ConfirmService>(CONFIRM_KEY);
	} catch {
		throw new Error('ConfirmService not found — mount <ConfirmDialog> in +layout.svelte');
	}
	if (!service)
		throw new Error('ConfirmService not found — mount <ConfirmDialog> in +layout.svelte');
	return service;
}
