/*!
 * Copyright (c) Microsoft Corporation and contributors. All rights reserved.
 * Licensed under the MIT License.
 */
import { UsageError } from "@fluidframework/telemetry-utils/internal";
import { assert } from "@fluidframework/core-utils/internal";
import { getOrCreateInnerNode, treeNodeApi, rollback, } from "../simple-tree/index.js";
import { addConstraintsToTransaction, SchematizingSimpleTreeView, } from "./schematizingTreeView.js";
import { Context } from "../feature-libraries/index.js";
/**
 * The {@link (Tree:interface)} singleton which holds various functions for interacting with {@link TreeNode}s.
 * @public
 */
export const Tree = {
    ...treeNodeApi,
    runTransaction: createRunTransaction(),
    contains(parent, child) {
        let toCheck = child;
        while (toCheck !== undefined) {
            if (toCheck === parent) {
                return true;
            }
            toCheck = Tree.parent(toCheck);
        }
        return false;
    },
};
// TODO: Add more constraint types here
/** Creates a copy of `runTransaction` with the `rollback` property added so as to satisfy the `RunTransaction` interface. */
function createRunTransaction() {
    /** A type-safe helper to add a "rollback" property (as required by the `RunTransaction` interface) to a given object */
    function defineRollbackProperty(target) {
        Reflect.defineProperty(target, "rollback", { value: rollback });
        return target;
    }
    return defineRollbackProperty(runTransaction.bind({}));
}
/**
 * Run the given transaction.
 * @remarks
 * This API is not publicly exported but is exported outside of this module so that test code may unit test the `Tree.runTransaction` function directly without being restricted to its public API overloads.
 * @deprecated This API catches exceptions then tries to modify the tree before rethrowing: this is not robust. Use {@link TreeViewAlpha.runTransaction} instead which does not try to edit content in the error case.
 */
export function runTransaction(treeOrNode, transaction, preconditions = []) {
    if (treeOrNode instanceof SchematizingSimpleTreeView) {
        const t = transaction;
        return runTransactionInCheckout(treeOrNode.checkout, () => t(treeOrNode.root), preconditions);
    }
    else {
        const node = treeOrNode;
        const t = transaction;
        const context = getOrCreateInnerNode(node).context;
        if (context.isHydrated() === false) {
            throw new UsageError("Transactions cannot be run on Unhydrated nodes. Transactions apply to a TreeView and Unhydrated nodes are not part of a TreeView.");
        }
        assert(context instanceof Context, 0xbe3 /* Expected context to be a Context instance. */);
        return runTransactionInCheckout(context.checkout, () => t(node), preconditions);
    }
}
/**
 * Run the given transaction.
 * @deprecated This API catches exceptions then tries to modify the tree before rethrowing: this is not robust. Use {@link TreeViewAlpha.runTransaction} instead which does not try to editing content in the error case.
 */
function runTransactionInCheckout(checkout, transaction, preconditions) {
    checkout.transaction.start();
    addConstraintsToTransaction(checkout, false, preconditions);
    let result;
    try {
        result = transaction();
    }
    catch (error) {
        // If the transaction has an unhandled error, abort and rollback the transaction but continue to propagate the error.
        // This might try and modify the tree or trigger events while things are in an inconsistent state.
        // It is up to the user of runTransaction to ensure that does not cause problems (and they have no robust way to do that, which is why its deprecated).
        checkout.transaction.abort();
        throw error;
    }
    if (result === rollback) {
        checkout.transaction.abort();
    }
    else {
        checkout.transaction.commit();
    }
    return result;
}
//# sourceMappingURL=tree.js.map