/*!
 * Copyright (c) Microsoft Corporation and contributors. All rights reserved.
 * Licensed under the MIT License.
 */
import { type IContainerStorageService } from "@fluidframework/container-definitions/internal";
import type { IContainerRuntime, IContainerRuntimeEvents } from "@fluidframework/container-runtime-definitions/internal";
import type { IEventProvider, IFluidHandleContext, IFluidHandleInternalPayloadPending, ILocalFluidHandle, ILocalFluidHandleEvents, Listenable, PayloadState } from "@fluidframework/core-interfaces/internal";
import type { IGarbageCollectionData, ISummaryTreeWithStats, ITelemetryContext, ISequencedMessageEnvelope } from "@fluidframework/runtime-definitions/internal";
import { FluidHandleBase } from "@fluidframework/runtime-utils/internal";
import { type IBlobManagerLoadInfo } from "./blobManagerSnapSum.js";
/**
 * This class represents blob (long string)
 * This object is used only when creating (writing) new blob and serialization purposes.
 * De-serialization process goes through FluidObjectHandle and request flow:
 * DataObject.request() recognizes requests in the form of `/blobs/<id>`
 * and loads blob.
 */
export declare class BlobHandle extends FluidHandleBase<ArrayBufferLike> implements ILocalFluidHandle<ArrayBufferLike>, IFluidHandleInternalPayloadPending<ArrayBufferLike> {
    readonly path: string;
    readonly routeContext: IFluidHandleContext;
    get: () => Promise<ArrayBufferLike>;
    readonly payloadPending: boolean;
    private readonly onAttachGraph?;
    private attached;
    get isAttached(): boolean;
    private _events;
    get events(): Listenable<ILocalFluidHandleEvents>;
    private _state;
    get payloadState(): PayloadState;
    /**
     * The error property starts undefined, signalling that there has been no error yet.
     * If an error occurs, the property will contain the error.
     */
    private _payloadShareError;
    get payloadShareError(): unknown;
    readonly absolutePath: string;
    constructor(path: string, routeContext: IFluidHandleContext, get: () => Promise<ArrayBufferLike>, payloadPending: boolean, onAttachGraph?: (() => void) | undefined);
    readonly notifyShared: () => void;
    readonly notifyFailed: (error: unknown) => void;
    attachGraph(): void;
}
export type IBlobManagerRuntime = Pick<IContainerRuntime, "attachState" | "baseLogger" | "disposed"> & IEventProvider<IContainerRuntimeEvents>;
export interface IPendingBlobs {
    [localId: string]: {
        blob: string;
        storageId?: string;
        uploadTime?: number;
        minTTLInSeconds?: number;
        acked?: boolean;
    };
}
export interface IBlobManagerEvents {
    noPendingBlobs: () => void;
}
export declare const blobManagerBasePath: "_blobs";
export declare class BlobManager {
    private readonly mc;
    private readonly publicEvents;
    get events(): Listenable<IBlobManagerEvents>;
    private readonly internalEvents;
    /**
     * Map of local IDs to storage IDs. Also includes identity mappings of storage ID to storage ID for all known
     * storage IDs. All requested IDs must be a key in this map. Blobs created while the container is detached are
     * stored in IDetachedBlobStorage which gives pseudo storage IDs; the real storage IDs are filled in at attach
     * time via setRedirectTable().
     */
    private readonly redirectTable;
    /**
     * Blobs which we have not yet seen a BlobAttach op round-trip and not yet attached to a DDS.
     */
    private readonly pendingBlobs;
    /**
     * Track ops in flight for online flow. This is used for optimizations where if we receive an ack for a storage ID,
     * we can resolve all pending blobs with the same storage ID even though they may have different local IDs. That's
     * because we know that the server will not delete the blob corresponding to that storage ID.
     */
    private readonly opsInFlight;
    private readonly sendBlobAttachOp;
    private readonly routeContext;
    private readonly storage;
    private readonly blobRequested;
    private readonly isBlobDeleted;
    private readonly runtime;
    private readonly localIdGenerator;
    private readonly createBlobPayloadPending;
    constructor(props: {
        readonly routeContext: IFluidHandleContext;
        blobManagerLoadInfo: IBlobManagerLoadInfo;
        readonly storage: Pick<IContainerStorageService, "createBlob" | "readBlob">;
        /**
         * Submit a BlobAttach op. When a blob is uploaded, there is a short grace period before which the blob is
         * deleted. The BlobAttach op notifies the server that blob is in use. The server will then not delete the
         * the blob as long as it is listed as referenced in future summaries. The summarizing client will know to
         * include the storage ID in the summary when it sees the op.
         *
         * The op will also include a local ID to inform all clients of the relation to the storage ID, without
         * knowledge of which they cannot request the blob from storage. It's important that this op is sequenced
         * before any ops that reference the local ID, otherwise, an invalid handle could be added to the document.
         */
        sendBlobAttachOp: (localId: string, storageId: string) => void;
        readonly blobRequested: (blobPath: string) => void;
        readonly isBlobDeleted: (blobPath: string) => boolean;
        readonly runtime: IBlobManagerRuntime;
        stashedBlobs: IPendingBlobs | undefined;
        readonly localIdGenerator?: (() => string) | undefined;
        readonly createBlobPayloadPending: boolean;
    });
    get allBlobsAttached(): boolean;
    get hasPendingBlobs(): boolean;
    private createAbortError;
    hasBlob(localId: string): boolean;
    /**
     * Lookup the blob storage ID for a given local blob id.
     * @param localId - The local blob id. Likely coming from a handle.
     * @returns The storage ID if found and the blob is not pending, undefined otherwise.
     * @remarks
     * For blobs with pending payloads (localId exists but upload hasn't finished), this is expected to return undefined.
     * Consumers should use the observability APIs on the handle (handle.payloadState, payloadShared event)
     * to understand/wait for storage ID availability.
     * Similarly, when the runtime is detached, this will return undefined as no blobs have been uploaded to storage.
     */
    lookupTemporaryBlobStorageId(localId: string): string | undefined;
    /**
     * Retrieve the blob with the given local blob id.
     * @param localId - The local blob id.  Likely coming from a handle.
     * @param payloadPending - Whether we suspect the payload may be pending and not available yet.
     * @returns A promise which resolves to the blob contents
     */
    getBlob(localId: string, payloadPending: boolean): Promise<ArrayBufferLike>;
    private getBlobHandle;
    private createBlobDetached;
    createBlob(blob: ArrayBufferLike, signal?: AbortSignal): Promise<IFluidHandleInternalPayloadPending<ArrayBufferLike>>;
    private createBlobLegacy;
    private createBlobWithPayloadPending;
    /**
     * Upload a blob to the storage service.
     * @returns A promise that resolves when the upload is complete and a blob attach op has been sent (but not ack'd).
     *
     * @privateRemarks This method must not reject, as there is no error handling for it in current tracking.
     */
    private uploadBlob;
    /**
     * Set up a mapping in the redirect table from fromId to toId. Also, notify the runtime that a reference is added
     * which is required for GC.
     */
    private setRedirection;
    private deletePendingBlobMaybe;
    private deletePendingBlob;
    private onUploadResolve;
    /**
     * Resubmit a BlobAttach op. Used to add storage IDs to ops that were
     * submitted to runtime while disconnected.
     * @param metadata - op metadata containing storage and/or local IDs
     */
    reSubmit(metadata: Record<string, unknown> | undefined): void;
    processBlobAttachMessage(message: ISequencedMessageEnvelope, local: boolean): void;
    summarize(telemetryContext?: ITelemetryContext): ISummaryTreeWithStats;
    /**
     * Generates data used for garbage collection. Each blob uploaded represents a node in the GC graph as it can be
     * individually referenced by storing its handle in a referenced DDS. Returns the list of blob ids as GC nodes.
     * @param fullGC - true to bypass optimizations and force full generation of GC data. BlobManager doesn't care
     * about this for now because the data is a simple list of blob ids.
     */
    getGCData(fullGC?: boolean): IGarbageCollectionData;
    /**
     * Delete attachment blobs that are sweep ready.
     * @param sweepReadyBlobRoutes - The routes of blobs that are sweep ready and should be deleted. These routes will
     * be based off of local ids.
     * @returns The routes of blobs that were deleted.
     */
    deleteSweepReadyNodes(sweepReadyBlobRoutes: readonly string[]): readonly string[];
    /**
     * Delete blobs with the given routes from the redirect table.
     *
     * @remarks
     * The routes are GC nodes paths of format -`/<blobManagerBasePath>/<localId>`.
     * Deleting the blobs involves 2 steps:
     *
     * 1. The redirect table entry for the local ids are deleted.
     *
     * 2. If the storage ids corresponding to the deleted local ids are not referenced by any further local ids, the
     * identity mappings in the redirect table are deleted as well.
     *
     * Note that this does not delete the blobs from storage service immediately. Deleting the blobs from redirect table
     * will ensure we don't create an attachment blob for them at the next summary. The service would then delete them
     * some time in the future.
     */
    private deleteBlobsFromRedirectTable;
    /**
     * Verifies that the blob with given id is not deleted, i.e., it has not been garbage collected. If the blob is GC'd,
     * log an error and throw if necessary.
     */
    private verifyBlobNotDeleted;
    /**
     * Called in detached state just prior to attaching, this will update the redirect table by
     * converting the pseudo storage IDs into real storage IDs using the provided detachedStorageTable.
     * The provided table must have exactly the same set of pseudo storage IDs as are found in the redirect table.
     * @param detachedStorageTable - A map of pseudo storage IDs to real storage IDs.
     */
    readonly patchRedirectTable: (detachedStorageTable: Map<string, string>) => void;
    /**
     * To be used in getPendingLocalState flow. Get a serializable record of the blobs that are
     * pending upload and/or their BlobAttach op, which can be given to a new BlobManager to
     * resume work.
     *
     * @privateRemarks
     * For now, we don't track any pending blobs since the getPendingBlobs flow doesn't enable
     * restoring to a state where an accessible handle has been stored by the customer (and we'll
     * just drop any BlobAttach ops on the ground during reSubmit). However, once we add support
     * for payload-pending handles, this will return the blobs associated with those handles.
     */
    getPendingBlobs(): IPendingBlobs | undefined;
    /**
     * Part of container serialization when imminent closure is enabled (Currently when calling closeAndGetPendingLocalState).
     * This asynchronous function resolves all pending createBlob calls and waits for each blob
     * to be attached. It will also send BlobAttach ops for each pending blob that hasn't sent it
     * yet so that serialized container can resubmit them when rehydrated.
     *
     * @param stopBlobAttachingSignal - Optional signal to abort the blob attaching process.
     * @returns - A promise that resolves with the details of the attached blobs,
     * or undefined if no blobs were processed.
     */
    attachAndGetPendingBlobs(stopBlobAttachingSignal?: AbortSignal): Promise<IPendingBlobs | undefined>;
}
/**
 * Returns whether a given path is for attachment blobs that are in the format - "/blobManagerBasePath/...".
 */
export declare const isBlobPath: (path: string) => path is `/_blobs/${string}`;
//# sourceMappingURL=blobManager.d.ts.map