/*!
 * 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 { describeFuzz, makeRandom } from "@fluid-private/stochastic-test-utils";
import { annotateRange, applyMessages, doOverRange, generateClientNames, insert, removeRange, runMergeTreeOperationRunner, } from "./mergeTreeOperationRunner.js";
import { TestClient } from "./testClient.js";
function applyMessagesWithReconnect(startingSeq, messageDatas, clients, logger, random) {
    let seq = startingSeq;
    const reconnectingClientIds = clients.length > 2 && random.bool()
        ? [clients[1].longClientId, clients[2].longClientId]
        : [clients[1].longClientId];
    const reconnectClientMsgs = new Map(reconnectingClientIds.map((id) => [id, []]));
    let minSeq = 0;
    // log and apply all the ops created in the round
    while (messageDatas.length > 0) {
        const [message, sg] = messageDatas.shift();
        assert(message.clientId, "expected clientId to be defined");
        if (reconnectingClientIds.includes(message.clientId)) {
            reconnectClientMsgs.get(message.clientId).push([message.contents, sg]);
        }
        else {
            message.sequenceNumber = ++seq;
            for (const c of clients)
                c.applyMsg(message);
            minSeq = message.minimumSequenceNumber;
        }
    }
    const reconnectMsgs = [];
    for (const [clientId, messageData] of reconnectClientMsgs.entries()) {
        const client = clients.find(({ longClientId }) => longClientId === clientId);
        for (const [op, segmentGroup] of messageData) {
            const newMsg = client.makeOpMessage(client.regeneratePendingOp(op, segmentGroup));
            newMsg.minimumSequenceNumber = minSeq;
            // apply message doesn't use the segment group, so just pass undefined
            reconnectMsgs.push([newMsg, undefined]);
        }
    }
    return applyMessages(seq, reconnectMsgs, clients, logger);
}
export const defaultOptions = {
    minLength: 16,
    clients: { min: 2, max: 8 },
    opsPerRoundRange: { min: 40, max: 320 },
    rounds: 3,
    operations: [annotateRange, removeRange, insert],
    growthFunc: (input) => input * 2,
};
// Generate a list of single character client names, support up to 69 clients
const clientNames = generateClientNames();
function runReconnectFarmTests(opts, extraSeed) {
    doOverRange(opts.clients, opts.growthFunc.bind(opts), (clientCount) => {
        it(`ReconnectFarm_${clientCount}_${extraSeed ?? 0}`, async () => {
            const random = makeRandom(0xdeadbeef, 0xfeedbed, clientCount, extraSeed ?? 0);
            const testOpts = { ...opts };
            if (extraSeed) {
                testOpts.resultsFilePostfix ??= "";
                testOpts.resultsFilePostfix += extraSeed;
            }
            const clients = [new TestClient({ mergeTreeEnableAnnotateAdjust: true })];
            for (const [i, c] of clients.entries())
                c.startOrUpdateCollaboration(clientNames[i]);
            let seq = 0;
            for (const c of clients)
                c.updateMinSeq(seq);
            // Add double the number of clients each iteration
            const targetClients = Math.max(opts.clients.min, clientCount);
            for (let cc = clients.length; cc < targetClients; cc++) {
                const newClient = await TestClient.createFromClientSnapshot(clients[0], clientNames[cc]);
                clients.push(newClient);
            }
            seq = runMergeTreeOperationRunner(random, seq, clients, testOpts.minLength, testOpts, applyMessagesWithReconnect);
        }).timeout(30 * 1000);
    });
}
describeFuzz("MergeTree.Client", ({ testCount }) => {
    const opts = defaultOptions;
    if (testCount > 1) {
        doOverRange({ min: 0, max: testCount - 1 }, (x) => x + 1, (seed) => {
            describe(`with seed ${seed}`, () => {
                runReconnectFarmTests(opts, seed);
            });
        });
    }
    else {
        runReconnectFarmTests(opts);
    }
});
//# sourceMappingURL=client.reconnectFarm.spec.js.map