package datakeycache

import (
	"bytes"
	"crypto/sha512"
	"encoding/hex"

	"code.justin.tv/amzn/TwitchOLE/ole/internal/encryptioncontext"
)

// CompositeKey is a composite key
type CompositeKey interface {
	Key() string
}

// EncryptionKeyCacheCompositeKey is the composite key for the encryption key
// in cache
type EncryptionKeyCacheCompositeKey struct {
	EncryptionContext map[string]string
	AlgorithmID       string
}

// Key returns the value of the composite key
func (k EncryptionKeyCacheCompositeKey) Key() string {
	hasher := sha512.New()
	_, err := hasher.Write([]byte(k.AlgorithmID))
	if err != nil {
		return ""
	}

	ec := encryptioncontext.Serialize(k.EncryptionContext)
	_, err = hasher.Write(ec)
	if err != nil {
		return ""
	}

	return hex.EncodeToString(hasher.Sum(nil))
}

func serializeEncryptedDataKey(edk []byte, keyProviderID, keyInfo string) []byte {
	buf := bytes.NewBuffer(nil)
	buf.Grow(len(edk) + len(keyProviderID) + len(keyInfo))
	buf.Write(edk)
	buf.WriteString(keyProviderID)
	buf.WriteString(keyInfo)
	return buf.Bytes()
}

// DecryptionKeyCacheCompositeKey is the composite key for the decryption key
// in cache
type DecryptionKeyCacheCompositeKey struct {
	EncryptedDataKey  []byte
	KeyProviderID     string
	KeyInfo           string
	AlgorithmID       string
	EncryptionContext map[string]string
}

// Key returns the value of the composite key
func (k DecryptionKeyCacheCompositeKey) Key() string {
	hasher := sha512.New()

	_, err := hasher.Write([]byte(k.AlgorithmID))
	if err != nil {
		return ""
	}

	_, err = hasher.Write(serializeEncryptedDataKey(k.EncryptedDataKey, k.KeyProviderID, k.KeyInfo))
	if err != nil {
		return ""
	}

	_, err = hasher.Write(make([]byte, 64))
	if err != nil {
		return ""
	}

	_, err = hasher.Write(encryptioncontext.Serialize(k.EncryptionContext))
	if err != nil {
		return ""
	}

	return hex.EncodeToString(hasher.Sum(nil))
}
