"use strict";
/*!
 * Copyright (c) Microsoft Corporation and contributors. All rights reserved.
 * Licensed under the MIT License.
 */
Object.defineProperty(exports, "__esModule", { value: true });
exports.PartialSyncTestHelper = void 0;
const node_assert_1 = require("node:assert");
const testClientLogger_js_1 = require("./testClientLogger.js");
const ClientIds = ["A", "B", "C", "D"];
/**
 * Like `ReconnectHelper`, but:
 * - does not support reconnecting clients
 * - supports advancing only some clients to a given sequence number (not all clients must be synchronized at the same time).
 *
 * This allows testing sequences of operations where clients have varying refSeqs, rather than having all clients advance refSeq
 * in lockstep.
 */
class PartialSyncTestHelper {
    idxFromName(name) {
        return (name.codePointAt(0) ?? 0) - ("A".codePointAt(0) ?? 0);
    }
    constructor(options = {}) {
        this.ops = [];
        this.clientToLastAppliedSeq = new Map();
        this.seq = 0;
        this.clients = (0, testClientLogger_js_1.createClientsAtInitialState)({
            initialState: "",
            options: {
                mergeTreeEnableObliterate: true,
                mergeTreeEnableSidedObliterate: true,
                ...options,
            },
        }, ...ClientIds);
        this.logger = new testClientLogger_js_1.TestClientLogger(this.clients.all);
        this.perClientOps = this.clients.all.map(() => []);
    }
    addMessage(message) {
        this.ops.push(message);
        // This implementation (specifically, that of applying ops / synchronizing clients) assumes messages
        // are pushed sequentially starting with seq 1.
        (0, node_assert_1.strict)(message.sequenceNumber === this.ops.length, "Partial sync test helper invariant violated");
    }
    insertText(clientName, pos, text) {
        const client = this.clients[clientName];
        this.addMessage(client.makeOpMessage(client.insertTextLocal(pos, text), ++this.seq));
    }
    removeRange(clientName, start, end) {
        const client = this.clients[clientName];
        this.addMessage(client.makeOpMessage(client.removeRangeLocal(start, end), ++this.seq));
    }
    obliterateRange(clientName, start, end) {
        const client = this.clients[clientName];
        this.addMessage(client.makeOpMessage(
        // TODO: remove type assertions when sidedness is enabled
        client.obliterateRangeLocal(start, end), ++this.seq));
    }
    advanceClientToSeq(clientName, seq) {
        const client = this.clients[clientName];
        const lastApplied = this.clientToLastAppliedSeq.get(clientName);
        (0, node_assert_1.strict)(seq > 0, "Can only advance clients to sequence numbers that exist");
        (0, node_assert_1.strict)(this.ops.length >= seq, "Cannot attempt to advance clients to sequence numbers that don't yet exist");
        let startIndex;
        if (lastApplied === undefined) {
            startIndex = 0;
        }
        else {
            if (lastApplied >= seq) {
                return;
            }
            startIndex = lastApplied;
        }
        for (let i = startIndex; i < seq; i++) {
            const nextMessage = this.ops[i];
            client.applyMsg(nextMessage);
            this.clientToLastAppliedSeq.set(clientName, nextMessage.sequenceNumber);
        }
    }
    /**
     * Sends all known ops to the procieded client ids.
     */
    advanceClients(...clientNames) {
        const latestSeq = this.ops[this.ops.length - 1].sequenceNumber;
        for (const name of clientNames) {
            this.advanceClientToSeq(name, latestSeq);
        }
    }
    processAllOps() {
        const latestSeq = this.ops[this.ops.length - 1].sequenceNumber;
        for (const name of ClientIds) {
            this.advanceClientToSeq(name, latestSeq);
        }
    }
}
exports.PartialSyncTestHelper = PartialSyncTestHelper;
//# sourceMappingURL=partialSyncHelper.js.map