//go:build kvdb_etcd
// +build kvdb_etcd

package etcd

import (
	"crypto/sha256"
)

const (
	bucketIDLength = 32
)

var (
	valuePostfix   = []byte{0x00}
	bucketPostfix  = []byte{0xFF}
	sequencePrefix = []byte("$seq$")
)

// makeBucketID returns a deterministic key for the passed byte slice.
// Currently it returns the sha256 hash of the slice.
func makeBucketID(key []byte) [bucketIDLength]byte {
	return sha256.Sum256(key)
}

// isValidBucketID checks if the passed slice is the required length to be a
// valid bucket id.
func isValidBucketID(s []byte) bool {
	return len(s) == bucketIDLength
}

// makeKey concatenates parent, key and postfix into one byte slice.
// The postfix indicates the use of this key (whether bucket or value), while
// parent refers to the parent bucket.
func makeKey(parent, key, postfix []byte) []byte {
	keyBuf := make([]byte, len(parent)+len(key)+len(postfix))
	copy(keyBuf, parent)
	copy(keyBuf[len(parent):], key)
	copy(keyBuf[len(parent)+len(key):], postfix)

	return keyBuf
}

// makeBucketKey returns a bucket key from the passed parent bucket id and
// the key.
func makeBucketKey(parent []byte, key []byte) []byte {
	return makeKey(parent, key, bucketPostfix)
}

// makeValueKey returns a value key from the passed parent bucket id and
// the key.
func makeValueKey(parent []byte, key []byte) []byte {
	return makeKey(parent, key, valuePostfix)
}

// makeSequenceKey returns a sequence key of the passed parent bucket id.
func makeSequenceKey(parent []byte) []byte {
	keyBuf := make([]byte, len(sequencePrefix)+len(parent))
	copy(keyBuf, sequencePrefix)
	copy(keyBuf[len(sequencePrefix):], parent)
	return keyBuf
}

// isBucketKey returns true if the passed key is a bucket key, meaning it
// keys a bucket name.
func isBucketKey(key string) bool {
	if len(key) < bucketIDLength+1 {
		return false
	}

	return key[len(key)-1] == bucketPostfix[0]
}

// getKey chops out the key from the raw key (by removing the bucket id
// prefixing the key and the postfix indicating whether it is a bucket or
// a value key)
func getKey(rawKey string) []byte {
	return []byte(rawKey[bucketIDLength : len(rawKey)-1])
}

// getKeyVal chops out the key from the raw key (by removing the bucket id
// prefixing the key and the postfix indicating whether it is a bucket or
// a value key) and also returns the appropriate value for the key, which is
// nil in case of buckets (or the set value otherwise).
func getKeyVal(kv *KV) ([]byte, []byte) {
	var val []byte

	if !isBucketKey(kv.key) {
		val = []byte(kv.val)
	}

	return getKey(kv.key), val
}

// BucketKey is a helper function used in tests to create a bucket key from
// passed bucket list.
func BucketKey(buckets ...string) string {
	var bucketKey []byte

	rootID := makeBucketID([]byte(etcdDefaultRootBucketId))
	parent := rootID[:]

	for _, bucketName := range buckets {
		bucketKey = makeBucketKey(parent, []byte(bucketName))
		id := makeBucketID(bucketKey)
		parent = id[:]
	}

	return string(bucketKey)
}

// BucketVal is a helper function used in tests to create a bucket value (the
// value for a bucket key) from the passed bucket list.
func BucketVal(buckets ...string) string {
	id := makeBucketID([]byte(BucketKey(buckets...)))
	return string(id[:])
}

// ValueKey is a helper function used in tests to create a value key from the
// passed key and bucket list.
func ValueKey(key string, buckets ...string) string {
	rootID := makeBucketID([]byte(etcdDefaultRootBucketId))
	bucket := rootID[:]

	for _, bucketName := range buckets {
		bucketKey := makeBucketKey(bucket, []byte(bucketName))
		id := makeBucketID(bucketKey)
		bucket = id[:]
	}

	return string(makeValueKey(bucket, []byte(key)))
}

// SequenceKey is a helper function used in tests or external tools to create a
// sequence key from the passed bucket list.
func SequenceKey(buckets ...string) string {
	id := makeBucketID([]byte(BucketKey(buckets...)))
	return string(makeSequenceKey(id[:]))
}
