import { RedisClientOptions } from 'redis';

/**
 * Pluggable wire-format codec for Redis string values used by `RedisStringsHandler`.
 *
 * The serializer is the single point at which an in-memory `CacheEntry` becomes the
 * string written to Redis (and back). Plugging in a custom serializer lets you add
 * compression (gzip/brotli), encryption, or any other custom encoding without forking
 * this package or losing the existing dedup / batch / keyspace features.
 *
 * Both `serialize` and `deserialize` may return either a value directly or a `Promise`,
 * which enables non-blocking async codecs such as stream-based compression
 * (`zlib.brotliCompress`) or encryption (`crypto.subtle`). Synchronous implementations
 * continue to work unchanged - awaiting a plain value is a no-op.
 *
 * The default export {@link jsonCacheValueSerializer} is `JSON.stringify` /
 * `JSON.parse` paired with {@link bufferAndMapReplacer} / {@link bufferAndMapReviver},
 * so native `Buffer` and `Map` values inside a `CacheEntry` round-trip transparently
 * (this is required for Next.js RSC payloads).
 *
 * Operational note: changing the serializer (or any of its parameters such as a
 * compression level or encryption key) makes existing Redis keys unreadable, because
 * the deserializer will fail or return `null` for entries written by the previous
 * format. Either flush the affected keys, bump `keyPrefix`, or migrate values
 * out-of-band before deploying a new serializer.
 */

type CacheValueSerializer = {
    /**
     * Encode an in-memory `CacheEntry` into the string written to Redis.
     * May return a `Promise` for async codecs (e.g. compression, encryption).
     */
    serialize(value: CacheEntry): string | Promise<string>;
    /**
     * Decode a string read from Redis back into a `CacheEntry`.
     * Returning `null` (or a `Promise<null>`) signals "treat as cache miss" -
     * the handler will return `null` from `get()` without surfacing an error.
     * May return a `Promise` for async codecs.
     */
    deserialize(stored: string): CacheEntry | null | Promise<CacheEntry | null>;
};
/**
 * Default serializer used by `RedisStringsHandler` when no `valueSerializer` is
 * configured. Wraps `JSON.stringify` / `JSON.parse` with this package's
 * {@link bufferAndMapReplacer} / {@link bufferAndMapReviver} so native `Buffer`
 * and `Map` values inside a `CacheEntry` survive the round-trip.
 *
 * Exported as a singleton so consumers can compare against the default by
 * reference (e.g. to detect that no custom serializer was configured).
 */
declare const jsonCacheValueSerializer: CacheValueSerializer;

type CacheEntry = {
    value: unknown;
    lastModified: number;
    tags: string[];
};
type CreateRedisStringsHandlerOptions = {
    /** Redis redisUrl to use.
     * @default process.env.REDIS_URL? process.env.REDIS_URL : process.env.REDISHOST
            ? `redis://${process.env.REDISHOST}:${process.env.REDISPORT}`
            : 'redis://localhost:6379'
     */
    redisUrl?: string;
    /** Redis database number to use. Uses DB 0 for production, DB 1 otherwise
     * @default process.env.VERCEL_ENV === 'production' ? 0 : 1
     */
    database?: number;
    /** Prefix added to all Redis keys
     * @default process.env.KEY_PREFIX || process.env.VERCEL_URL || 'UNDEFINED_URL_'
     */
    keyPrefix?: string;
    /** Timeout in milliseconds for time critical Redis operations (during cache get, which blocks site rendering).
     *  If redis get is not fulfilled within this time, the cache handler will return null so site rendering will
     *  not be blocked further and site can fallback to re-render/re-fetch the content.
     * @default 500
     */
    getTimeoutMs?: number;
    /** Number of entries to query in one batch during full sync of shared tags hash map
     * @default 250
     */
    revalidateTagQuerySize?: number;
    /** Key used to store shared tags hash map in Redis
     * @default '__sharedTags__'
     */
    sharedTagsKey?: string;
    /** Average interval in milliseconds between tag map full re-syncs
     * @default 3600000 (1 hour)
     */
    avgResyncIntervalMs?: number;
    /** Enable deduplication of Redis get requests via internal in-memory cache
     * @default true
     */
    redisGetDeduplication?: boolean;
    /** Time in milliseconds to cache Redis get results in memory. Set this to 0 to disable in-memory caching completely
     * @default 10000
     */
    inMemoryCachingTime?: number;
    /** Default stale age in seconds for cached items
     * @default 1209600 (14 days)
     */
    defaultStaleAge?: number;
    /** Function to calculate expire age (redis TTL value) from stale age
     * @default Production: staleAge * 2, Other: staleAge * 1.2
     */
    estimateExpireAge?: (staleAge: number) => number;
    /** Kill container on Redis client error if error threshold is reached
     * @default 0 (0 means no error threshold)
     */
    killContainerOnErrorThreshold?: number;
    /** Additional Redis client socket options
     * @example { tls: true, rejectUnauthorized: false }
     */
    socketOptions?: RedisClientOptions['socket'];
    /** Additional Redis client options to be passed directly to createClient
     * @example { username: 'user', password: 'pass' }
     */
    clientOptions?: Omit<RedisClientOptions, 'url' | 'database' | 'socket'>;
    /** Pluggable wire-format codec for Redis string values. Lets you plug in
     * compression (gzip/brotli), encryption, or any other custom encoding without
     * forking this package or losing the existing dedup / batch / keyspace features.
     *
     * Both `serialize` and `deserialize` may return a `Promise`, enabling
     * non-blocking async codecs (e.g. `zlib.brotliCompress`) that don't block the
     * Node.js event loop. Synchronous implementations continue to work unchanged.
     *
     * Only the main cache-entry storage path is routed through the serializer.
     * The shared-tags map and the revalidated-tags map are not. The in-memory
     * deduplication cache stores the wire-format string verbatim - its contents
     * change with the serializer, but the cache itself is not re-encoded.
     *
     * Operational note: changing the serializer (or any of its parameters such as
     * a compression level or encryption key) makes existing Redis keys unreadable.
     * Either flush the affected keys or bump `keyPrefix` before deploying.
     *
     * @default jsonCacheValueSerializer (JSON.stringify with bufferAndMapReplacer
     * so native Buffer and Map values inside a CacheEntry round-trip transparently)
     */
    valueSerializer?: CacheValueSerializer;
};
declare class RedisStringsHandler {
    private client;
    private sharedTagsMap;
    private revalidatedTagsMap;
    private inMemoryDeduplicationCache;
    private getTimeoutMs;
    private redisGet;
    private redisDeduplicationHandler;
    private deduplicatedRedisGet;
    private keyPrefix;
    private redisGetDeduplication;
    private inMemoryCachingTime;
    private defaultStaleAge;
    private estimateExpireAge;
    private killContainerOnErrorThreshold;
    private valueSerializer;
    constructor({ redisUrl, database, keyPrefix, sharedTagsKey, getTimeoutMs, revalidateTagQuerySize, avgResyncIntervalMs, redisGetDeduplication, inMemoryCachingTime, defaultStaleAge, estimateExpireAge, killContainerOnErrorThreshold, socketOptions, clientOptions, valueSerializer, }: CreateRedisStringsHandlerOptions);
    resetRequestCache(): void;
    private clientReadyCalls;
    private assertClientIsReady;
    get(key: string, ctx: {
        kind: 'APP_ROUTE' | 'APP_PAGE';
        isRoutePPREnabled: boolean;
        isFallback: boolean;
    } | {
        kind: 'FETCH';
        revalidate: number;
        fetchUrl: string;
        fetchIdx: number;
        tags: string[];
        softTags: string[];
        isFallback: boolean;
    }): Promise<CacheEntry | null>;
    set(key: string, data: {
        kind: 'APP_PAGE';
        status?: number;
        headers: {
            'x-nextjs-stale-time': string;
            'x-next-cache-tags': string;
        };
        html: string;
        rscData: Buffer;
        segmentData: unknown;
        postboned: unknown;
    } | {
        kind: 'APP_ROUTE';
        status: number;
        headers: {
            'cache-control'?: string;
            'x-nextjs-stale-time': string;
            'x-next-cache-tags': string;
        };
        body: Buffer;
    } | {
        kind: 'FETCH';
        data: {
            headers: Record<string, string>;
            body: string;
            status: number;
            url: string;
        };
        revalidate: number | false;
    }, ctx: {
        isRoutePPREnabled: boolean;
        isFallback: boolean;
        tags?: string[];
        revalidate?: number | false;
        cacheControl?: {
            revalidate: 5;
            expire: undefined;
        };
    }): Promise<void>;
    revalidateTag(tagOrTags: string | string[], ...rest: any[]): Promise<void>;
}

type NextCacheHandlerOptions = CreateRedisStringsHandlerOptions & {
    serverDistDir?: string;
};
declare class CachedHandler {
    constructor(options: NextCacheHandlerOptions);
    get(...args: Parameters<RedisStringsHandler['get']>): ReturnType<RedisStringsHandler['get']>;
    set(...args: Parameters<RedisStringsHandler['set']>): ReturnType<RedisStringsHandler['set']>;
    revalidateTag(...args: Parameters<RedisStringsHandler['revalidateTag']>): ReturnType<RedisStringsHandler['revalidateTag']>;
    resetRequestCache(...args: Parameters<RedisStringsHandler['resetRequestCache']>): ReturnType<RedisStringsHandler['resetRequestCache']>;
}

declare function bufferAndMapReviver(_: string, value: any): any;
declare function bufferAndMapReplacer(_: string, value: any): any;

interface CacheComponentsEntry {
    value: ReadableStream<Uint8Array>;
    tags: string[];
    stale: number;
    timestamp: number;
    expire: number;
    revalidate: number;
}
interface CacheComponentsHandler {
    get(cacheKey: string, softTags: string[]): Promise<CacheComponentsEntry | undefined>;
    set(cacheKey: string, pendingEntry: Promise<CacheComponentsEntry>): Promise<void>;
    refreshTags(): Promise<void>;
    getExpiration(tags: string[]): Promise<number>;
    updateTags(tags: string[], durations?: {
        expire?: number;
    }): Promise<void>;
}
type CreateCacheComponentsHandlerOptions = CreateRedisStringsHandlerOptions & {
    serverDistDir?: string;
};
declare function getRedisCacheComponentsHandler(options?: CreateCacheComponentsHandlerOptions): CacheComponentsHandler;
declare const redisCacheHandler: CacheComponentsHandler;

export { type CacheValueSerializer, type CreateRedisStringsHandlerOptions, RedisStringsHandler, bufferAndMapReplacer, bufferAndMapReviver, CachedHandler as default, getRedisCacheComponentsHandler, jsonCacheValueSerializer, redisCacheHandler };
