
// contentMetadata provides functions to store and retrieve metadata about profiles and messages
// Content metadata is stored in the database, so the content can be deleted and the metadata can still be retained

package contentMetadata

// Storing content metadata in the database enables:
//	1. We do not have to calculate the ProfileIsCanonical status and the attribute hashes for the same profile more than once
//	2. Hosts/Moderators can delete banned messages and still keep the cipher key hash to verify reviews for the messages
//	3. Hosts can delete banned profiles/messages and still be aware if those profile's/message's reviews are within their range
//		Knowing if a profile's/message's reviews are within your range requires knowing the profile author/message inbox
//	4. Hosts can delete banned profiles and still be aware of why those profiles were banned (if their attributes were banned)
//		We need to keep track of the profile's attribute hashes to be able to determine the profile's verdict consensus
//	5. It is faster to retrieve metadata than to read it from the profile/message
//		This is because we do not need to read the entire message/profile into memory

// Profile metadata:
//	-Profile version
//	-Profile network type
//	-Profile identity Hash
//	-Profile creation time
//	-Profile is disabled status
//	-Profile is canonical status
//	-Profile attribute hashes map
// Message metadata:
//	-Message version
//	-Message network type
//	-Message size in bytes
//	-Message Inbox
//	-Message Cipher key hash

import "seekia/internal/encoding"
import "seekia/internal/badgerDatabase"
import "seekia/internal/profiles/readProfiles"
import "seekia/internal/messaging/readMessages"

import messagepack "github.com/vmihailenco/msgpack/v5"

import "errors"

//Outputs:
//	-bool: Profile metadata exists
//	-int: Profile Version
//	-byte: Profile network type (1 == Mainnet, 2 == Testnet1)
//	-[16]byte: Profile author identity Hash
//	-int64: Profile creation time
//	-bool: Profile is disabled
//	-bool: Profile is canonical
//	-map[int][27]byte: Map of Attribute identifier -> Attribute hash
//	-error
func GetProfileMetadata(profileHash [28]byte)(bool, int, byte, [16]byte, int64, bool, bool, map[int][27]byte, error){

	metadataExists, profileMetadata, err := badgerDatabase.GetProfileMetadata(profileHash)
	if (err != nil) { return false, 0, 0, [16]byte{}, 0, false, false, nil, err }
	if (metadataExists == true){

		type profileMetadataStruct struct{
			ProfileVersion int
			NetworkType byte
			IdentityHash [16]byte
			CreationTime int64
			IsDisabled bool
			IsCanonical bool
			AttributeHashesMap map[int][27]byte
		}

		var profileMetadataObject profileMetadataStruct

		err := encoding.DecodeMessagePackBytes(false, profileMetadata, &profileMetadataObject)
		if (err != nil) {
			return false, 0, 0, [16]byte{}, 0, false, false, nil, errors.New("Database malformed: contains invalid profile metadata: " + err.Error())
		}

		profileVersion := profileMetadataObject.ProfileVersion
		profileNetworkType := profileMetadataObject.NetworkType
		profileIdentityHash := profileMetadataObject.IdentityHash
		profileCreationTime := profileMetadataObject.CreationTime
		profileIsDisabled := profileMetadataObject.IsDisabled
		profileIsCanonical := profileMetadataObject.IsCanonical
		attributeHashesMap := profileMetadataObject.AttributeHashesMap

		if (profileIsDisabled == true){
			emptyMap := make(map[int][27]byte)
			return true, profileVersion, profileNetworkType, profileIdentityHash, profileCreationTime, true, true, emptyMap, nil
		}

		return true, profileVersion, profileNetworkType, profileIdentityHash, profileCreationTime, false, profileIsCanonical, attributeHashesMap, nil
	}

	profileType, _, err := readProfiles.ReadProfileHashMetadata(profileHash)
	if (err != nil) { return false, 0, 0, [16]byte{}, 0, false, false, nil, err }

	profileExists, profileBytes, err := badgerDatabase.GetUserProfile(profileType, profileHash)
	if (err != nil) { return false, 0, 0, [16]byte{}, 0, false, false, nil, err }
	if (profileExists == false){
		return false, 0, 0, [16]byte{}, 0, false, false, nil, nil
	}

	ableToRead, profileHash_Retrieved, profileVersion, profileNetworkType, profileIdentityHash, profileCreationTime, profileIsDisabled, rawProfileMap, err := readProfiles.ReadProfileAndHash(false, profileBytes)
	if (err != nil) { return false, 0, 0, [16]byte{}, 0, false, false, nil, err }
	if (ableToRead == false){
		return false, 0, 0, [16]byte{}, 0, false, false, nil, errors.New("Database corrupt: Contains invalid profile: " + err.Error())
	}
	if (profileHash != profileHash_Retrieved){
		return false, 0, 0, [16]byte{}, 0, false, false, nil, errors.New("Database corrupt: Profile hash does not match profile entry key")
	}

	profileAttributeHashesMap, profileIsCanonical, err := readProfiles.GetProfileAttributeHashesMap(profileIdentityHash, profileVersion, profileNetworkType, rawProfileMap)
	if (err != nil) { return false, 0, 0, [16]byte{}, 0, false, false, nil, err }

	profileVersionEncoded, err := encoding.EncodeMessagePackBytes(profileVersion)
	if (err != nil){ return false, 0, 0, [16]byte{}, 0, false, false, nil, err }

	profileNetworkTypeEncoded, err := encoding.EncodeMessagePackBytes(profileNetworkType)
	if (err != nil){ return false, 0, 0, [16]byte{}, 0, false, false, nil, err }

	identityHashEncoded, err := encoding.EncodeMessagePackBytes(profileIdentityHash)
	if (err != nil){ return false, 0, 0, [16]byte{}, 0, false, false, nil, err }

	creationTimeEncoded, err := encoding.EncodeMessagePackBytes(profileCreationTime)
	if (err != nil){ return false, 0, 0, [16]byte{}, 0, false, false, nil, err }

	isDisabledEncoded, err := encoding.EncodeMessagePackBytes(profileIsDisabled)
	if (err != nil){ return false, 0, 0, [16]byte{}, 0, false, false, nil, err }

	isCanonicalEncoded, err := encoding.EncodeMessagePackBytes(profileIsCanonical)
	if (err != nil){ return false, 0, 0, [16]byte{}, 0, false, false, nil, err }

	attributeHashesMapEncoded, err := encoding.EncodeMessagePackBytes(profileAttributeHashesMap)
	if (err != nil){ return false, 0, 0, [16]byte{}, 0, false, false, nil, err }

	profileMetadataSlice := []messagepack.RawMessage{profileVersionEncoded, profileNetworkTypeEncoded, identityHashEncoded, creationTimeEncoded, isDisabledEncoded, isCanonicalEncoded, attributeHashesMapEncoded}

	profileMetadataBytes, err := encoding.EncodeMessagePackBytes(profileMetadataSlice)
	if (err != nil) { return false, 0, 0, [16]byte{}, 0, false, false, nil, err }

	err = badgerDatabase.AddProfileMetadata(profileHash, profileMetadataBytes)
	if (err != nil) { return false, 0, 0, [16]byte{}, 0, false, false, nil, err }

	return true, profileVersion, profileNetworkType, profileIdentityHash, profileCreationTime, profileIsDisabled, profileIsCanonical, profileAttributeHashesMap, nil
}

//Outputs:
//	-bool: Message metadata exists
//	-int: Message version
//	-byte: Message network type (1 == Mainnet, 2 == Testnet1)
//	-int: Message size in bytes
//	-[10]byte: Message inbox
//	-[25]byte: Message cipher key hash
//	-error
func GetMessageMetadata(messageHash [26]byte)(bool, int, byte, int, [10]byte, [25]byte, error){

	exists, messageMetadata, err := badgerDatabase.GetMessageMetadata(messageHash)
	if (err != nil) { return false, 0, 0, 0, [10]byte{}, [25]byte{}, err }
	if (exists == true){

		type messageMetadataStruct struct{
			MessageVersion int
			MessageNetworkType byte
			MessageSize int
			MessageInbox [10]byte
			MessageCipherKeyHash [25]byte
		}

		var messageMetadataObject messageMetadataStruct

		err := encoding.DecodeMessagePackBytes(false, messageMetadata, &messageMetadataObject)
		if (err != nil) {
			return false, 0, 0, 0, [10]byte{}, [25]byte{}, errors.New("Database malformed: contains invalid message metadata: " + err.Error())
		}

		messageVersion := messageMetadataObject.MessageVersion
		messageNetworkType := messageMetadataObject.MessageNetworkType
		messageSize := messageMetadataObject.MessageSize
		messageInbox := messageMetadataObject.MessageInbox
		messageCipherKeyHash := messageMetadataObject.MessageCipherKeyHash

		return true, messageVersion, messageNetworkType, messageSize, messageInbox, messageCipherKeyHash, nil
	}

	messageExists, messageBytes, err := badgerDatabase.GetChatMessage(messageHash)
	if (err != nil) { return false, 0, 0, 0, [10]byte{}, [25]byte{}, err }
	if (messageExists == false){
		return false, 0, 0, 0, [10]byte{}, [25]byte{}, nil
	}

	ableToRead, messageHash_Retrieved, messageVersion, messageNetworkType, messageInbox, _, _, _, messageCipherKeyHash, _, _, err := readMessages.ReadChatMessagePublicDataAndHash(false, messageBytes)
	if (err != nil) { return false, 0, 0, 0, [10]byte{}, [25]byte{}, err }
	if (ableToRead == false){
		return false, 0, 0, 0, [10]byte{}, [25]byte{}, errors.New("Database corrupt: Contains invalid message.")
	}
	if (messageHash != messageHash_Retrieved){
		return false, 0, 0, 0, [10]byte{}, [25]byte{}, errors.New("Database corrupt: Chat message entry key does not match message hash.")
	}

	messageSize := len(messageBytes)

	messageVersionEncoded, err := encoding.EncodeMessagePackBytes(messageVersion)
	if (err != nil) { return false, 0, 0, 0, [10]byte{}, [25]byte{}, err }

	messageNetworkTypeEncoded, err := encoding.EncodeMessagePackBytes(messageNetworkType)
	if (err != nil) { return false, 0, 0, 0, [10]byte{}, [25]byte{}, err }

	messageSizeEncoded, err := encoding.EncodeMessagePackBytes(messageSize)
	if (err != nil) { return false, 0, 0, 0, [10]byte{}, [25]byte{}, err }

	messageInboxEncoded, err := encoding.EncodeMessagePackBytes(messageInbox)
	if (err != nil) { return false, 0, 0, 0, [10]byte{}, [25]byte{}, err }

	messageCipherKeyHashEncoded, err := encoding.EncodeMessagePackBytes(messageCipherKeyHash)
	if (err != nil) { return false, 0, 0, 0, [10]byte{}, [25]byte{}, err }

	messageSlice := []messagepack.RawMessage{messageVersionEncoded, messageNetworkTypeEncoded, messageSizeEncoded, messageInboxEncoded, messageCipherKeyHashEncoded}

	newMessageMetadata, err := encoding.EncodeMessagePackBytes(messageSlice)
	if (err != nil) { return false, 0, 0, 0, [10]byte{}, [25]byte{}, err }

	err = badgerDatabase.AddMessageMetadata(messageHash, newMessageMetadata)
	if (err != nil) { return false, 0, 0, 0, [10]byte{}, [25]byte{}, err }

	return true, messageVersion, messageNetworkType, messageSize, messageInbox, messageCipherKeyHash, nil
}



