/*!
 * Copyright (c) Microsoft Corporation and contributors. All rights reserved.
 * Licensed under the MIT License.
 */
import { bufferToString } from "@fluid-internal/client-utils";
import { assert } from "@fluidframework/core-utils/internal";
import { createSingleBlobSummary } from "@fluidframework/shared-object-base/internal";
import { applyDelta, forEachField, makeDetachedFieldIndex, } from "../../core/index.js";
import { idAllocatorFromMaxId } from "../../util/index.js";
import { chunkFieldSingle, defaultChunkPolicy } from "../chunked-forest/index.js";
import { makeForestSummarizerCodec } from "./codec.js";
/**
 * The storage key for the blob in the summary containing tree data
 */
export const treeBlobKey = "ForestTree";
/**
 * Provides methods for summarizing and loading a forest.
 */
export class ForestSummarizer {
    /**
     * @param encoderContext - The schema if provided here must be mutated by the caller to keep it up to date.
     */
    constructor(forest, revisionTagCodec, fieldBatchCodec, encoderContext, options, idCompressor) {
        this.forest = forest;
        this.revisionTagCodec = revisionTagCodec;
        this.encoderContext = encoderContext;
        this.idCompressor = idCompressor;
        this.key = "Forest";
        // TODO: this should take in CodecWriteOptions, and use it to pick the write version.
        this.codec = makeForestSummarizerCodec(options, fieldBatchCodec);
    }
    /**
     * Synchronous monolithic summarization of tree content.
     *
     * TODO: when perf matters, this should be replaced with a chunked async version using a binary format.
     *
     * @returns a snapshot of the forest's tree as a string.
     */
    getTreeString(stringify) {
        const rootCursor = this.forest.getCursorAboveDetachedFields();
        const fieldMap = new Map();
        // TODO: Encode all detached fields in one operation for better performance and compression
        forEachField(rootCursor, (cursor) => {
            const key = cursor.getFieldKey();
            const innerCursor = this.forest.allocateCursor("getTreeString");
            assert(this.forest.tryMoveCursorToField({ fieldKey: key, parent: undefined }, innerCursor) ===
                1 /* TreeNavigationResult.Ok */, 0x892 /* failed to navigate to field */);
            fieldMap.set(key, innerCursor);
        });
        const encoded = this.codec.encode(fieldMap, this.encoderContext);
        fieldMap.forEach((value) => value.free());
        return stringify(encoded);
    }
    summarize(props) {
        return createSingleBlobSummary(treeBlobKey, this.getTreeString(props.stringify));
    }
    async load(services, parse) {
        if (await services.contains(treeBlobKey)) {
            const treeBuffer = await services.readBlob(treeBlobKey);
            const treeBufferString = bufferToString(treeBuffer, "utf8");
            // TODO: this code is parsing data without an optional validator, this should be defined in a typebox schema as part of the
            // forest summary format.
            const fields = this.codec.decode(parse(treeBufferString), this.encoderContext);
            const allocator = idAllocatorFromMaxId();
            const fieldChanges = [];
            const build = [];
            for (const [fieldKey, field] of fields) {
                const chunked = chunkFieldSingle(field, {
                    policy: defaultChunkPolicy,
                    idCompressor: this.idCompressor,
                });
                const buildId = { minor: allocator.allocate(chunked.topLevelLength) };
                build.push({
                    id: buildId,
                    trees: chunked,
                });
                fieldChanges.push([fieldKey, [{ count: chunked.topLevelLength, attach: buildId }]]);
            }
            assert(this.forest.isEmpty, 0x797 /* forest must be empty */);
            applyDelta({ build, fields: new Map(fieldChanges) }, undefined, this.forest, makeDetachedFieldIndex("init", this.revisionTagCodec, this.idCompressor));
        }
    }
}
//# sourceMappingURL=forestSummarizer.js.map