/*!
 * Copyright (c) Microsoft Corporation and contributors. All rights reserved.
 * Licensed under the MIT License.
 */
/* eslint-disable @typescript-eslint/no-non-null-assertion */
import { strict as assert } from "node:assert";
import { MockStorage } from "@fluidframework/test-runtime-utils/internal";
import { ReferenceType } from "../ops.js";
import { SnapshotV1 } from "../snapshotV1.js";
import { TestClient } from "./testClient.js";
import { createClientsAtInitialState } from "./testClientLogger.js";
import { TestSerializer } from "./testSerializer.js";
// Reconstitutes a MergeTree client from a summary
export async function loadSnapshot(summary, options) {
    const services = MockStorage.createFromSummary(summary);
    const client2 = new TestClient(options);
    const runtime = {
        logger: client2.logger,
        clientId: "1",
    };
    const { catchupOpsP } = await client2.load(runtime, services, new TestSerializer());
    await catchupOpsP;
    return client2;
}
// Wrapper around MergeTree client that provides a convenient SharedString-like API for tests.
export class TestString {
    constructor(id, options, initialState = "") {
        this.options = options;
        this.pending = [];
        this.seq = 0;
        this.minSeq = 0;
        this.client = createClientsAtInitialState({ initialState, options }, id)[id];
        this.client.startOrUpdateCollaboration(id);
    }
    insert(pos, text, increaseMsn) {
        this.queue(this.client.insertTextLocal(pos, text, { segment: this.pending.length }), increaseMsn);
    }
    annotate(start, end, props, increaseMsn) {
        this.queue(this.client.annotateRangeLocal(start, end, props), increaseMsn);
    }
    append(text, increaseMsn) {
        this.insert(this.client.getLength(), text, increaseMsn);
    }
    insertMarker(pos, increaseMsn) {
        this.queue(this.client.insertMarkerLocal(pos, ReferenceType.Simple, {
            segment: this.pending.length,
        }), increaseMsn);
    }
    appendMarker(increaseMsn) {
        this.insertMarker(this.client.getLength(), increaseMsn);
    }
    removeRange(start, end, increaseMsn) {
        this.queue(this.client.removeRangeLocal(start, end), increaseMsn);
    }
    obliterateRange(start, end, increaseMsn) {
        this.queue(this.client.obliterateRangeLocal(start, end), increaseMsn);
    }
    // Ensures the client's text matches the `expected` string and round-trips through a snapshot
    // into a new client.  The current client is then replaced with the loaded client in the hope
    // that it will help detect corruption bugs as further ops are applied.
    async expect(expected) {
        assert.equal(this.client.getText(), expected, "MergeTree must contain the expected text prior to applying ops.");
        await this.checkSnapshot(this.options);
    }
    // Ensures the MergeTree client's contents successfully roundtrip through a snapshot.
    async checkSnapshot(options) {
        this.applyPendingOps();
        const expectedAttributionKeys = this.client.getAllAttributionSeqs();
        const summary = this.getSummary();
        const client2 = await loadSnapshot(summary, options ?? this.options);
        assert.equal(this.client.getText(), client2.getText(), "Snapshot must produce a MergeTree with the same text as the original");
        // Also check the length as weak test for non-TextSegments.
        assert.equal(this.client.getLength(), client2.getLength(), "Snapshot must produce a MergeTree with the same length as the original");
        const actualAttributionKeys = client2.getAllAttributionSeqs();
        assert.deepEqual(actualAttributionKeys, expectedAttributionKeys, "Snapshot must produce a MergeTree with identical attribution as the original");
        // Replace our client with the one loaded by the snapshot.
        this.client = client2;
    }
    getSummary() {
        const snapshot = new SnapshotV1(this.client.mergeTree, this.client.logger, (id) => this.client.getLongClientId(id));
        snapshot.extractSync();
        return snapshot.emit(TestClient.serializer, undefined).summary;
    }
    getText() {
        return this.client.getText();
    }
    applyPendingOps() {
        for (const msg of this.pending) {
            this.client.applyMsg(msg);
        }
        this.pending.splice(0, this.pending.length);
    }
    queue(op, increaseMsn) {
        const refSeq = this.seq;
        const seq = ++this.seq;
        this.pending.push(this.client.makeOpMessage(op, seq, refSeq, this.client.longClientId, (this.minSeq = increaseMsn ? seq : this.minSeq)));
    }
    getSegment(pos) {
        const { segment } = this.client.getContainingSegment(pos) ?? {};
        assert(segment !== undefined);
        return segment;
    }
}
//# sourceMappingURL=snapshot.utils.js.map