"use strict";
/*!
 * Copyright (c) Microsoft Corporation and contributors. All rights reserved.
 * Licensed under the MIT License.
 */
Object.defineProperty(exports, "__esModule", { value: true });
const node_assert_1 = require("node:assert");
const stochastic_test_utils_1 = require("@fluid-private/stochastic-test-utils");
const mergeTreeTracking_js_1 = require("../mergeTreeTracking.js");
const ops_js_1 = require("../ops.js");
const segmentInfos_js_1 = require("../segmentInfos.js");
const sortedSegmentSet_js_1 = require("../sortedSegmentSet.js");
const testClient_js_1 = require("./testClient.js");
const segmentCount = 15;
function validateSorted(set, getOrdinal, prefix) {
    for (let i = 0; i < set.size - 1; i++) {
        const a = getOrdinal(set.items[i]);
        const b = getOrdinal(set.items[i + 1]);
        (0, node_assert_1.strict)(a !== undefined, `${prefix}: Undefined ordinal ${i}`);
        (0, node_assert_1.strict)(b !== undefined, `${prefix}: Undefined ordinal  ${i + 1}`);
        (0, node_assert_1.strict)(a <= b, `${prefix}: Not sorted at item ${i}`);
    }
}
function validateSet(client, set, getOrdinal) {
    validateSorted(set, getOrdinal, "initial");
    // add content to shift ordinals in tree
    for (let i = 0; i < segmentCount * 5; i++) {
        client.insertTextLocal((i * 3) % client.getLength(), `X`);
    }
    validateSorted(set, getOrdinal, "after insert");
    for (let i = set.size; set.size > 0; i += set.size) {
        // jump around the list a bit, so its not just an in-order remove
        const item = set.items[i % set.size];
        node_assert_1.strict.equal(set.remove(item), true, "remove failed");
        node_assert_1.strict.equal(set.has(item), false);
        validateSorted(set, getOrdinal, "during remove");
    }
}
describe("SortedSegmentSet", () => {
    const localUserLongId = "localUser";
    let client;
    beforeEach(() => {
        client = new testClient_js_1.TestClient();
        for (let i = 0; i < segmentCount; i++) {
            client.insertTextLocal(client.getLength(), i.toString()[0].repeat(i + 1));
        }
        client.startOrUpdateCollaboration(localUserLongId);
    });
    it("SortedSegmentSet of objects with segments", () => {
        const set = new sortedSegmentSet_js_1.SortedSegmentSet();
        for (let i = 0; i < client.getLength(); i++) {
            for (const pos of [i, client.getLength() - 1 - i]) {
                const segment = client.getContainingSegment(pos)?.segment;
                (0, node_assert_1.strict)(segment);
                const item = { segment };
                node_assert_1.strict.equal(set.has(item), false);
                set.addOrUpdate(item);
                node_assert_1.strict.equal(set.has(item), true);
            }
        }
        node_assert_1.strict.equal(set.size, client.getLength() * 2);
        validateSet(client, set, (i) => (0, segmentInfos_js_1.toMergeNodeInfo)(i.segment)?.ordinal);
    });
    it("SortedSegmentSet of segments", () => {
        const set = new sortedSegmentSet_js_1.SortedSegmentSet();
        for (let i = 0; i < client.getLength(); i++) {
            for (const pos of [i, client.getLength() - 1 - i]) {
                const segment = client.getContainingSegment(pos)?.segment;
                (0, node_assert_1.strict)(segment);
                set.addOrUpdate(segment);
                node_assert_1.strict.equal(set.has(segment), true);
            }
        }
        node_assert_1.strict.equal(set.size, segmentCount);
        validateSet(client, set, (i) => (0, segmentInfos_js_1.toMergeNodeInfo)(i)?.ordinal);
    });
    describe("SortedSegmentSet of local references", () => {
        it("Inserts in order", () => {
            const random = (0, stochastic_test_utils_1.makeRandom)(0);
            const refsAtAllPositions = [];
            for (let i = 0; i < client.getLength(); i++) {
                const { segment, offset } = client.getContainingSegment(i) ?? {};
                (0, node_assert_1.strict)(segment !== undefined);
                (0, node_assert_1.strict)(offset !== undefined);
                refsAtAllPositions.push(client.createLocalReferencePosition(segment, offset, ops_js_1.ReferenceType.SlideOnRemove, undefined));
            }
            random.shuffle(refsAtAllPositions);
            const segmentSet = new sortedSegmentSet_js_1.SortedSegmentSet();
            for (const ref of refsAtAllPositions) {
                segmentSet.addOrUpdate(ref);
            }
            // Validate that the set is sorted
            const refsBackToPositions = segmentSet.items.map((ref) => client.localReferencePositionToPosition(ref));
            node_assert_1.strict.deepEqual(refsBackToPositions, Array.from({ length: client.getLength() }, (_, i) => i));
        });
    });
    it("SortedSegmentSet of local references", () => {
        // using a sorted segment set directly creates problems,
        // as we don't correctly split, or merge, so leverage
        // the tracking group for correct behavior in those case
        // and spy it's internal set
        const set = new mergeTreeTracking_js_1.TrackingGroup();
        for (let i = 0; i < client.getLength(); i++) {
            for (const pos of [i, client.getLength() - 1 - i]) {
                const segmentInfo = client.getContainingSegment(pos);
                (0, node_assert_1.strict)(segmentInfo?.segment);
                const lref = client.createLocalReferencePosition(segmentInfo.segment, segmentInfo.offset, ops_js_1.ReferenceType.SlideOnRemove, undefined);
                node_assert_1.strict.equal(set.has(lref), false);
                set.link(lref);
                node_assert_1.strict.equal(set.has(lref), true);
            }
        }
        node_assert_1.strict.equal(set.size, client.getLength() * 2);
        validateSet(client, 
        // Cast to any because we are validating a set of local references, but the instantiated type of trackedSet
        // on TrackingGroup is SortedSegmentSet<Trackable>.
        // eslint-disable-next-line @typescript-eslint/no-unsafe-argument, @typescript-eslint/no-unsafe-member-access, @typescript-eslint/no-explicit-any
        set.trackedSet, (i) => (0, segmentInfos_js_1.toMergeNodeInfo)(i.getSegment())?.ordinal);
    });
});
//# sourceMappingURL=sortedSegmentSet.spec.js.map