// Copyright (C) MongoDB, Inc. 2023-present.
//
// Licensed under the Apache License, Version 2.0 (the "License"); you may
// not use this file except in compliance with the License. You may obtain
// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0

package driverutil

import (
	"context"
	"math"
	"time"
)

// Operation Names should be sourced from the command reference documentation:
// https://www.mongodb.com/docs/manual/reference/command/
const (
	AbortTransactionOp  = "abortTransaction"  // AbortTransactionOp is the name for aborting a transaction
	AggregateOp         = "aggregate"         // AggregateOp is the name for aggregating
	CommitTransactionOp = "commitTransaction" // CommitTransactionOp is the name for committing a transaction
	CountOp             = "count"             // CountOp is the name for counting
	CreateOp            = "create"            // CreateOp is the name for creating
	CreateIndexesOp     = "createIndexes"     // CreateIndexesOp is the name for creating indexes
	DeleteOp            = "delete"            // DeleteOp is the name for deleting
	DistinctOp          = "distinct"          // DistinctOp is the name for distinct
	DropOp              = "drop"              // DropOp is the name for dropping
	DropDatabaseOp      = "dropDatabase"      // DropDatabaseOp is the name for dropping a database
	DropIndexesOp       = "dropIndexes"       // DropIndexesOp is the name for dropping indexes
	EndSessionsOp       = "endSessions"       // EndSessionsOp is the name for ending sessions
	FindAndModifyOp     = "findAndModify"     // FindAndModifyOp is the name for finding and modifying
	FindOp              = "find"              // FindOp is the name for finding
	InsertOp            = "insert"            // InsertOp is the name for inserting
	ListCollectionsOp   = "listCollections"   // ListCollectionsOp is the name for listing collections
	ListIndexesOp       = "listIndexes"       // ListIndexesOp is the name for listing indexes
	ListDatabasesOp     = "listDatabases"     // ListDatabasesOp is the name for listing databases
	UpdateOp            = "update"            // UpdateOp is the name for updating
	BulkWriteOp         = "bulkWrite"         // BulkWriteOp is the name for client-level bulk write
)

// CalculateMaxTimeMS calculates the maxTimeMS value to send to the server
// based on the context deadline and the minimum round trip time. If the
// calculated maxTimeMS is likely to cause a socket timeout, then this function
// will return 0 and false.
func CalculateMaxTimeMS(ctx context.Context, rttMin time.Duration) (int64, bool) {
	deadline, ok := ctx.Deadline()
	if !ok {
		return 0, true
	}

	remainingTimeout := time.Until(deadline)

	// Always round up to the next millisecond value so we never truncate the calculated
	// maxTimeMS value (e.g. 400 microseconds evaluates to 1ms, not 0ms).
	maxTimeMS := int64((remainingTimeout - rttMin + time.Millisecond - 1) / time.Millisecond)
	if maxTimeMS <= 0 {
		return 0, false
	}

	// The server will return a "BadValue" error if maxTimeMS is greater
	// than the maximum positive int32 value (about 24.9 days). If the
	// user specified a timeout value greater than that,  omit maxTimeMS
	// and let the client-side timeout handle cancelling the op if the
	// timeout is ever reached.
	if maxTimeMS > math.MaxInt32 {
		return 0, true
	}

	return maxTimeMS, true
}
