/**
 * `@platform-modules/notifications` — multi-channel delivery seam (zero channel code).
 */
/** Mirrors `@platform-modules/jobs` `IdempotencyStore` — host wires one store to both. */
interface DedupStore {
    seen(key: string): Promise<boolean>;
    mark(key: string, ttl?: number): Promise<void>;
}
interface PreferenceStore {
    getEnabledChannels(userId: string, eventType: string): Promise<string[]>;
}
type NotifyEvent = {
    id?: string;
    type: string;
    userId: string;
    dedupKey?: string;
    template: {
        subject?: string;
        body: string;
        html?: string;
    };
    data: Record<string, string>;
    recipients: Record<string, unknown>;
};
type RenderedMessage = {
    subject?: string;
    body: string;
    html?: string;
};
type ChannelError = {
    message: string;
    retryable?: boolean;
    code?: string;
};
type ChannelResult = {
    ok: true;
    id?: string;
} | {
    ok: false;
    error: ChannelError;
};
interface ChannelAdapter {
    channel: string;
    send(rendered: RenderedMessage, recipient: unknown): Promise<ChannelResult>;
    supports?(event: NotifyEvent): boolean;
}
type DeliveryResult = {
    channel: string;
    status: 'sent' | 'skipped' | 'deduped' | 'failed';
    id?: string;
    error?: ChannelError;
};
type NotifyContext = {
    adapters: ChannelAdapter[];
    preferences: PreferenceStore;
    dedup: DedupStore;
    /**
     * Wraps a per-channel delivery attempt. Within-call idempotence is a local
     * per-channel success flag (independent of the store): once an attempt
     * succeeds, further attempts short-circuit — so a retry after a successful
     * first attempt does not double-deliver, even for an unkeyed event. The
     * persistent dedup record is written on success only, so a retry after a
     * FAILED first attempt re-sends.
     */
    retry?: (attempt: () => Promise<ChannelResult>) => Promise<ChannelResult>;
};
/** Zero-dep `{{key}}` interpolation — missing keys become empty strings. */
declare function render(tpl: string, data: Record<string, string>): string;
declare function deriveDedupKey(event: NotifyEvent, channel: string, recipientKey: string): string | null;
declare function resolvePreferences(store: PreferenceStore, userId: string, eventType: string): Promise<string[]>;
declare function notify(event: NotifyEvent, ctx: NotifyContext): Promise<DeliveryResult[]>;

export { type ChannelAdapter, type ChannelError, type ChannelResult, type DedupStore, type DeliveryResult, type NotifyContext, type NotifyEvent, type PreferenceStore, type RenderedMessage, deriveDedupKey, notify, render, resolvePreferences };
