/**
 * HTTP client for the Splitwise API.
 *
 * Wraps `fetch` with:
 *  - bearer-token auth (token is fetched per request via `getAccessToken`)
 *  - automatic snake_case <-> camelCase conversion at the boundary
 *  - form-urlencoded request bodies (the Splitwise API's default) with an
 *    opt-in JSON path for new endpoints
 *  - typed error responses via `createApiError`
 *  - transparent retries with exponential backoff
 *  - request timeouts via AbortController
 */
import type { LogLevel, Logger } from './types.js';
export interface HttpClientConfig {
    baseUrl: string;
    getAccessToken: () => Promise<string>;
    fetch?: typeof fetch;
    /** Per-request timeout in ms. Default 30000. */
    timeout?: number;
    /** Maximum retries for transient failures. Default 2. */
    maxRetries?: number;
    logger?: Logger;
    /** Default 'none'. */
    logLevel?: LogLevel;
    /** Optional User-Agent string. */
    userAgent?: string;
    /** Lifecycle hooks; see `Hooks` interface for the contract. */
    hooks?: Hooks;
}
/**
 * Lifecycle hooks for observability and side-effects. Inspired by Stripe's
 * event emitter (`stripe.on('request', cb)`), but expressed as a plain
 * options bag so consumers don't need to import an event-emitter API.
 *
 * Hooks are called synchronously (not awaited). Returning a Promise has no
 * effect on the request flow -- if a hook needs to do async work, it should
 * fire-and-forget, and any thrown error is caught and ignored so that hook
 * misbehavior doesn't break SDK calls.
 */
export interface Hooks {
    /** Called before each HTTP request leaves the client. */
    onRequest?: (event: RequestEvent) => void;
    /**
     * Called whenever the SDK receives an HTTP response, regardless of status.
     * Fires for 2xx success, 4xx/5xx errors, and 200 responses with embedded
     * errors -- i.e. any time the network actually returned something. Pair
     * with `onError` if you want a separate signal for failures.
     */
    onResponse?: (event: ResponseEvent) => void;
    /**
     * Called for every error that the SDK is about to throw. Includes both
     * transport failures (connection, timeout, abort) and API errors. Fires
     * once per attempt, so retried requests fire the hook multiple times.
     */
    onError?: (event: ErrorEvent) => void;
}
export interface RequestEvent {
    method: string;
    url: string;
    /** The Authorization header is replaced with "Bearer [REDACTED]" for safety. */
    headers: Record<string, string>;
    /** 1-indexed; >1 indicates a retry. */
    attempt: number;
}
export interface ResponseEvent {
    method: string;
    url: string;
    status: number;
    /** Response headers (lowercased keys). */
    headers: Record<string, string>;
    /** Wall-clock ms from request dispatch to response received. */
    durationMs: number;
    attempt: number;
}
export interface ErrorEvent {
    method: string;
    url: string;
    error: unknown;
    durationMs: number;
    attempt: number;
}
/**
 * Per-request overrides exposed publicly to consumers of the SDK. The same
 * options bag is accepted by every resource method (as the second argument)
 * and by `sw.rawRequest()`. Internal-only options live on `RequestOptions`.
 */
export interface RequestOverrides {
    /** Cancel the request via an AbortSignal. */
    signal?: AbortSignal;
    /** Override the client's per-request timeout (ms) for this call. */
    timeout?: number;
    /** Override the client's `maxRetries` for this call (0 disables retry). */
    maxRetries?: number;
    /** Override the base URL for this call (rare; useful for testing). */
    baseUrl?: string;
}
export interface RequestOptions extends RequestOverrides {
    /** Query string parameters. Always sent in the URL regardless of method. */
    query?: Record<string, unknown>;
    /** Body, used for POST/PUT/DELETE. */
    body?: Record<string, unknown>;
    /**
     * If true (default), serialize body as form-urlencoded with flattenParams
     * (the Splitwise convention). If false, send as JSON with snake_case keys.
     */
    formEncoded?: boolean;
    /**
     * Property to extract from the parsed response, e.g. 'expenses' to get the
     * value of `{ expenses: [...] }`. If undefined, returns the full response.
     */
    unwrapKey?: string;
    /**
     * If true, skip the SplitwiseConstraintError throw for 200 responses with
     * `success:false` or non-empty `errors`. Used by endpoints (like
     * /parse_sentence) where these fields are normal response data rather
     * than failure signals.
     */
    bypassEmbeddedErrors?: boolean;
}
export declare class HttpClient {
    private readonly baseUrl;
    private readonly getAccessToken;
    private readonly fetchImpl;
    private readonly timeout;
    private readonly maxRetries;
    private readonly logger;
    private readonly userAgent;
    private readonly hooks;
    constructor(config: HttpClientConfig);
    get<T>(path: string, options?: Omit<RequestOptions, 'body' | 'formEncoded'>): Promise<T>;
    post<T>(path: string, options?: RequestOptions): Promise<T>;
    put<T>(path: string, options?: RequestOptions): Promise<T>;
    delete<T>(path: string, options?: RequestOptions): Promise<T>;
    private request;
    private requestOnce;
    /**
     * Calls a hook if registered. Wraps the event-builder in a function so we
     * skip the work entirely when no hook is registered. Catches synchronous
     * throws so misbehaving user code doesn't break the request.
     */
    private fireHook;
    private handleResponse;
}
//# sourceMappingURL=http.d.ts.map