/**
 * Tests running the delete command on a time-series collection with concurrent modifications to the
 * collection.
 * @tags: [
 *   assumes_no_implicit_collection_creation_after_drop,
 *   does_not_support_stepdowns,
 *   does_not_support_transactions,
 *   requires_fcv_51,
 *   requires_getmore,
 *   # $currentOp can't run with a readConcern other than 'local'.
 *   assumes_read_concern_unchanged,
 *   # This test only synchronizes deletes on the primary.
 *   assumes_read_preference_unchanged,
 *   # Fail points in this test do not exist on mongos.
 *   assumes_against_mongod_not_mongos,
 *   uses_parallel_shell,
 * ]
 */

(function() {
"use strict";

load("jstests/core/timeseries/libs/timeseries.js");
load("jstests/libs/curop_helpers.js");
load('jstests/libs/parallel_shell_helpers.js');

if (!TimeseriesTest.timeseriesUpdatesAndDeletesEnabled(db.getMongo())) {
    jsTestLog("Skipping test because the time-series updates and deletes feature flag is disabled");
    return;
}

const timeFieldName = "time";
const metaFieldName = "tag";
const collName = 't';
const dbName = jsTestName();

const testCases = {
    DROP_COLLECTION: 0,
    REPLACE_COLLECTION: 1,
    REPLACE_METAFIELD: 2
};

const validateDeleteIndex =
    (docsToInsert, deleteQuery, expectedErrorCode, testCase, newMetaField = null) => {
        const testDB = db.getSiblingDB(dbName);
        const awaitTestDelete = startParallelShell(funWithArgs(
            function(docsToInsert,
                     deleteQuery,
                     timeFieldName,
                     metaFieldName,
                     collName,
                     expectedErrorCode,
                     dbName) {
                const testDB = db.getSiblingDB(dbName);
                const coll = testDB.getCollection(collName);

                assert.commandWorked(testDB.createCollection(
                    coll.getName(),
                    {timeseries: {timeField: timeFieldName, metaField: metaFieldName}}));

                assert.commandWorked(coll.insert(docsToInsert));

                assert.commandWorked(testDB.adminCommand(
                    {configureFailPoint: "hangDuringBatchRemove", mode: "alwaysOn"}));
                assert.commandFailedWithCode(
                    testDB.runCommand({delete: coll.getName(), deletes: deleteQuery}),
                    expectedErrorCode);

                coll.drop();
            },
            docsToInsert,
            deleteQuery,
            timeFieldName,
            metaFieldName,
            collName,
            expectedErrorCode,
            dbName));
        const coll = testDB.getCollection(collName);

        const childOp =
            waitForCurOpByFailPoint(testDB, coll.getFullName(), "hangDuringBatchRemove")[0];

        // Drop the collection in all test cases.
        assert(coll.drop());
        switch (testCase) {
            case testCases.REPLACE_COLLECTION:
                assert.commandWorked(testDB.createCollection(coll.getName()));
                break;
            case testCases.REPLACE_METAFIELD:
                assert.commandWorked(testDB.createCollection(
                    coll.getName(),
                    {timeseries: {timeField: timeFieldName, metaField: newMetaField}}));
                break;
        }

        assert.commandWorked(
            testDB.adminCommand({configureFailPoint: "hangDuringBatchRemove", mode: "off"}));

        // Wait for testDelete to finish.
        awaitTestDelete();
    };

const objA = {
    [timeFieldName]: ISODate(),
    "measurement": {"A": "cpu"},
    [metaFieldName]: {a: "A"}
};

// Attempt to delete from a collection that has been dropped.
validateDeleteIndex([objA],
                    [{q: {[metaFieldName]: {a: "A"}}, limit: 0}],
                    ErrorCodes.NamespaceNotFound,
                    testCases.DROP_COLLECTION);

// Attempt to delete from a collection that has been replaced with a non-time-series collection.
validateDeleteIndex([objA],
                    [{q: {[metaFieldName]: {a: "A"}}, limit: 0}],
                    ErrorCodes.NamespaceNotFound,
                    testCases.REPLACE_COLLECTION);

// Attempt to delete from a collection that has been replaced with a new time-series collection with
// a different metaField.
validateDeleteIndex([objA],
                    [{q: {[metaFieldName]: {a: "A"}}, limit: 0}],
                    ErrorCodes.InvalidOptions,
                    testCases.REPLACE_METAFIELD,
                    "meta");
})();
