import { Context } from "hono";
import { Bindings, Variables } from "../types";
import { FORM_FIELD_TYPES, LoginSession, Node, User } from "@authhero/adapter-interfaces";
import { EnrichedClient } from "../helpers/client";
export { FORM_FIELD_TYPES };
export declare function isFormHook(hook: any): hook is {
    form_id: string;
    enabled: boolean;
};
/**
 * Resolves a template string like "{{context.user.email}}", "{{user.id}}", or "{{$form.gender}}" to its actual value
 */
export declare function resolveTemplateField(field: string, context: ResolveContext): string | undefined;
/**
 * Context passed to resolveNode and condition evaluation
 */
export interface ResolveContext {
    user: User;
    submittedFields?: Record<string, string>;
}
/**
 * Flow action type used during resolution
 */
interface FlowAction {
    type: string;
    action?: string;
    params?: {
        target?: "change-email" | "account" | "custom";
        custom_url?: string;
        user_id?: string;
        connection_id?: string;
        changes?: Record<string, unknown>;
    };
}
/**
 * Flow fetcher function type for async flow resolution
 */
export type FlowFetcher = (flowId: string) => Promise<{
    actions?: FlowAction[];
} | null>;
/**
 * Pending user update action to be executed by the caller
 */
export interface PendingUserUpdate {
    user_id: string;
    changes: Record<string, string>;
}
/**
 * Builds userUpdates object from a PendingUserUpdate's changes map.
 * Handles dot-notation key prefixes:
 *  - "metadata.X" → user_metadata.X
 *  - "address.X"  → address.X  (nested OIDC address claim)
 *  - anything else → top-level user field
 */
export declare function buildUserUpdates(changes: Record<string, string>, existingUser: {
    user_metadata?: unknown;
    address?: unknown;
}): Record<string, unknown>;
/**
 * Merge multiple PendingUserUpdate entries by user_id so that overlapping
 * changes (e.g. two updates both touching metadata.*) are accumulated into
 * a single changes map per user.  This avoids the stale-snapshot problem
 * where each call to buildUserUpdates would spread the *original* user
 * object, causing later writes to overwrite earlier ones.
 */
export declare function mergeUserUpdates(updates: PendingUserUpdate[]): PendingUserUpdate[];
/**
 * Result type for node resolution
 */
type ResolveNodeResult = {
    type: "step";
    nodeId: string;
    userUpdates?: PendingUserUpdate[];
} | {
    type: "redirect";
    target: string;
    customUrl?: string;
    userUpdates?: PendingUserUpdate[];
} | {
    type: "end";
    userUpdates?: PendingUserUpdate[];
} | null;
/**
 * Resolves the target redirect URL based on the target type
 */
export declare function getRedirectUrl(target: "change-email" | "account" | "custom", customUrl: string | undefined, state: string): string;
/**
 * Resolves the first displayable node by following ROUTER, ACTION, and FLOW nodes
 */
export declare function resolveNode(nodes: Node[], startNodeId: string, context: ResolveContext, flowFetcher?: FlowFetcher, maxDepth?: number): Promise<ResolveNodeResult>;
/**
 * Handles a form hook: validates the form exists and returns a redirect Response to the first node.
 * If the form resolves to 'end' or no step node is found, returns the user to continue normal auth flow.
 * Throws if the form or start node is missing.
 */
export declare function handleFormHook(ctx: Context<{
    Bindings: Bindings;
    Variables: Variables;
}>, form_id: string, loginSession: LoginSession, user?: User, client?: EnrichedClient): Promise<User | Response>;
