"use strict";
/*!
 * Copyright (c) Microsoft Corporation and contributors. All rights reserved.
 * Licensed under the MIT License.
 */
Object.defineProperty(exports, "__esModule", { value: true });
exports.runTransaction = exports.treeApi = void 0;
const internal_1 = require("@fluidframework/core-utils/internal");
const internal_2 = require("@fluidframework/telemetry-utils/internal");
const index_js_1 = require("../feature-libraries/index.js");
const index_js_2 = require("../simple-tree/index.js");
const schematizingTreeView_js_1 = require("./schematizingTreeView.js");
const checkoutFlexTreeView_js_1 = require("./checkoutFlexTreeView.js");
const transactionTypes_js_1 = require("./transactionTypes.js");
/**
 * The `Tree` object holds various functions for interacting with {@link TreeNode}s.
 * @public
 */
exports.treeApi = {
    ...index_js_2.treeNodeApi,
    runTransaction: createRunTransaction(),
    contains(parent, child) {
        let toCheck = child;
        while (toCheck !== undefined) {
            if (toCheck === parent) {
                return true;
            }
            toCheck = exports.treeApi.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: transactionTypes_js_1.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.
 */
function runTransaction(treeOrNode, transaction, preconditions = []) {
    if (treeOrNode instanceof schematizingTreeView_js_1.SchematizingSimpleTreeView) {
        const t = transaction;
        return runTransactionInCheckout(treeOrNode.checkout, () => t(treeOrNode.root), preconditions);
    }
    else {
        const node = treeOrNode;
        const t = transaction;
        const context = (0, index_js_2.getOrCreateInnerNode)(node).context;
        if (context.isHydrated() === false) {
            throw new internal_2.UsageError("Transactions cannot be run on Unhydrated nodes. Transactions apply to a TreeView and Unhydrated nodes are not part of a TreeView.");
        }
        const treeView = (0, checkoutFlexTreeView_js_1.getCheckoutFlexTreeView)(context);
        return runTransactionInCheckout(treeView.checkout, () => t(node), preconditions);
    }
}
exports.runTransaction = runTransaction;
function runTransactionInCheckout(checkout, transaction, preconditions) {
    checkout.transaction.start();
    for (const constraint of preconditions) {
        switch (constraint.type) {
            case "nodeInDocument": {
                const node = (0, index_js_2.getOrCreateInnerNode)(constraint.node);
                const nodeStatus = exports.treeApi.status(constraint.node);
                if (nodeStatus !== index_js_1.TreeStatus.InDocument) {
                    throw new internal_2.UsageError(`Attempted to add a "nodeInDocument" constraint, but the node is not currently in the document. Node status: ${nodeStatus}`);
                }
                checkout.editor.addNodeExistsConstraint(node.anchorNode);
                break;
            }
            default:
                (0, internal_1.unreachableCase)(constraint.type);
        }
    }
    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.
        checkout.transaction.abort();
        throw error;
    }
    if (result === transactionTypes_js_1.rollback) {
        checkout.transaction.abort();
    }
    else {
        checkout.transaction.commit();
    }
    return result;
}
//# sourceMappingURL=treeApi.js.map