// SPDX-License-Identifier: MIT
// SPDX-FileCopyrightText: 2022 mochi-co
// SPDX-FileContributor: mochi-co

package packets

// Code contains a reason code and reason string for a response.
type Code struct {
	Reason string
	Code   byte
}

// String returns the readable reason for a code.
func (c Code) String() string {
	return c.Reason
}

// Error returns the readable reason for a code.
func (c Code) Error() string {
	return c.Reason
}

var (
	// QosCodes indicicates the reason codes for each Qos byte.
	QosCodes = map[byte]Code{
		0: CodeGrantedQos0,
		1: CodeGrantedQos1,
		2: CodeGrantedQos2,
	}

	CodeSuccess                               = Code{Code: 0x00, Reason: "success"}
	CodeDisconnect                            = Code{Code: 0x00, Reason: "disconnected"}
	CodeGrantedQos0                           = Code{Code: 0x00, Reason: "granted qos 0"}
	CodeGrantedQos1                           = Code{Code: 0x01, Reason: "granted qos 1"}
	CodeGrantedQos2                           = Code{Code: 0x02, Reason: "granted qos 2"}
	CodeDisconnectWillMessage                 = Code{Code: 0x04, Reason: "disconnect with will message"}
	CodeNoMatchingSubscribers                 = Code{Code: 0x10, Reason: "no matching subscribers"}
	CodeNoSubscriptionExisted                 = Code{Code: 0x11, Reason: "no subscription existed"}
	CodeContinueAuthentication                = Code{Code: 0x18, Reason: "continue authentication"}
	CodeReAuthenticate                        = Code{Code: 0x19, Reason: "re-authenticate"}
	ErrUnspecifiedError                       = Code{Code: 0x80, Reason: "unspecified error"}
	ErrMalformedPacket                        = Code{Code: 0x81, Reason: "malformed packet"}
	ErrMalformedProtocolName                  = Code{Code: 0x81, Reason: "malformed packet: protocol name"}
	ErrMalformedProtocolVersion               = Code{Code: 0x81, Reason: "malformed packet: protocol version"}
	ErrMalformedFlags                         = Code{Code: 0x81, Reason: "malformed packet: flags"}
	ErrMalformedKeepalive                     = Code{Code: 0x81, Reason: "malformed packet: keepalive"}
	ErrMalformedPacketID                      = Code{Code: 0x81, Reason: "malformed packet: packet identifier"}
	ErrMalformedTopic                         = Code{Code: 0x81, Reason: "malformed packet: topic"}
	ErrMalformedWillTopic                     = Code{Code: 0x81, Reason: "malformed packet: will topic"}
	ErrMalformedWillPayload                   = Code{Code: 0x81, Reason: "malformed packet: will message"}
	ErrMalformedUsername                      = Code{Code: 0x81, Reason: "malformed packet: username"}
	ErrMalformedPassword                      = Code{Code: 0x81, Reason: "malformed packet: password"}
	ErrMalformedQos                           = Code{Code: 0x81, Reason: "malformed packet: qos"}
	ErrMalformedOffsetUintOutOfRange          = Code{Code: 0x81, Reason: "malformed packet: offset uint out of range"}
	ErrMalformedOffsetBytesOutOfRange         = Code{Code: 0x81, Reason: "malformed packet: offset bytes out of range"}
	ErrMalformedOffsetByteOutOfRange          = Code{Code: 0x81, Reason: "malformed packet: offset byte out of range"}
	ErrMalformedOffsetBoolOutOfRange          = Code{Code: 0x81, Reason: "malformed packet: offset boolean out of range"}
	ErrMalformedInvalidUTF8                   = Code{Code: 0x81, Reason: "malformed packet: invalid utf-8 string"}
	ErrMalformedVariableByteInteger           = Code{Code: 0x81, Reason: "malformed packet: variable byte integer out of range"}
	ErrMalformedBadProperty                   = Code{Code: 0x81, Reason: "malformed packet: unknown property"}
	ErrMalformedProperties                    = Code{Code: 0x81, Reason: "malformed packet: properties"}
	ErrMalformedWillProperties                = Code{Code: 0x81, Reason: "malformed packet: will properties"}
	ErrMalformedSessionPresent                = Code{Code: 0x81, Reason: "malformed packet: session present"}
	ErrMalformedReasonCode                    = Code{Code: 0x81, Reason: "malformed packet: reason code"}
	ErrProtocolViolation                      = Code{Code: 0x82, Reason: "protocol violation"}
	ErrProtocolViolationProtocolName          = Code{Code: 0x82, Reason: "protocol violation: protocol name"}
	ErrProtocolViolationProtocolVersion       = Code{Code: 0x82, Reason: "protocol violation: protocol version"}
	ErrProtocolViolationReservedBit           = Code{Code: 0x82, Reason: "protocol violation: reserved bit not 0"}
	ErrProtocolViolationFlagNoUsername        = Code{Code: 0x82, Reason: "protocol violation: username flag set but no value"}
	ErrProtocolViolationFlagNoPassword        = Code{Code: 0x82, Reason: "protocol violation: password flag set but no value"}
	ErrProtocolViolationUsernameNoFlag        = Code{Code: 0x82, Reason: "protocol violation: username set but no flag"}
	ErrProtocolViolationPasswordNoFlag        = Code{Code: 0x82, Reason: "protocol violation: username set but no flag"}
	ErrProtocolViolationPasswordTooLong       = Code{Code: 0x82, Reason: "protocol violation: password too long"}
	ErrProtocolViolationUsernameTooLong       = Code{Code: 0x82, Reason: "protocol violation: username too long"}
	ErrProtocolViolationNoPacketID            = Code{Code: 0x82, Reason: "protocol violation: missing packet id"}
	ErrProtocolViolationSurplusPacketID       = Code{Code: 0x82, Reason: "protocol violation: surplus packet id"}
	ErrProtocolViolationQosOutOfRange         = Code{Code: 0x82, Reason: "protocol violation: qos out of range"}
	ErrProtocolViolationSecondConnect         = Code{Code: 0x82, Reason: "protocol violation: second connect packet"}
	ErrProtocolViolationZeroNonZeroExpiry     = Code{Code: 0x82, Reason: "protocol violation: non-zero expiry"}
	ErrProtocolViolationRequireFirstConnect   = Code{Code: 0x82, Reason: "protocol violation: first packet must be connect"}
	ErrProtocolViolationWillFlagNoPayload     = Code{Code: 0x82, Reason: "protocol violation: will flag no payload"}
	ErrProtocolViolationWillFlagSurplusRetain = Code{Code: 0x82, Reason: "protocol violation: will flag surplus retain"}
	ErrProtocolViolationSurplusWildcard       = Code{Code: 0x82, Reason: "protocol violation: topic contains wildcards"}
	ErrProtocolViolationSurplusSubID          = Code{Code: 0x82, Reason: "protocol violation: contained subscription identifier"}
	ErrProtocolViolationInvalidTopic          = Code{Code: 0x82, Reason: "protocol violation: invalid topic"}
	ErrProtocolViolationInvalidSharedNoLocal  = Code{Code: 0x82, Reason: "protocol violation: invalid shared no local"}
	ErrProtocolViolationNoFilters             = Code{Code: 0x82, Reason: "protocol violation: must contain at least one filter"}
	ErrProtocolViolationInvalidReason         = Code{Code: 0x82, Reason: "protocol violation: invalid reason"}
	ErrProtocolViolationOversizeSubID         = Code{Code: 0x82, Reason: "protocol violation: oversize subscription id"}
	ErrProtocolViolationDupNoQos              = Code{Code: 0x82, Reason: "protocol violation: dup true with no qos"}
	ErrProtocolViolationUnsupportedProperty   = Code{Code: 0x82, Reason: "protocol violation: unsupported property"}
	ErrProtocolViolationNoTopic               = Code{Code: 0x82, Reason: "protocol violation: no topic or alias"}
	ErrImplementationSpecificError            = Code{Code: 0x83, Reason: "implementation specific error"}
	ErrRejectPacket                           = Code{Code: 0x83, Reason: "packet rejected"}
	ErrUnsupportedProtocolVersion             = Code{Code: 0x84, Reason: "unsupported protocol version"}
	ErrClientIdentifierNotValid               = Code{Code: 0x85, Reason: "client identifier not valid"}
	ErrClientIdentifierTooLong                = Code{Code: 0x85, Reason: "client identifier too long"}
	ErrBadUsernameOrPassword                  = Code{Code: 0x86, Reason: "bad username or password"}
	ErrNotAuthorized                          = Code{Code: 0x87, Reason: "not authorized"}
	ErrServerUnavailable                      = Code{Code: 0x88, Reason: "server unavailable"}
	ErrServerBusy                             = Code{Code: 0x89, Reason: "server busy"}
	ErrBanned                                 = Code{Code: 0x8A, Reason: "banned"}
	ErrServerShuttingDown                     = Code{Code: 0x8B, Reason: "server shutting down"}
	ErrBadAuthenticationMethod                = Code{Code: 0x8C, Reason: "bad authentication method"}
	ErrKeepAliveTimeout                       = Code{Code: 0x8D, Reason: "keep alive timeout"}
	ErrSessionTakenOver                       = Code{Code: 0x8E, Reason: "session takeover"}
	ErrTopicFilterInvalid                     = Code{Code: 0x8F, Reason: "topic filter invalid"}
	ErrTopicNameInvalid                       = Code{Code: 0x90, Reason: "topic name invalid"}
	ErrPacketIdentifierInUse                  = Code{Code: 0x91, Reason: "packet identifier in use"}
	ErrPacketIdentifierNotFound               = Code{Code: 0x92, Reason: "packet identifier not found"}
	ErrReceiveMaximum                         = Code{Code: 0x93, Reason: "receive maximum exceeded"}
	ErrTopicAliasInvalid                      = Code{Code: 0x94, Reason: "topic alias invalid"}
	ErrPacketTooLarge                         = Code{Code: 0x95, Reason: "packet too large"}
	ErrMessageRateTooHigh                     = Code{Code: 0x96, Reason: "message rate too high"}
	ErrQuotaExceeded                          = Code{Code: 0x97, Reason: "quota exceeded"}
	ErrPendingClientWritesExceeded            = Code{Code: 0x97, Reason: "too many pending writes"}
	ErrAdministrativeAction                   = Code{Code: 0x98, Reason: "administrative action"}
	ErrPayloadFormatInvalid                   = Code{Code: 0x99, Reason: "payload format invalid"}
	ErrRetainNotSupported                     = Code{Code: 0x9A, Reason: "retain not supported"}
	ErrQosNotSupported                        = Code{Code: 0x9B, Reason: "qos not supported"}
	ErrUseAnotherServer                       = Code{Code: 0x9C, Reason: "use another server"}
	ErrServerMoved                            = Code{Code: 0x9D, Reason: "server moved"}
	ErrSharedSubscriptionsNotSupported        = Code{Code: 0x9E, Reason: "shared subscriptiptions not supported"}
	ErrConnectionRateExceeded                 = Code{Code: 0x9F, Reason: "connection rate exceeded"}
	ErrMaxConnectTime                         = Code{Code: 0xA0, Reason: "maximum connect time"}
	ErrSubscriptionIdentifiersNotSupported    = Code{Code: 0xA1, Reason: "subscription identifiers not supported"}
	ErrWildcardSubscriptionsNotSupported      = Code{Code: 0xA2, Reason: "wildcard subscriptions not supported"}

	// MQTTv3 specific bytes.
	Err3UnsupportedProtocolVersion = Code{Code: 0x01}
	Err3ClientIdentifierNotValid   = Code{Code: 0x02}
	Err3ServerUnavailable          = Code{Code: 0x03}
	ErrMalformedUsernameOrPassword = Code{Code: 0x04}
	Err3NotAuthorized              = Code{Code: 0x05}

	// V5CodesToV3 maps MQTTv5 Connack reason codes to MQTTv3 return codes.
	// This is required because MQTTv3 has different return byte specification.
	// See http://docs.oasis-open.org/mqtt/mqtt/v3.1.1/os/mqtt-v3.1.1-os.html#_Toc385349257
	V5CodesToV3 = map[Code]Code{
		ErrUnsupportedProtocolVersion: Err3UnsupportedProtocolVersion,
		ErrClientIdentifierNotValid:   Err3ClientIdentifierNotValid,
		ErrServerUnavailable:          Err3ServerUnavailable,
		ErrMalformedUsername:          ErrMalformedUsernameOrPassword,
		ErrMalformedPassword:          ErrMalformedUsernameOrPassword,
		ErrBadUsernameOrPassword:      Err3NotAuthorized,
	}
)
