/**
 * Performs concurrent majority writes alongside transactions to verify both will eventually
 * complete as expected.
 *
 * The base workload assumes transactions run at snapshot read concern, so the tag is copied here.
 * @tags: [uses_transactions, assumes_snapshot_transactions]
 */

import {extendWorkload} from "jstests/concurrency/fsm_libs/extend_workload.js";
import {isMongos} from "jstests/concurrency/fsm_workload_helpers/server_types.js";
import {
    $config as $baseConfig
} from
    "jstests/concurrency/fsm_workloads/txns/multi_statement_transaction/multi_statement_transaction_simple.js";

export const $config = extendWorkload($baseConfig, function($config, $super) {
    $config.data.majorityWriteCollName = 'majority_writes';
    $config.data.counter = 0;

    /**
     * Runs the base workload's init state function and inserts a document to be majority updated
     * later by this thread.
     */
    $config.states.init = function init(db, collName) {
        $super.states.init.apply(this, arguments);

        assert.commandWorked(db[this.majorityWriteCollName].insert(
            {_id: this.tid, counter: this.counter}, {writeConcern: {w: 'majority'}}));
    };

    /**
     * Updates a document unrelated to the transaction run in the base workload using majority write
     * concern and verifies the update is immediately visible in the majority snapshot once the
     * write returns.
     */
    $config.states.majorityWriteUnrelatedDoc = function majorityWriteUnrelatedDoc(db, collName) {
        this.counter += 1;
        assert.commandWorked(db[this.majorityWriteCollName].update(
            {_id: this.tid}, {$set: {counter: this.counter}}, {writeConcern: {w: 'majority'}}));

        // As soon as the write returns, its effects should be visible in the majority snapshot.
        const doc = db[this.majorityWriteCollName].findOne({_id: this.tid});
        assert.eq(this.counter, doc.counter, 'unexpected counter value, doc: ' + tojson(doc));
    };

    /**
     * Updates a document that may be written to by the transaction run in the base workload using
     * majority write concern and verifies the update is immediately visible in the majority
     * snapshot once the write returns.
     */
    $config.states.majorityWriteTxnDoc = function majorityWriteTxnDoc(db, collName) {
        this.counter += 1;

        // Choose a random document that may be written to by the base workload. The base collection
        // contains documents with _id ranging from 0 to the number of accounts. Update a field
        // based on the thread's id, since threads may concurrently write to the same document.
        const transactionDocId = Random.randInt(this.numAccounts);
        const threadUniqueField = 'thread' + this.tid;
        assert.commandWorked(db[collName].update({_id: transactionDocId},
                                                 {$set: {[threadUniqueField]: this.counter}},
                                                 {writeConcern: {w: 'majority'}}));

        // As soon as the write returns, its effects should be visible in the majority snapshot.
        const doc = db[collName].findOne({_id: transactionDocId});
        assert.eq(
            this.counter,
            doc[threadUniqueField],
            'unexpected thread unique field value, thread: ' + this.tid + ', doc: ' + tojson(doc));
    };

    /**
     * Runs the base workload's setup and, if necessary, shards the collection that is majority
     * written to by this workload.
     */
    $config.setup = function setup(db, collName, cluster) {
        $super.setup.apply(this, arguments);

        if (isMongos(db)) {
            // The database will already have had sharding enabled by the fsm infrastructure.
            db.adminCommand({
                shardCollection: db[this.majorityWriteCollName].getFullName(),
                key: {_id: 'hashed'}
            });
        }
    };

    $config.transitions = {
        init: {transferMoney: 1},
        transferMoney: {
            transferMoney: 0.5,
            checkMoneyBalance: 0.1,
            majorityWriteUnrelatedDoc: 0.2,
            majorityWriteTxnDoc: 0.2
        },
        checkMoneyBalance:
            {transferMoney: 0.5, majorityWriteUnrelatedDoc: 0.25, majorityWriteTxnDoc: 0.25},
        majorityWriteUnrelatedDoc:
            {transferMoney: 0.5, majorityWriteUnrelatedDoc: 0.25, majorityWriteTxnDoc: 0.25},
        majorityWriteTxnDoc:
            {transferMoney: 0.5, majorityWriteUnrelatedDoc: 0.25, majorityWriteTxnDoc: 0.25},
    };

    return $config;
});
