import type { SSESession } from '../routes/fastifyRouteTypes.ts';
import type { SSESessionSpy } from '../sse/SSESessionSpy.ts';
import { type ParsedSSEEvent } from '../sse/sseParser.ts';
/**
 * Interface for objects that have a sessionSpy (e.g., SSE controllers in test mode).
 */
export type HasSessionSpy = {
    connectionSpy: SSESessionSpy;
};
/**
 * Options for connecting to an SSE endpoint via HTTP.
 */
export type SSEHttpConnectOptions = {
    /** Query parameters to add to the URL */
    query?: Record<string, string | undefined>;
    /** Additional headers to send with the request */
    headers?: Record<string, string>;
};
/**
 * Options for connecting with automatic server-side connection waiting.
 */
export type SSEHttpConnectWithSpyOptions = SSEHttpConnectOptions & {
    /**
     * Wait for server-side connection registration after HTTP headers are received.
     * This eliminates the race condition between `connect()` returning and the
     * server-side handler completing connection registration.
     */
    awaitServerConnection: {
        /** The SSE controller (must have connectionSpy enabled via isTestMode) */
        controller: HasSessionSpy;
        /** Timeout in milliseconds (default: 5000) */
        timeout?: number;
    };
};
/**
 * Result when connecting with awaitServerConnection option.
 */
export type SSEHttpConnectResult = {
    client: SSEHttpClient;
    serverConnection: SSESession;
};
/**
 * SSE client for testing long-lived connections using real HTTP.
 *
 * This client uses the native `fetch()` API to establish a real HTTP connection
 * to an SSE endpoint. Events are streamed incrementally as the server sends them,
 * making it suitable for testing:
 *
 * - **Long-lived connections** that stay open indefinitely
 * - **Real-time notifications** where events arrive over time
 * - **Push-based streaming** where the client waits for server-initiated events
 *
 * **When to use SSEHttpClient vs SSEInjectClient:**
 *
 * | SSEHttpClient (this class)          | SSEInjectClient                      |
 * |-------------------------------------|--------------------------------------|
 * | Real HTTP connection via fetch()    | Fastify's inject() (no network)     |
 * | Events arrive incrementally         | All events returned at once         |
 * | Connection can stay open            | Response must complete              |
 * | Requires running server (listen())  | Works without starting server       |
 * | Use for: notifications, chat, feeds | Use for: OpenAI-style streaming     |
 *
 * @example
 * ```typescript
 * // 1. Start a real HTTP server
 * await app.listen({ port: 0 })
 * const address = app.server.address() as { port: number }
 * const baseUrl = `http://localhost:${address.port}`
 *
 * // 2. Connect to SSE endpoint (returns when headers are received)
 * const client = await SSEHttpClient.connect(baseUrl, '/api/notifications', {
 *   headers: { authorization: 'Bearer token' },
 * })
 *
 * // 3. Server can now send events at any time
 * controller.sendEvent(connectionId, { event: 'notification', data: { msg: 'Hello' } })
 *
 * // 4. Collect events as they arrive
 * const events = await client.collectEvents(3) // wait for 3 events
 * // or: collect until a specific event
 * const events = await client.collectEvents(e => e.event === 'done')
 *
 * // 5. Alternative: use async iterator for manual control
 * for await (const event of client.events()) {
 *   console.log('Received:', event.event, event.data)
 *   if (event.event === 'done') break
 * }
 *
 * // 6. Cleanup
 * client.close()
 * await app.close()
 * ```
 */
export declare class SSEHttpClient {
    /** The fetch Response object. Available immediately after connect() returns. */
    readonly response: Response;
    private readonly abortController;
    private readonly reader;
    private readonly decoder;
    private buffer;
    private closed;
    private constructor();
    /**
     * Connect to an SSE endpoint.
     *
     * The returned promise resolves as soon as HTTP headers are received,
     * indicating the connection is established. Events can then be consumed
     * via `events()` or `collectEvents()`.
     *
     * @param baseUrl - Base URL of the server (e.g., 'http://localhost:3000')
     * @param path - SSE endpoint path (e.g., '/api/notifications')
     * @param options - Connection options (query params, headers)
     * @returns Connected SSE client ready to receive events
     *
     * @example
     * ```typescript
     * // Basic connection (returns when HTTP headers received)
     * const client = await SSEHttpClient.connect(
     *   'http://localhost:3000',
     *   '/api/stream',
     *   { query: { userId: '123' }, headers: { authorization: 'Bearer token' } }
     * )
     *
     * // With awaitServerConnection (waits for server-side registration)
     * const { client, serverConnection } = await SSEHttpClient.connect(
     *   'http://localhost:3000',
     *   '/api/stream',
     *   { awaitServerConnection: { controller } }
     * )
     * // serverConnection is ready to use immediately
     * await controller.sendEvent(serverConnection.id, { event: 'test', data: {} })
     * ```
     */
    static connect(baseUrl: string, path: string, options: SSEHttpConnectWithSpyOptions): Promise<SSEHttpConnectResult>;
    static connect(baseUrl: string, path: string, options?: SSEHttpConnectOptions): Promise<SSEHttpClient>;
    /**
     * Async generator that yields parsed SSE events as they arrive.
     *
     * Use this for full control over event processing. The generator
     * completes when the server closes the connection or the abort signal fires.
     *
     * @param signal - Optional AbortSignal to stop the generator early
     *
     * @example
     * ```typescript
     * for await (const event of client.events()) {
     *   const data = JSON.parse(event.data)
     *   console.log(`[${event.event}]`, data)
     *
     *   if (event.event === 'done') {
     *     break // Stop consuming, connection stays open until close()
     *   }
     * }
     * ```
     *
     * @example
     * ```typescript
     * // With abort signal for timeout control
     * const controller = new AbortController()
     * setTimeout(() => controller.abort(), 5000)
     *
     * for await (const event of client.events(controller.signal)) {
     *   console.log(event)
     * }
     * ```
     */
    events(signal?: AbortSignal): AsyncGenerator<ParsedSSEEvent, void, unknown>;
    /**
     * Read from the stream with abort signal support.
     * Returns 'aborted' if the signal fires before read completes.
     */
    private readWithAbort;
    /**
     * Collect events until a count is reached or predicate returns true.
     *
     * @param countOrPredicate - Either a number of events to collect,
     *   or a predicate function that returns true when collection should stop.
     *   The event that matches the predicate IS included in the result.
     * @param timeout - Maximum time to wait in milliseconds (default: 5000)
     * @returns Array of collected events
     * @throws Error if timeout is reached before condition is met
     *
     * @example
     * ```typescript
     * // Collect exactly 5 events
     * const events = await client.collectEvents(5)
     *
     * // Collect until 'done' event is received
     * const events = await client.collectEvents(e => e.event === 'done')
     *
     * // Collect with custom timeout
     * const events = await client.collectEvents(10, 30000) // 30s timeout
     * ```
     */
    collectEvents(countOrPredicate: number | ((event: ParsedSSEEvent) => boolean), timeout?: number): Promise<ParsedSSEEvent[]>;
    /**
     * Close the connection from the client side.
     *
     * This aborts the underlying fetch request. Call this when done
     * consuming events to clean up resources.
     */
    close(): void;
}
