/*!
 * Copyright (c) Microsoft Corporation and contributors. All rights reserved.
 * Licensed under the MIT License.
 */
import { strict as assert } from "node:assert";
import fs from "node:fs";
import { UnassignedSequenceNumber } from "../constants.js";
import { walkAllChildSegments } from "../mergeTreeNodeWalk.js";
import { Marker } from "../mergeTreeNodes.js";
import { PartialSequenceLengths, verifyExpectedPartialLengths, verifyPartialLengths, } from "../partialLengths.js";
import * as info from "../segmentInfos.js";
import { TextSegment } from "../textSegment.js";
import { loadText } from "./text.js";
export function loadTextFromFile(filename, mergeTree, segLimit = 0) {
    const content = fs.readFileSync(filename, "utf8");
    return loadText(content, mergeTree, segLimit);
}
export function loadTextFromFileWithMarkers(filename, mergeTree, segLimit = 0) {
    const content = fs.readFileSync(filename, "utf8");
    return loadText(content, mergeTree, segLimit, true);
}
export function insertMarker({ mergeTree, pos, refSeq, clientId, seq, behaviors, props, opArgs, }) {
    mergeTree.insertSegments(pos, [Marker.make(behaviors, props)], refSeq, clientId, seq, opArgs);
}
export function insertText({ mergeTree, pos, refSeq, clientId, seq, text, props, opArgs, }) {
    mergeTree.insertSegments(pos, [TextSegment.make(text, props)], refSeq, clientId, seq, opArgs);
}
export function insertSegments({ mergeTree, pos, segments, refSeq, clientId, seq, opArgs, }) {
    mergeTree.insertSegments(pos, segments, refSeq, clientId, seq, opArgs);
}
export function markRangeRemoved({ mergeTree, start, end, refSeq, clientId, seq, opArgs, }) {
    mergeTree.markRangeRemoved(start, end, refSeq, clientId, seq, opArgs);
}
export function obliterateRange({ mergeTree, start, end, refSeq, clientId, seq, opArgs, }) {
    mergeTree.obliterateRange(start, end, refSeq, clientId, seq, opArgs);
}
export function nodeOrdinalsHaveIntegrity(block) {
    const olen = block.ordinal.length;
    for (let i = 0; i < block.childCount; i++) {
        if (block.children[i].ordinal) {
            if (olen !== block.children[i].ordinal.length - 1) {
                console.log("node integrity issue");
                return false;
            }
            if (i > 0 && block.children[i].ordinal <= block.children[i - 1].ordinal) {
                console.log("node sib integrity issue");
                return false;
            }
            if (!block.children[i].isLeaf()) {
                return nodeOrdinalsHaveIntegrity(block.children[i]);
            }
        }
        else {
            console.log(`node child ordinal not set ${i}`);
            return false;
        }
    }
    return true;
}
/**
 * Returns an object that tallies each delta and maintenance operation observed
 * for the given 'mergeTree'.
 */
export function countOperations(mergeTree) {
    const counts = {};
    assert.strictEqual(mergeTree.mergeTreeDeltaCallback, undefined);
    assert.strictEqual(mergeTree.mergeTreeMaintenanceCallback, undefined);
    const fn = (deltaArgs) => {
        const previous = counts[deltaArgs.operation];
        counts[deltaArgs.operation] = previous === undefined ? 1 : previous + 1;
    };
    mergeTree.mergeTreeDeltaCallback = (opArgs, deltaArgs) => {
        fn(deltaArgs);
    };
    mergeTree.mergeTreeMaintenanceCallback = fn;
    return counts;
}
function getPartialLengths(clientId, seq, mergeTree, localSeq, mergeBlock = mergeTree.root) {
    const partialLen = mergeBlock.partialLengths?.getPartialLength(seq, clientId, localSeq);
    let actualLen = 0;
    const isInserted = (segment) => info.isInserted(segment) &&
        ((segment.seq !== UnassignedSequenceNumber && segment.seq <= seq) ||
            (localSeq !== undefined &&
                segment.seq === UnassignedSequenceNumber &&
                segment.localSeq !== undefined &&
                segment.localSeq <= localSeq));
    const isRemoved = (segment) => info.isRemoved(segment) &&
        ((localSeq !== undefined &&
            segment.removedSeq === UnassignedSequenceNumber &&
            segment.localRemovedSeq !== undefined &&
            segment.localRemovedSeq <= localSeq) ||
            (segment.removedSeq !== UnassignedSequenceNumber && segment.removedSeq <= seq));
    const isMoved = (segment) => info.isMoved(segment) &&
        ((localSeq !== undefined &&
            segment.movedSeq === UnassignedSequenceNumber &&
            segment.localMovedSeq !== undefined &&
            segment.localMovedSeq <= localSeq) ||
            (segment.movedSeq !== UnassignedSequenceNumber && segment.movedSeq <= seq));
    walkAllChildSegments(mergeBlock, (segment) => {
        if (isInserted(segment) && !isRemoved(segment) && !isMoved(segment)) {
            actualLen += segment.cachedLength;
        }
        return true;
    });
    return {
        partialLen,
        actualLen,
    };
}
export function validatePartialLengths(clientId, mergeTree, expectedValues, localSeq, mergeBlock = mergeTree.root) {
    mergeTree.computeLocalPartials(0);
    for (let i = mergeTree.collabWindow.minSeq + 1; i <= mergeTree.collabWindow.currentSeq; i++) {
        const { partialLen, actualLen } = getPartialLengths(clientId, i, mergeTree, localSeq, mergeBlock);
        if (partialLen && partialLen < 0) {
            assert.fail("Negative partial length returned");
        }
        assert.equal(partialLen, actualLen);
    }
    if (!expectedValues) {
        return;
    }
    for (const { seq, len, localSeq: expectedLocalSeq } of expectedValues) {
        const { partialLen, actualLen } = getPartialLengths(clientId, seq, mergeTree, expectedLocalSeq ?? localSeq, mergeBlock);
        assert.equal(partialLen, len);
        assert.equal(actualLen, len);
    }
}
export function validateRefCount(collection) {
    if (!collection) {
        return;
    }
    const expectedLength = [...collection].length;
    // eslint-disable-next-line @typescript-eslint/dot-notation
    assert.equal(collection["refCount"], expectedLength);
}
/**
 * Enable stricter partial length assertions inside tests
 *
 * Note that these assertions can be expensive, and so should not be enabled in
 * production code or tests that run through thousands of ops (e.g. the SharedString
 * fuzz tests).
 */
export function useStrictPartialLengthChecks() {
    beforeEach("Enable strict partial lengths", () => {
        PartialSequenceLengths.options.verifier = verifyPartialLengths;
        PartialSequenceLengths.options.verifyExpected = verifyExpectedPartialLengths;
    });
    afterEach("Disable strict partial lengths", () => {
        PartialSequenceLengths.options.verifier = undefined;
        PartialSequenceLengths.options.verifyExpected = undefined;
    });
}
//# sourceMappingURL=testUtils.js.map