/**
 * Tests passing API parameters into transaction-continuing commands.
 *
 * @tags: [
 *   # The test runs commands that are not allowed with security token: endSession.
 *   not_allowed_with_signed_security_token,
 *   uses_api_parameters,
 *   uses_transactions,
 * ]
 */

import {withRetryOnTransientTxnError} from "jstests/libs/auto_retry_transaction_in_sharding.js";

const dbName = jsTestName();
const collName = "test";

const testDB = db.getSiblingDB(dbName);
const testColl = testDB.getCollection(collName);

testColl.drop({writeConcern: {w: "majority"}});
assert.commandWorked(
    testDB.runCommand({create: testColl.getName(), writeConcern: {w: "majority"}}));

const apiParamCombos = [
    {},
    {apiVersion: "1"},
    {apiVersion: "1", apiDeprecationErrors: true},
    {apiVersion: "1", apiDeprecationErrors: false},
    {apiVersion: "1", apiStrict: true},
    {apiVersion: "1", apiStrict: true, apiDeprecationErrors: true},
    {apiVersion: "1", apiStrict: true, apiDeprecationErrors: false},
    {apiVersion: "1", apiStrict: false},
    {apiVersion: "1", apiStrict: false, apiDeprecationErrors: true},
    {apiVersion: "1", apiStrict: false, apiDeprecationErrors: false}
];

function addApiParams(obj, params) {
    return Object.assign(Object.assign({}, obj), params);
}

for (const txnInitiatingParams of apiParamCombos) {
    for (const txnContinuingParams of apiParamCombos) {
        for (const txnEndingCmdName of ["commitTransaction", "abortTransaction"]) {
            const compatibleParams = (txnContinuingParams === txnInitiatingParams);
            const session = db.getMongo().startSession();
            const sessionDb = session.getDatabase(dbName);

            function checkCommand(db, command) {
                const commandWithParams = addApiParams(command, txnContinuingParams);
                jsTestLog(`Session ${session.getSessionId().id}, ` +
                          `initial params: ${tojson(txnInitiatingParams)}, ` +
                          `continuing params: ${tojson(txnContinuingParams)}, ` +
                          `compatible: ${tojson(compatibleParams)}`);
                jsTestLog(`Command: ${tojson(commandWithParams)}`);
                const reply = db.runCommand(commandWithParams);
                jsTestLog(`Reply: ${tojson(reply)}`);

                if (compatibleParams) {
                    assert.commandWorked(reply);
                } else {
                    assert.commandFailedWithCode(reply, ErrorCodes.APIMismatchError);
                }
            }

            withRetryOnTransientTxnError(
                () => {
                    session.startTransaction();
                    assert.commandWorked(sessionDb.runCommand(addApiParams(
                        {insert: collName, documents: [{}, {}, {}]}, txnInitiatingParams)));

                    /*
                     * Check "insert" with API params in a transaction.
                     */
                    checkCommand(sessionDb, {insert: collName, documents: [{}]});
                },
                () => {
                    session.abortTransaction_forTesting();
                });

            /*
             * Check "commitTransaction" or "abortTransaction".
             */
            let txnEndingCmd = {};
            txnEndingCmd[txnEndingCmdName] = 1;
            Object.assign(txnEndingCmd,
                          {txnNumber: session.getTxnNumber_forTesting(), autocommit: false});

            checkCommand(session.getDatabase("admin"), txnEndingCmd);

            if (!compatibleParams) {
                jsTestLog('Cleaning up');
                // Clean up by calling abortTransaction with the right API parameters.
                const abortCmd = {
                    abortTransaction: 1,
                    txnNumber: session.getTxnNumber_forTesting(),
                    autocommit: false
                };
                const cleanupReply = session.getDatabase("admin").runCommand(
                    addApiParams(abortCmd, txnInitiatingParams));
                jsTestLog(`Cleanup reply ${tojson(cleanupReply)}`);
            }

            session.endSession();
        }
    }
}
