import type { SSEReplyInterface } from '@fastify/sse';
import type { FastifyReply, FastifyRequest } from 'fastify';
import type { z } from 'zod';
import type { DualModeType } from '../dualmode/dualModeTypes.ts';
import type { SSERoomManager } from '../sse/rooms/SSERoomManager.ts';
import type { SSEEventSchemas, SSELogger, SSEMessage } from '../sse/sseTypes.ts';
import type { SSEContext, SSESession, SSESessionMode } from './fastifyRouteTypes.ts';
/**
 * FastifyReply extended with SSE capabilities from @fastify/sse.
 */
export type SSEReply = FastifyReply & {
    sse: SSEReplyInterface;
};
/**
 * Minimal interface for SSE controller methods used by route utilities.
 * This allows shared utilities to work with both SSE and dual-mode controllers.
 */
export type SSEControllerLike = {
    _sendEventRaw(connectionId: string, message: SSEMessage): Promise<boolean>;
    registerConnection(connection: SSESession): void;
    unregisterConnection(connectionId: string): void;
    /** Room manager, if rooms are enabled */
    _internalRoomManager?: SSERoomManager;
};
/**
 * Reason why the SSE connection was closed.
 * - 'server': Server explicitly called closeConnection() or returned success('disconnect')
 * - 'client': Client closed the connection (EventSource.close(), navigated away, etc.)
 */
export type SSECloseReason = 'server' | 'client';
/**
 * Options for SSE connection lifecycle hooks.
 */
export type SSELifecycleOptions<TConnection = SSESession> = {
    onConnect?: (connection: TConnection) => void | Promise<void>;
    /**
     * Called when the SSE connection closes for any reason (client disconnect,
     * network failure, or server explicitly closing via closeConnection()).
     *
     * @param connection - The connection that was closed
     * @param reason - Why the connection was closed:
     *   - 'server': Server explicitly closed (closeConnection() or success('disconnect'))
     *   - 'client': Client closed (EventSource.close(), navigation, network failure)
     *
     * Use this for cleanup like unsubscribing from events or removing from tracking.
     */
    onClose?: (connection: TConnection, reason: SSECloseReason) => void | Promise<void>;
    onReconnect?: (connection: TConnection, lastEventId: string) => Iterable<SSEMessage> | AsyncIterable<SSEMessage> | void | Promise<void>;
    logger?: SSELogger;
};
/**
 * Extract Fastify path template from pathResolver.
 *
 * This function creates placeholder params with ':paramName' values and calls
 * the pathResolver to generate a Fastify-compatible path template.
 *
 * @example
 * ```typescript
 * // pathResolver: (p) => `/users/${p.userId}/posts/${p.postId}`
 * // paramsSchema: z.object({ userId: z.string(), postId: z.string() })
 * // Result: '/users/:userId/posts/:postId'
 * ```
 */
export declare function extractPathTemplate<Params>(pathResolver: (params: Params) => string, paramsSchema: z.ZodObject<z.ZodRawShape>): string;
/**
 * Check if an error has a valid httpStatusCode property (like PublicNonRecoverableError).
 * Uses duck typing instead of instanceof for reliability across realms.
 * Validates the status code is a finite integer within valid HTTP range (100-599).
 */
export declare function hasHttpStatusCode(err: unknown): err is {
    httpStatusCode: number;
};
/**
 * Send replay events from either sync or async iterables.
 */
export declare function sendReplayEvents(sseReply: SSEReply, replayEvents: Iterable<SSEMessage> | AsyncIterable<SSEMessage>): Promise<void>;
/**
 * Handle Last-Event-ID reconnection by replaying missed events.
 */
export declare function handleReconnection(sseReply: SSEReply, connection: SSESession, lastEventId: string, options: SSELifecycleOptions | undefined, logPrefix?: string): Promise<void>;
/**
 * Send error event to client and close connection gracefully.
 */
export declare function handleSSEError(sseReply: SSEReply, controller: SSEControllerLike, connectionId: string, err: unknown, logger?: SSELogger): Promise<void>;
/**
 * Result of setting up an SSE connection.
 */
export type SSESessionSetupResult<Events extends SSEEventSchemas = SSEEventSchemas> = {
    connectionId: string;
    connection: SSESession<Events>;
    connectionClosed: Promise<void>;
    sseReply: SSEReply;
};
/**
 * Result of creating an SSE context.
 */
export type SSEContextResult<Events extends SSEEventSchemas = SSEEventSchemas> = {
    sseContext: SSEContext<Events>;
    /** Promise that resolves when client disconnects */
    connectionClosed: Promise<void>;
    /** The SSE reply object for advanced operations */
    sseReply: SSEReply;
    /** Get the connection if streaming was started */
    getConnection: () => SSESession<Events> | undefined;
    /** Get the connection ID if streaming was started */
    getConnectionId: () => string | undefined;
    /** Check if streaming was started */
    isStarted: () => boolean;
    /** Check if a response was sent via sse.respond() */
    hasResponse: () => boolean;
    /** Get the response data if sse.respond() was called */
    getResponseData: () => {
        code: number;
        body: unknown;
    } | undefined;
    /** Get the session mode if streaming was started */
    getMode: () => SSESessionMode | undefined;
};
/**
 * Create an SSEContext for deferred header sending.
 *
 * This factory creates the `sse` parameter passed to SSE handlers, allowing:
 * - Validation before headers are sent
 * - Proper HTTP error responses (404, 422, etc.)
 * - Explicit streaming start via `sse.start()`
 *
 * @param controller - The SSE controller for connection management
 * @param request - The Fastify request
 * @param reply - The Fastify reply
 * @param eventSchemas - Event schemas for type-safe event sending
 * @param options - Lifecycle hooks and options
 * @param logPrefix - Prefix for log messages
 *
 * @returns SSEContext result with context object and state accessors
 */
export declare function createSSEContext<Events extends SSEEventSchemas>(controller: SSEControllerLike, request: FastifyRequest, reply: FastifyReply, eventSchemas: Events, options: SSELifecycleOptions | undefined, logPrefix?: string): SSEContextResult<Events>;
/**
 * Setup an SSE connection with all the boilerplate:
 * - Create connection object with typed event sender
 * - Register with controller
 * - Setup disconnect handler
 * - Initialize SSE reply (keepAlive, sendHeaders, flushHeaders)
 * - Handle reconnection
 * - Call onConnect hook
 *
 * @deprecated Use createSSEContext for new code. This function is kept for backwards compatibility.
 *
 * @returns Connection setup result with connection object and closed promise
 */
export declare function setupSSESession<Events extends SSEEventSchemas>(controller: SSEControllerLike, request: FastifyRequest, reply: FastifyReply, eventSchemas: Events, options: SSELifecycleOptions | undefined, logPrefix?: string): Promise<SSESessionSetupResult<Events>>;
/**
 * Determine response mode from Accept header.
 *
 * Parses the Accept header and determines whether to use JSON or SSE mode.
 * Supports quality values (q=) for content negotiation.
 *
 * @param accept - The Accept header value
 * @param defaultMode - Mode to use when no preference is specified
 * @returns The determined response mode
 */
export declare function determineMode(accept: string | undefined, defaultMode?: DualModeType): DualModeType;
/**
 * Result of sync format determination.
 */
export type SyncFormatResult = {
    mode: 'sse';
} | {
    mode: 'sync';
    contentType: string;
};
/**
 * Determine sync format from Accept header for content negotiation.
 *
 * Parses the Accept header and determines which format to use.
 * Supports quality values (q=) for content negotiation and subtype wildcards
 * (e.g., "application/*", "text/*").
 *
 * Matching priority:
 * 1. text/event-stream (SSE mode)
 * 2. Exact matches against supportedFormats
 * 3. Subtype wildcards (e.g., "text/*" matches first "text/..." in supportedFormats)
 * 4. Full wildcard (*\/*) uses fallback format
 * 5. Fallback to defaultFormat or first supported format
 *
 * @param accept - The Accept header value
 * @param supportedFormats - Array of Content-Types that the route supports
 * @param defaultFormat - Format to use when no preference is specified (default: first supported format)
 * @returns The determined format or 'sse' mode indicator
 */
export declare function determineSyncFormat(accept: string | undefined, supportedFormats: string[], defaultFormat?: string): SyncFormatResult;
