/**
 *    Copyright (C) 2021-present MongoDB, Inc.
 *
 *    This program is free software: you can redistribute it and/or modify
 *    it under the terms of the Server Side Public License, version 1,
 *    as published by MongoDB, Inc.
 *
 *    This program is distributed in the hope that it will be useful,
 *    but WITHOUT ANY WARRANTY; without even the implied warranty of
 *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *    Server Side Public License for more details.
 *
 *    You should have received a copy of the Server Side Public License
 *    along with this program. If not, see
 *    <http://www.mongodb.com/licensing/server-side-public-license>.
 *
 *    As a special exception, the copyright holders give permission to link the
 *    code of portions of this program with the OpenSSL library under certain
 *    conditions as described in each individual source file and distribute
 *    linked combinations including the program with the OpenSSL library. You
 *    must comply with the Server Side Public License in all respects for
 *    all of the code used other than as permitted herein. If you modify file(s)
 *    with this exception, you may extend this exception to your version of the
 *    file(s), but you are not obligated to do so. If you do not wish to do so,
 *    delete this exception statement from your version. If you delete this
 *    exception statement from all source files in the program, then also delete
 *    it in the license file.
 */

#pragma once

#include "mongo/db/matcher/expression.h"

namespace mongo {
namespace change_stream_filter {
/**
 * Produce a timestamp filter that starts an oplog scan at a specific time.
 */
std::unique_ptr<MatchExpression> buildTsFilter(
    const boost::intrusive_ptr<ExpressionContext>& expCtx,
    Timestamp startFromInclusive,
    const MatchExpression* userMatch);

/**
 * Produce a filter that rejects any operations marked with the "fromMigrate" flag. These operations
 * occurs as part of chunk migration and should not be visible to user change streams, because they
 * don't reflect user operations to the database.
 */
std::unique_ptr<MatchExpression> buildNotFromMigrateFilter(
    const boost::intrusive_ptr<ExpressionContext>& expCtx, const MatchExpression* userMatch);

/**
 * Produce an oplog filter which captures all operations relevant to change streams, including CRUD
 * events and certain commands. Depending on the 'userMatch' $match expression, this filter may be
 * able to reject some entries before they get transformed into change stream entries.
 */
std::unique_ptr<MatchExpression> buildOperationFilter(
    const boost::intrusive_ptr<ExpressionContext>& expCtx, const MatchExpression* userMatch);

/**
 * Produce an oplog filter which matches any operation that should cause the change stream to be
 * invalidated. The filter generated by this function will differ depending on the type of change
 * stream being constructed.
 */
std::unique_ptr<MatchExpression> buildInvalidationFilter(
    const boost::intrusive_ptr<ExpressionContext>& expCtx, const MatchExpression* userMatch);

/**
 * Produce a filter matching any 'applyOps' or 'commitTransaction' commands that commit a
 * transaction. An 'applyOps' command implicitly commits a transaction if _both_ of the following
 * are true: 1) it is not marked with the 'partialTxn' field, which would indicate that there are
 * more entries to come in the transaction and 2) it is not marked with the 'prepare' field, which
 * would indicate that the transaction is only committed if there is a follow-up 'commitTransaction'
 * command in the oplog.
 *
 * This filter will ignore all but the last 'applyOps' command in a transaction comprising multiple
 * 'applyOps' commands, and it will ignore all 'applyOps' commands in a prepared transaction. The
 * change stream traverses back through the oplog to recover the ignored commands when it sees an
 * entry that commits a transaction.
 *
 * As an optimization, this filter also ignores any transaction with just a single 'applyOps' if
 * that 'applyOps' does not contain any updates that modify the namespace that the change stream is
 * watching.
 */
std::unique_ptr<MatchExpression> buildTransactionFilter(
    const boost::intrusive_ptr<ExpressionContext>& expCtx, const MatchExpression* userMatch);

/**
 * Produce a filter matching any operations for internal change stream use. These events must not be
 * filtered out by the change stream's oplog scan, even if they are not ultimately returned to the
 * user.
 */
std::unique_ptr<MatchExpression> buildInternalOpFilter(
    const boost::intrusive_ptr<ExpressionContext>& expCtx, const MatchExpression* userMatch);
}  // namespace change_stream_filter
}  // namespace mongo
