//#region src/index.ts
/** Zero-dep `{{key}}` interpolation — missing keys become empty strings. */
function render(tpl, data) {
	return tpl.replace(/\{\{(\w+)\}\}/g, (_match, key) => data[key] ?? "");
}
function deriveDedupKey(event, channel, recipientKey) {
	const base = event.dedupKey ?? event.id;
	if (base === void 0) return null;
	return `${base}:${channel}:${recipientKey}`;
}
function resolvePreferences(store, userId, eventType) {
	return store.getEnabledChannels(userId, eventType);
}
function recipientKey(recipient) {
	if (typeof recipient === "string") return recipient;
	return JSON.stringify(recipient);
}
function renderEvent(event) {
	return {
		subject: event.template.subject ? render(event.template.subject, event.data) : void 0,
		body: render(event.template.body, event.data),
		html: event.template.html ? render(event.template.html, event.data) : void 0
	};
}
async function notify(event, ctx) {
	const enabled = await resolvePreferences(ctx.preferences, event.userId, event.type);
	const rendered = renderEvent(event);
	const results = [];
	for (const adapter of ctx.adapters) {
		if (!enabled.includes(adapter.channel)) {
			results.push({
				channel: adapter.channel,
				status: "skipped"
			});
			continue;
		}
		if (adapter.supports && !adapter.supports(event)) {
			results.push({
				channel: adapter.channel,
				status: "skipped"
			});
			continue;
		}
		const recipient = event.recipients[adapter.channel];
		if (recipient === void 0) {
			results.push({
				channel: adapter.channel,
				status: "skipped"
			});
			continue;
		}
		const dedupKey = deriveDedupKey(event, adapter.channel, recipientKey(recipient));
		if (dedupKey !== null && await ctx.dedup.seen(dedupKey)) {
			results.push({
				channel: adapter.channel,
				status: "deduped"
			});
			continue;
		}
		let delivered = false;
		const attempt = async () => {
			if (delivered) return { ok: true };
			const result = await adapter.send(rendered, recipient);
			if (result.ok) {
				delivered = true;
				if (dedupKey !== null) await ctx.dedup.mark(dedupKey);
			}
			return result;
		};
		try {
			const sendResult = ctx.retry ? await ctx.retry(attempt) : await attempt();
			if (sendResult.ok) results.push({
				channel: adapter.channel,
				status: "sent",
				id: sendResult.id
			});
			else results.push({
				channel: adapter.channel,
				status: "failed",
				error: sendResult.error
			});
		} catch (err) {
			results.push({
				channel: adapter.channel,
				status: "failed",
				error: {
					message: err instanceof Error ? err.message : String(err),
					retryable: true
				}
			});
		}
	}
	return results;
}
//#endregion
export { deriveDedupKey, notify, render, resolvePreferences };
