// Copyright 2018-2023 The NATS Authors
// 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
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package nkeys

import (
	
	
	
)

// PrefixByte is a lead byte representing the type.
type PrefixByte byte

const (
	// PrefixByteSeed is the version byte used for encoded NATS Seeds
	PrefixByteSeed PrefixByte = 18 << 3 // Base32-encodes to 'S...'

	// PrefixBytePrivate is the version byte used for encoded NATS Private keys
	PrefixBytePrivate PrefixByte = 15 << 3 // Base32-encodes to 'P...'

	// PrefixByteServer is the version byte used for encoded NATS Servers
	PrefixByteServer PrefixByte = 13 << 3 // Base32-encodes to 'N...'

	// PrefixByteCluster is the version byte used for encoded NATS Clusters
	PrefixByteCluster PrefixByte = 2 << 3 // Base32-encodes to 'C...'

	// PrefixByteOperator is the version byte used for encoded NATS Operators
	PrefixByteOperator PrefixByte = 14 << 3 // Base32-encodes to 'O...'

	// PrefixByteAccount is the version byte used for encoded NATS Accounts
	PrefixByteAccount PrefixByte = 0 // Base32-encodes to 'A...'

	// PrefixByteUser is the version byte used for encoded NATS Users
	PrefixByteUser PrefixByte = 20 << 3 // Base32-encodes to 'U...'

	// PrefixByteCurve is the version byte used for encoded CurveKeys (X25519)
	PrefixByteCurve PrefixByte = 23 << 3 // Base32-encodes to 'X...'

	// PrefixByteUnknown is for unknown prefixes.
	PrefixByteUnknown PrefixByte = 25 << 3 // Base32-encodes to 'Z...'
)

// Set our encoding to not include padding '=='
var b32Enc = base32.StdEncoding.WithPadding(base32.NoPadding)

// Encode will encode a raw key or seed with the prefix and crc16 and then base32 encoded.
func ( PrefixByte,  []byte) ([]byte, error) {
	if  := checkValidPrefixByte();  != nil {
		return nil, 
	}

	var  bytes.Buffer

	// write prefix byte
	if  := .WriteByte(byte());  != nil {
		return nil, 
	}

	// write payload
	if ,  := .Write();  != nil {
		return nil, 
	}

	// Calculate and write crc16 checksum
	 := binary.Write(&, binary.LittleEndian, crc16(.Bytes()))
	if  != nil {
		return nil, 
	}

	 := .Bytes()
	 := make([]byte, b32Enc.EncodedLen(len()))
	b32Enc.Encode(, )
	return [:], nil
}

// EncodeSeed will encode a raw key with the prefix and then seed prefix and crc16 and then base32 encoded.
// `src` must be 32 bytes long (ed25519.SeedSize).
func ( PrefixByte,  []byte) ([]byte, error) {
	if  := checkValidPublicPrefixByte();  != nil {
		return nil, 
	}

	if len() != seedLen {
		return nil, ErrInvalidSeedLen
	}

	// In order to make this human printable for both bytes, we need to do a little
	// bit manipulation to setup for base32 encoding which takes 5 bits at a time.
	 := byte(PrefixByteSeed) | (byte() >> 5)
	 := (byte() & 31) << 3 // 31 = 00011111

	var  bytes.Buffer

	.WriteByte()
	.WriteByte()

	// write payload
	if ,  := .Write();  != nil {
		return nil, 
	}

	// Calculate and write crc16 checksum
	 := binary.Write(&, binary.LittleEndian, crc16(.Bytes()))
	if  != nil {
		return nil, 
	}

	 := .Bytes()
	 := make([]byte, b32Enc.EncodedLen(len()))
	b32Enc.Encode(, )
	return , nil
}

// IsValidEncoding will tell you if the encoding is a valid key.
func ( []byte) bool {
	,  := decode()
	return  == nil
}

// decode will decode the base32 and check crc16 and the prefix for validity.
func decode( []byte) ([]byte, error) {
	 := make([]byte, b32Enc.DecodedLen(len()))
	,  := b32Enc.Decode(, )
	if  != nil {
		return nil, 
	}
	 = [:]

	if  < 4 {
		return nil, ErrInvalidEncoding
	}

	 := binary.LittleEndian.Uint16([-2:])

	// ensure checksum is valid
	if  := validate([0:-2], );  != nil {
		return nil, 
	}

	return [:-2], nil
}

// Decode will decode the base32 string and check crc16 and enforce the prefix is what is expected.
func ( PrefixByte,  []byte) ([]byte, error) {
	if  := checkValidPrefixByte();  != nil {
		return nil, 
	}
	,  := decode()
	if  != nil {
		return nil, 
	}
	 := [0] & 248 // 248 = 11111000
	if  := PrefixByte();  !=  {
		return nil, ErrInvalidPrefixByte
	}
	return [1:], nil
}

// DecodeSeed will decode the base32 string and check crc16 and enforce the prefix is a seed
// and the subsequent type is a valid type.
func ( []byte) (PrefixByte, []byte, error) {
	,  := decode()
	if  != nil {
		return PrefixByteSeed, nil, 
	}
	// Need to do the reverse here to get back to internal representation.
	 := [0] & 248                          // 248 = 11111000
	 := ([0]&7)<<5 | (([1] & 248) >> 3) // 7 = 00000111

	if PrefixByte() != PrefixByteSeed {
		return PrefixByteSeed, nil, ErrInvalidSeed
	}
	if checkValidPublicPrefixByte(PrefixByte()) != nil {
		return PrefixByteSeed, nil, ErrInvalidSeed
	}
	return PrefixByte(), [2:], nil
}

// Prefix returns PrefixBytes of its input
func ( string) PrefixByte {
	,  := decode([]byte())
	if  != nil {
		return PrefixByteUnknown
	}
	 := PrefixByte([0])
	 = checkValidPrefixByte()
	if  == nil {
		return 
	}
	// Might be a seed.
	 := [0] & 248
	if PrefixByte() == PrefixByteSeed {
		return PrefixByteSeed
	}
	return PrefixByteUnknown
}

// IsValidPublicKey will decode and verify that the string is a valid encoded public key.
func ( string) bool {
	,  := decode([]byte())
	if  != nil {
		return false
	}
	if  := PrefixByte([0]); checkValidPublicPrefixByte() != nil {
		return false
	}
	return true
}

// IsValidPublicUserKey will decode and verify the string is a valid encoded Public User Key.
func ( string) bool {
	,  := Decode(PrefixByteUser, []byte())
	return  == nil
}

// IsValidPublicAccountKey will decode and verify the string is a valid encoded Public Account Key.
func ( string) bool {
	,  := Decode(PrefixByteAccount, []byte())
	return  == nil
}

// IsValidPublicServerKey will decode and verify the string is a valid encoded Public Server Key.
func ( string) bool {
	,  := Decode(PrefixByteServer, []byte())
	return  == nil
}

// IsValidPublicClusterKey will decode and verify the string is a valid encoded Public Cluster Key.
func ( string) bool {
	,  := Decode(PrefixByteCluster, []byte())
	return  == nil
}

// IsValidPublicOperatorKey will decode and verify the string is a valid encoded Public Operator Key.
func ( string) bool {
	,  := Decode(PrefixByteOperator, []byte())
	return  == nil
}

// IsValidPublicCurveKey will decode and verify the string is a valid encoded Public Curve Key.
func ( string) bool {
	,  := Decode(PrefixByteCurve, []byte())
	return  == nil
}

// checkValidPrefixByte returns an error if the provided value
// is not one of the defined valid prefix byte constants.
func checkValidPrefixByte( PrefixByte) error {
	switch  {
	case PrefixByteOperator, PrefixByteServer, PrefixByteCluster,
		PrefixByteAccount, PrefixByteUser, PrefixByteSeed, PrefixBytePrivate, PrefixByteCurve:
		return nil
	}
	return ErrInvalidPrefixByte
}

// checkValidPublicPrefixByte returns an error if the provided value
// is not one of the public defined valid prefix byte constants.
func checkValidPublicPrefixByte( PrefixByte) error {
	switch  {
	case PrefixByteOperator, PrefixByteServer, PrefixByteCluster, PrefixByteAccount, PrefixByteUser, PrefixByteCurve:
		return nil
	}
	return ErrInvalidPrefixByte
}

func ( PrefixByte) () string {
	switch  {
	case PrefixByteOperator:
		return "operator"
	case PrefixByteServer:
		return "server"
	case PrefixByteCluster:
		return "cluster"
	case PrefixByteAccount:
		return "account"
	case PrefixByteUser:
		return "user"
	case PrefixByteSeed:
		return "seed"
	case PrefixBytePrivate:
		return "private"
	case PrefixByteCurve:
		return "x25519"
	}
	return "unknown"
}

// CompatibleKeyPair returns an error if the KeyPair doesn't match expected PrefixByte(s)
func ( KeyPair,  ...PrefixByte) error {
	,  := .PublicKey()
	if  != nil {
		return 
	}
	 := Prefix()
	for ,  := range  {
		if  ==  {
			return nil
		}
	}

	return ErrIncompatibleKey
}