package message

import (
	"strconv"

	"github.com/milvus-io/milvus/pkg/v3/mlog"
	"github.com/milvus-io/milvus/pkg/v3/proto/messagespb"
)

type MessageType messagespb.MessageType

type MessageTypeProperties struct {
	// The log level of the message, only used for logging, info by default.
	LogLevel mlog.Level
	// A system message type is used to determine the commit behavior, used to achieve the consistency (timetick confirmed, txn) of the message.
	IsSystem bool
	// A self controlled message type is generated by the wal-system itself, not by the client.
	SelfControlled bool
	// An exclusive required message type should be appended exclusively at related vchannel(pchannel),
	ExclusiveRequired bool
	// a cipher enabled message type will be encrypted before appending to the wal if cipher is enabled.
	CipherEnabled bool
	// A message type belong to some data operation, such as insert, delete, upsert, which may create a huge overhead if not limited.
	DMLMessageType bool
}

var messageTypePropertiesMap = map[MessageType]MessageTypeProperties{
	MessageTypeTimeTick: {
		LogLevel:       mlog.DebugLevel,
		IsSystem:       true,
		SelfControlled: true,
	},
	MessageTypeInsert: {
		LogLevel:       mlog.DebugLevel,
		CipherEnabled:  true,
		DMLMessageType: true,
	},
	MessageTypeDelete: {
		LogLevel:       mlog.DebugLevel,
		CipherEnabled:  true,
		DMLMessageType: true,
	},
	MessageTypeCreateCollection: {
		ExclusiveRequired: true,
	},
	MessageTypeDropCollection: {
		ExclusiveRequired: true,
	},
	MessageTypeTruncateCollection: {
		ExclusiveRequired: true,
	},
	MessageTypeCreatePartition: {
		ExclusiveRequired: true,
	},
	MessageTypeDropPartition: {
		ExclusiveRequired: true,
	},
	MessageTypeImport: {},
	MessageTypeCommitImport: {
		ExclusiveRequired: true,
	},
	MessageTypeRollbackImport: {
		ExclusiveRequired: true,
	},
	MessageTypeBatchUpdateManifest: {},
	MessageTypeCreateSegment: {
		SelfControlled: true,
	},
	MessageTypeFlush: {
		SelfControlled: true,
	},
	MessageTypeManualFlush: {
		ExclusiveRequired: true,
	},
	MessageTypeAlterReplicateConfig: {
		ExclusiveRequired: true,
	},
	MessageTypeBeginTxn: {
		LogLevel: mlog.DebugLevel,
		IsSystem: true,
	},
	MessageTypeCommitTxn: {
		LogLevel: mlog.DebugLevel,
		IsSystem: true,
	},
	MessageTypeRollbackTxn: {
		LogLevel: mlog.DebugLevel,
		IsSystem: true,
	},
	MessageTypeTxn: {
		LogLevel: mlog.DebugLevel,
		IsSystem: true,
	},
	MessageTypeSchemaChange: {
		ExclusiveRequired: true,
	},
	MessageTypeAlterCollection: {
		ExclusiveRequired: true,
	},
	MessageTypeAlterLoadConfig:     {},
	MessageTypeDropLoadConfig:      {},
	MessageTypeCreateDatabase:      {},
	MessageTypeAlterDatabase:       {},
	MessageTypeDropDatabase:        {},
	MessageTypeAlterAlias:          {},
	MessageTypeDropAlias:           {},
	MessageTypeAlterUser:           {},
	MessageTypeDropUser:            {},
	MessageTypeAlterRole:           {},
	MessageTypeDropRole:            {},
	MessageTypeAlterUserRole:       {},
	MessageTypeDropUserRole:        {},
	MessageTypeAlterPrivilege:      {},
	MessageTypeDropPrivilege:       {},
	MessageTypeAlterPrivilegeGroup: {},
	MessageTypeDropPrivilegeGroup:  {},
	MessageTypeRestoreRBAC:         {},
	MessageTypeAlterResourceGroup:  {},
	MessageTypeDropResourceGroup:   {},
	MessageTypeCreateIndex:         {},
	MessageTypeAlterIndex:          {},
	MessageTypeDropIndex:           {},
	MessageTypeFlushAll: {
		ExclusiveRequired: true,
	},
	MessageTypeAlterWAL: {
		ExclusiveRequired: true,
	},
}

// String implements fmt.Stringer interface.
func (t MessageType) String() string {
	return messagespb.MessageType_name[int32(t)]
}

// marshal marshal MessageType to string.
func (t MessageType) marshal() string {
	return strconv.FormatInt(int64(t), 10)
}

// Valid checks if the MessageType is valid.
func (t MessageType) Valid() bool {
	typ := int32(t)
	_, ok := messagespb.MessageType_name[typ]
	return t != MessageTypeUnknown && ok
}

// IsExclusiveRequired checks if the MessageType is exclusive append required.
// An exclusive required message type is that the message's timetick should keep same order with message id.
// And when the message is appending, other messages with the same vchannel cannot append concurrently.
// Collection-scoped broadcast messages apply this requirement to their data VChannel copies. Their control-channel
// copies are ordered by broadcaster resource keys so non-conflicting DDL can append concurrently.
func (t MessageType) IsExclusiveRequired() bool {
	return messageTypePropertiesMap[t].ExclusiveRequired
}

// CanEnableCipher checks if the MessageType can enable cipher.
func (t MessageType) CanEnableCipher() bool {
	return messageTypePropertiesMap[t].CipherEnabled
}

// IsSysmtem checks if the MessageType is a system type.
func (t MessageType) IsSystem() bool {
	return messageTypePropertiesMap[t].IsSystem
}

// IsSelfControlled checks if the MessageType is self controlled.
func (t MessageType) IsSelfControlled() bool {
	return messageTypePropertiesMap[t].SelfControlled
}

// IsDMLMessageType checks if the MessageType is a data operation message type.
func (t MessageType) IsDMLMessageType() bool {
	return messageTypePropertiesMap[t].DMLMessageType
}

// LogLevel returns the log level of the MessageType.
func (t MessageType) LogLevel() mlog.Level {
	return messageTypePropertiesMap[t].LogLevel
}

// unmarshalMessageType unmarshal MessageType from string.
func unmarshalMessageType(s string) MessageType {
	i, err := strconv.ParseInt(s, 10, 32)
	if err != nil {
		return MessageTypeUnknown
	}
	return MessageType(i)
}
