
// myChatKeys provides functions to manage a user's chat keys
// New keys are created after a set duration has passed
// Old keys are deleted after the user has downloaded all of the messages during the period when they were used

package myChatKeys

import "seekia/internal/cryptography/mlkem"
import "seekia/internal/cryptography/nacl"
import "seekia/internal/cryptography/csidh"
import "seekia/internal/encoding"
import "seekia/internal/helpers"
import "seekia/internal/identity"
import "seekia/internal/logger"
import "seekia/internal/messaging/readMessages"
import "seekia/internal/myDatastores/myMap"
import "seekia/internal/myDatastores/myMapList"
import "seekia/internal/myIdentity"

import "sync"
import "time"
import "strings"
import "errors"

//TODO: Add pruning function to get rid of old keys

// This mutex will be locked whenever we update the chat keys maplist
var updatingMyChatKeysMutex sync.Mutex

// This mutex will be locked whenever we update the chatKeysLatestUpdateTime map object
var updatingMyChatKeysLatestUpdateTimeMutex sync.Mutex

var myChatKeysMapListDatastore *myMapList.MyMapList

// Map Structure: Identity Hash + "@" + Network Type -> Latest update unix time
var myChatKeysLatestUpdateTimeMapDatastore *myMap.MyMap

// Map Structure: Identity Hash + "@" + Network Type -> Nacl Key + "+" + Mlkem Key
var myNewestBroadcastPublicChatKeysMapDatastore *myMap.MyMap


// This function must be called whenever an app user signs in
func InitializeMyChatKeysDatastores()error{

	updatingMyChatKeysMutex.Lock()
	defer updatingMyChatKeysMutex.Unlock()
	
	updatingMyChatKeysLatestUpdateTimeMutex.Lock()
	defer updatingMyChatKeysLatestUpdateTimeMutex.Unlock()

	newMyChatKeysLatestUpdateTimeMapDatastore, err := myMap.CreateNewMap("MyChatKeysLatestUpdateTime")
	if (err != nil) { return err }

	newMyChatKeysMapListDatastore, err := myMapList.CreateNewMapList("MyChatKeys")
	if (err != nil) { return err }

	newMyNewestBroadcastPublicChatKeysMapDatastore, err := myMap.CreateNewMap("MyNewestBroadcastPublicChatKeys")
	if (err != nil) { return err }

	myChatKeysLatestUpdateTimeMapDatastore = newMyChatKeysLatestUpdateTimeMapDatastore
	
	myChatKeysMapListDatastore = newMyChatKeysMapListDatastore

	myNewestBroadcastPublicChatKeysMapDatastore = newMyNewestBroadcastPublicChatKeysMapDatastore
	
	return nil
}

// This function provides the decryption keys for a particular identity hash

//Outputs:
//	-bool: My identity found
//	-bool: Any chat keys found (will be false if user has never broadcasted their profile)
//	-[]ChatKeySet: Chat keys list
//	-error
func GetMyChatDecryptionKeySetsList(myIdentityHash [16]byte, networkType byte)(bool, bool, []readMessages.ChatKeySet, error){

	isMine, myIdentityType, err := myIdentity.CheckIfIdentityHashIsMine(myIdentityHash)
	if (err != nil) { return false, false, nil, err }
	if (isMine == false) {
		return false, false, nil, nil
	}

	if (myIdentityType != "Mate" && myIdentityType != "Moderator"){
		return false, false, nil, errors.New("GetMyChatDecryptionKeysMapList called with Host identity.")
	}

	isValid := helpers.VerifyNetworkType(networkType)
	if (isValid == false){
		networkTypeString := helpers.ConvertByteToString(networkType)
		return false, false, nil, errors.New("GetMyChatDecryptionKeySetsList called with invalid networkType: " + networkTypeString)
	}

	myIdentityHashString, _, err := identity.EncodeIdentityHashBytesToString(myIdentityHash)
	if (err != nil) {
		myIdentityHashHex := encoding.EncodeBytesToHexString(myIdentityHash[:])
		return false, false, nil, errors.New("CheckIfIdentityHashIsMine not verifying identity hash: " + myIdentityHashHex)
	}

	networkTypeString := helpers.ConvertByteToString(networkType)

	lookupMap := map[string]string{
		"MyIdentityHash": myIdentityHashString,
		"NetworkType": networkTypeString,
	}

	anyExist, myChatKeysMapList, err := myChatKeysMapListDatastore.GetMapListItems(lookupMap)
	if (err != nil){ return false, false, nil, err }
	if (anyExist == false){
		return true, false, nil, nil
	}

	chatDecryptionKeySetsList := make([]readMessages.ChatKeySet, 0, len(myChatKeysMapList))

	for _, keySetMap := range myChatKeysMapList{

		myNaclPublicKey, exists := keySetMap["NaclPublicKey"]
		if (exists == false){
			return false, false, nil, errors.New("Malformed myChatKeys map list: Item missing NaclPublicKey")
		}
		myNaclPrivateKey, exists := keySetMap["NaclPrivateKey"]
		if (exists == false){
			return false, false, nil, errors.New("Malformed myChatKeys map list: Item missing NaclPrivateKey")
		}
		myMlkemPrivateKey, exists := keySetMap["MlkemPrivateKey"]
		if (exists == false){
			return false, false, nil, errors.New("Malformed myChatKeys map list: Item missing MlkemPrivateKey")
		}
		myCsidhPrivateKey, exists := keySetMap["CsidhPrivateKey"]
		if (exists == false){
			return false, false, nil, errors.New("Malformed myChatKeys map list: Item missing CsidhPrivateKey")
		}

		myNaclPublicKeyArray, err := nacl.ReadNaclPublicKeyString(myNaclPublicKey)
		if (err != nil){
			return false, false, nil, errors.New("Malformed myChatKeys map list: Invalid NaclPublicKey: " + myNaclPublicKey)
		}

		myNaclPrivateKeyBytes, err := encoding.DecodeHexStringToBytes(myNaclPrivateKey)
		if (err != nil){
			return false, false, nil, errors.New("Malformed myChatKeys map list: NaclPrivateKey is not Hex.")
		}

		if (len(myNaclPrivateKeyBytes) != 32){
			return false, false, nil, errors.New("Malformed myChatKeys map list: NaclPrivateKey is invalid length.")
		}

		myNaclPrivateKeyArray := [32]byte(myNaclPrivateKeyBytes)

		myMlkemPrivateKeyBytes, err := encoding.DecodeHexStringToBytes(myMlkemPrivateKey)
		if (err != nil){
			return false, false, nil, errors.New("Malformed myChatKeys map list: MlkemPrivateKey is not Hex.")
		}

		if (len(myMlkemPrivateKeyBytes) != 64){
			return false, false, nil, errors.New("Malformed myChatKeys map list: MlkemPrivateKey is invalid length.")
		}

		myMlkemPrivateKeyArray := [64]byte(myMlkemPrivateKeyBytes)

		myCsidhPrivateKeyBytes, err := encoding.DecodeHexStringToBytes(myCsidhPrivateKey)
		if (err != nil){
			return false, false, nil, errors.New("Malformed myChatKeys map list: CsidhPrivateKey is not Hex.")
		}

		if (len(myCsidhPrivateKeyBytes) != 37){
			return false, false, nil, errors.New("Malformed myChatKeys map list: CsidhPrivateKey is invalid length.")
		}

		myCsidhPrivateKeyArray := [37]byte(myCsidhPrivateKeyBytes)

		newChatKeySetObject := readMessages.ChatKeySet{
			NaclPublicKey: myNaclPublicKeyArray,
			NaclPrivateKey: myNaclPrivateKeyArray,
			MlkemPrivateKey: myMlkemPrivateKeyArray,
			CsidhPrivateKey: myCsidhPrivateKeyArray,
		}

		chatDecryptionKeySetsList = append(chatDecryptionKeySetsList, newChatKeySetObject)
	}

	return true, true, chatDecryptionKeySetsList, nil
}

// This function generates new chat keys for an identity.
func GenerateNewChatKeys(myIdentityHash [16]byte, networkType byte)error{

	myIdentityHashString, _, err := identity.EncodeIdentityHashBytesToString(myIdentityHash)
	if (err != nil) {
		myIdentityHashHex := encoding.EncodeBytesToHexString(myIdentityHash[:])
		return errors.New("GenerateNewChatKeys called with invalid identity hash: " + myIdentityHashHex)
	}

	isValid := helpers.VerifyNetworkType(networkType)
	if (isValid == false){
		networkTypeString := helpers.ConvertByteToString(networkType)
		return errors.New("GenerateNewChatKeys called with invalid networkType: " + networkTypeString)
	}

	updatingMyChatKeysMutex.Lock()
	defer updatingMyChatKeysMutex.Unlock()

	currentTime := time.Now().Unix()
	currentTimeString := helpers.ConvertInt64ToString(currentTime)

	networkTypeString := helpers.ConvertByteToString(networkType)

	newNaclPublicKey, newNaclPrivateKey, err := nacl.GetNewRandomPublicPrivateNaclKeys()
	if (err != nil) { return err }

	newMlkemPublicKey, newMlkemPrivateKey, err := mlkem.GetNewRandomPublicPrivateMlkemKeys()
	if (err != nil) { return err }

	newCsidhPublicKey, newCsidhPrivateKey, err := csidh.GetNewRandomPublicPrivateCsidhKeys()
	if (err != nil) { return err }

	newNaclPublicKeyString := encoding.EncodeBytesToBase64String(newNaclPublicKey[:])
	newNaclPrivateKeyString := encoding.EncodeBytesToHexString(newNaclPrivateKey[:])

	newMlkemPublicKeyString := encoding.EncodeBytesToBase64String(newMlkemPublicKey[:])
	newMlkemPrivateKeyString := encoding.EncodeBytesToHexString(newMlkemPrivateKey[:])

	newCsidhPublicKeyString := encoding.EncodeBytesToBase64String(newCsidhPublicKey[:])
	newCsidhPrivateKeyString := encoding.EncodeBytesToHexString(newCsidhPrivateKey[:])

	newMapListItem := map[string]string{
		"MyIdentityHash": myIdentityHashString,
		"CreatedTime": currentTimeString,
		"NetworkType": networkTypeString,
		"NaclPublicKey": newNaclPublicKeyString,
		"NaclPrivateKey": newNaclPrivateKeyString,
		"MlkemPublicKey": newMlkemPublicKeyString,
		"MlkemPrivateKey": newMlkemPrivateKeyString,
		"CsidhPublicKey": newCsidhPublicKeyString,
		"CsidhPrivateKey": newCsidhPrivateKeyString,
	}

	err = myChatKeysMapListDatastore.AddMapListItem(newMapListItem)
	if (err != nil) { return err }

	return nil
}


// This function will get a user's newest public chat keys
// It will create new chat keys for the provided identity if none exist
//Outputs:
//	-bool: My identity exists
//	-[32]byte: My Nacl Public key
//	-[1568]byte: My Mlkem Public key
//	-[64]byte: My Csidh Public key
//	-error
func GetMyNewestPublicChatKeys(myIdentityHash [16]byte, networkType byte)(bool, [32]byte, [1568]byte, [64]byte, error){

	isMine, myIdentityType, err := myIdentity.CheckIfIdentityHashIsMine(myIdentityHash)
	if (err != nil) { return false, [32]byte{}, [1568]byte{}, [64]byte{}, err }
	if (isMine == false){
		return false, [32]byte{}, [1568]byte{}, [64]byte{}, nil
	}
	if (myIdentityType != "Mate" && myIdentityType != "Moderator"){
		return false, [32]byte{}, [1568]byte{}, [64]byte{}, errors.New("GetMyNewestPublicChatKeys called with host identity.")
	}

	isValid := helpers.VerifyNetworkType(networkType)
	if (isValid == false){
		networkTypeString := helpers.ConvertByteToString(networkType)
		return false, [32]byte{}, [1568]byte{}, [64]byte{}, errors.New("GetMyNewestPublicChatKeys called with invalid networkType: " + networkTypeString)
	}

	myIdentityHashString, _, err := identity.EncodeIdentityHashBytesToString(myIdentityHash)
	if (err != nil){
		myIdentityHashHex := encoding.EncodeBytesToHexString(myIdentityHash[:])
		return false, [32]byte{}, [1568]byte{}, [64]byte{}, errors.New("CheckIfIdentityHashIsMine not verifying identity hash: " + myIdentityHashHex)
	}

	updatingMyChatKeysMutex.Lock()
	defer updatingMyChatKeysMutex.Unlock()

	networkTypeString := helpers.ConvertByteToString(networkType)

	lookupMap := map[string]string{
		"MyIdentityHash": myIdentityHashString,
		"NetworkType": networkTypeString,
	}

	anyItemsFound, myChatKeysMapList, err := myChatKeysMapListDatastore.GetMapListItems(lookupMap)
	if (err != nil){ return false, [32]byte{}, [1568]byte{}, [64]byte{}, err }
	if (anyItemsFound == true){

		if (len(myChatKeysMapList) == 0){
			return false, [32]byte{}, [1568]byte{}, [64]byte{}, errors.New("GetMapListItems returning empty items list after claiming items were found.")
		}

		newestChatKeysCreatedTime := int64(0)
		var newestNaclPublicKey [32]byte
		var newestMlkemPublicKey [1568]byte
		var newestCsidhPublicKey [64]byte

		for index, chatKeysSetMap := range myChatKeysMapList{

			itemMyIdentityHash, exists := chatKeysSetMap["MyIdentityHash"]
			if (exists == false) {
				return false, [32]byte{}, [1568]byte{}, [64]byte{}, errors.New("myMapList lookup not working properly.")
			}

			if (itemMyIdentityHash != myIdentityHashString){
				return false, [32]byte{}, [1568]byte{}, [64]byte{}, errors.New("myMapList lookup not working properly.")
			}

			timeKeysCreated, exists := chatKeysSetMap["CreatedTime"]
			if (exists == false) {
				return false, [32]byte{}, [1568]byte{}, [64]byte{}, errors.New("Malformed myChatKeys map list: Item missing CreatedTime")
			}
			naclPublicKey, exists := chatKeysSetMap["NaclPublicKey"]
			if (exists == false) {
				return false, [32]byte{}, [1568]byte{}, [64]byte{}, errors.New("Malformed myChatKeys map list: Item missing NaclPublicKey")
			}
			mlkemPublicKey, exists := chatKeysSetMap["MlkemPublicKey"]
			if (exists == false) {
				return false, [32]byte{}, [1568]byte{}, [64]byte{}, errors.New("Malformed myChatKeys map list: Item missing MlkemPublicKey")
			}
			csidhPublicKey, exists := chatKeysSetMap["CsidhPublicKey"]
			if (exists == false) {
				return false, [32]byte{}, [1568]byte{}, [64]byte{}, errors.New("Malformed myChatKeys map list: Item missing CsidhPublicKey")
			}

			naclPublicKeyArray, err := nacl.ReadNaclPublicKeyString(naclPublicKey)
			if (err != nil){
				return false, [32]byte{}, [1568]byte{}, [64]byte{}, errors.New("Malformed myChatKeys map list: Item contains invalid NaclPublicKey: " + naclPublicKey)
			}

			mlkemPublicKeyArray, err := mlkem.ReadMlkemPublicKeyString(mlkemPublicKey)
			if (err != nil){
				return false, [32]byte{}, [1568]byte{}, [64]byte{}, errors.New("Malformed myChatKeys map list: Item contains invalid MlkemPublicKey: " + mlkemPublicKey)
			}

			csidhPublicKeyArray, err := csidh.ReadCsidhPublicKeyString(csidhPublicKey)
			if (err != nil){
				return false, [32]byte{}, [1568]byte{}, [64]byte{}, errors.New("Malformed myChatKeys map list: Item contains invalid CsidhPublicKey: " + csidhPublicKey)
			}

			timeKeysCreatedInt64, err := helpers.ConvertStringToInt64(timeKeysCreated)
			if (err != nil) {
				return false, [32]byte{}, [1568]byte{}, [64]byte{}, errors.New("Malformed myChatKeys map list: Item contains invalid CreatedTime: " + timeKeysCreated)
			}

			if (index == 0 || newestChatKeysCreatedTime < timeKeysCreatedInt64){
				newestChatKeysCreatedTime = timeKeysCreatedInt64
				newestNaclPublicKey = naclPublicKeyArray
				newestMlkemPublicKey = mlkemPublicKeyArray
				newestCsidhPublicKey = csidhPublicKeyArray
			}
		}

		return true, newestNaclPublicKey, newestMlkemPublicKey, newestCsidhPublicKey, nil
	}

	// We must create new chat keys

	currentTime := time.Now().Unix()
	currentTimeString := helpers.ConvertInt64ToString(currentTime)

	newNaclPublicKey, newNaclPrivateKey, err := nacl.GetNewRandomPublicPrivateNaclKeys()
	if (err != nil) { return false, [32]byte{}, [1568]byte{}, [64]byte{}, err }
	
	newMlkemPublicKey, newMlkemPrivateKey, err := mlkem.GetNewRandomPublicPrivateMlkemKeys()
	if (err != nil) { return false, [32]byte{}, [1568]byte{}, [64]byte{}, err }

	newCsidhPublicKey, newCsidhPrivateKey, err := csidh.GetNewRandomPublicPrivateCsidhKeys()
	if (err != nil) { return false, [32]byte{}, [1568]byte{}, [64]byte{}, err }

	newNaclPublicKeyString := encoding.EncodeBytesToBase64String(newNaclPublicKey[:])
	newNaclPrivateKeyString := encoding.EncodeBytesToHexString(newNaclPrivateKey[:])

	newMlkemPublicKeyString := encoding.EncodeBytesToBase64String(newMlkemPublicKey[:])
	newMlkemPrivateKeyString := encoding.EncodeBytesToHexString(newMlkemPrivateKey[:])

	newCsidhPublicKeyString := encoding.EncodeBytesToBase64String(newCsidhPublicKey[:])
	newCsidhPrivateKeyString := encoding.EncodeBytesToHexString(newCsidhPrivateKey[:])

	newChatKeysSetMap := map[string]string{
		"MyIdentityHash": myIdentityHashString,
		"NetworkType": networkTypeString,
		"CreatedTime": currentTimeString,
		"NaclPublicKey": newNaclPublicKeyString,
		"NaclPrivateKey": newNaclPrivateKeyString,
		"MlkemPublicKey": newMlkemPublicKeyString,
		"MlkemPrivateKey": newMlkemPrivateKeyString,
		"CsidhPublicKey": newCsidhPublicKeyString,
		"CsidhPrivateKey": newCsidhPrivateKeyString,
	}

	err = myChatKeysMapListDatastore.AddMapListItem(newChatKeysSetMap)
	if (err != nil) { return false, [32]byte{}, [1568]byte{}, [64]byte{}, err }

	return true, newNaclPublicKey, newMlkemPublicKey, newCsidhPublicKey, nil	
}

// This function is only called when a new profile is added to myBroadcasts
func SetMyChatKeysLatestUpdateTime(myIdentityHash [16]byte, networkType byte, newLatestUpdateTime int64)error{

	isMine, myIdentityType, err := myIdentity.CheckIfIdentityHashIsMine(myIdentityHash)
	if (err != nil) { return err }
	if (isMine == false){
		return errors.New("SetMyChatKeysLatestUpdateTime called with identity that is not mine.")
	}
	if (myIdentityType != "Mate" && myIdentityType != "Moderator"){
		return errors.New("SetMyChatKeysLatestUpdateTime called with Host identity.")
	}

	isValid := helpers.VerifyNetworkType(networkType)
	if (isValid == false){
		networkTypeString := helpers.ConvertByteToString(networkType)
		return errors.New("SetMyChatKeysLatestUpdateTime called with invalid networkType: " + networkTypeString)
	}

	updatingMyChatKeysLatestUpdateTimeMutex.Lock()
	defer updatingMyChatKeysLatestUpdateTimeMutex.Unlock()

	exists, existingLatestUpdateTime, err := GetMyChatKeysLatestUpdateTime(myIdentityHash, networkType)
	if (err != nil) { return err }
	if (exists == true){
		if (existingLatestUpdateTime > newLatestUpdateTime) {
			// This would only happen if our system clock was invalid, and became valid
			// We will log this, but allow our latestUpdateTime to be updated
			err := logger.AddLogEntry("General", "Existing latest chat keys update time is later than new update time.")
			if (err != nil) { return err }

		} else if (existingLatestUpdateTime == newLatestUpdateTime){
			// No update is needed.
			return nil
		}
	}

	myIdentityHashString, _, err := identity.EncodeIdentityHashBytesToString(myIdentityHash)
	if (err != nil){
		myIdentityHashHex := encoding.EncodeBytesToHexString(myIdentityHash[:])
		return errors.New("CheckIfIdentityHashIsMine not verifying identity hash: " + myIdentityHashHex)
	}

	networkTypeString := helpers.ConvertByteToString(networkType)

	mapEntryKey := myIdentityHashString + "@" + networkTypeString

	newLatestUpdateTimeString := helpers.ConvertInt64ToString(newLatestUpdateTime)

	err = myChatKeysLatestUpdateTimeMapDatastore.SetMapEntry(mapEntryKey, newLatestUpdateTimeString)
	if (err != nil) { return err }
	
	return nil
}


//Outputs:
//	-bool: Update time exists (wont exist if profile has never been broadcast, or existing profile is not imported yet)
//	-int64: Current latest update time  
//	-error
func GetMyChatKeysLatestUpdateTime(myIdentityHash [16]byte, networkType byte)(bool, int64, error){

	isMine, myIdentityType, err := myIdentity.CheckIfIdentityHashIsMine(myIdentityHash)
	if (err != nil) { return false, 0, err }
	if (isMine == false){
		return false, 0, errors.New("GetMyChatKeysLatestUpdateTime called with identity that is not mine.")
	}
	if (myIdentityType != "Mate" && myIdentityType != "Moderator"){
		return false, 0, errors.New("GetMyChatKeysLatestUpdateTime called with Host identity.")
	}

	isValid := helpers.VerifyNetworkType(networkType)
	if (isValid == false){
		networkTypeString := helpers.ConvertByteToString(networkType)
		return false, 0, errors.New("GetMyChatKeysLatestUpdateTime called with invalid networkType: " + networkTypeString)
	}

	myIdentityHashString, _, err := identity.EncodeIdentityHashBytesToString(myIdentityHash)
	if (err != nil){
		myIdentityHashHex := encoding.EncodeBytesToHexString(myIdentityHash[:])
		return false, 0, errors.New("CheckIfIdentityHashIsMine not verifying my identity hash: " + myIdentityHashHex)
	}

	networkTypeString := helpers.ConvertByteToString(networkType)

	mapEntryKey := myIdentityHashString + "@" + networkTypeString

	exists, latestUpdateTimeString, err := myChatKeysLatestUpdateTimeMapDatastore.GetMapEntry(mapEntryKey)
	if (err != nil) { return false, 0, err }
	if (exists == false) {
		return false, 0, nil
	}

	latestUpdateTimeInt64, err := helpers.ConvertStringToInt64(latestUpdateTimeString)
	if (err != nil) {
		return false, 0, errors.New("myChatKeysLatestUpdateTimeMapDatastore is corrupt: Contains invalid latestUpdateTime: " + latestUpdateTimeString)
	}

	return true, latestUpdateTimeInt64, nil
}


// This function is used to keep track of the user's newest broadcasted chat keys
// We need to do this because we need to be able to tell if the profile we are broadcasting contains new keys
// This is stored seperately from myBroadcasts
// This datastore can prevent users from having to update their latestChatKeysUpdateTime when moving to a new device, if they import their keys
func SetMyNewestBroadcastPublicChatKeys(myIdentityHash [16]byte, networkType byte, naclKey [32]byte, mlkemKey [1568]byte, csidhKey [64]byte)error{

	isMine, myIdentityType, err := myIdentity.CheckIfIdentityHashIsMine(myIdentityHash)
	if (err != nil) { return err }
	if (isMine == false){
		return errors.New("SetMyNewestBroadcastPublicChatKeys called with identity that is not mine.")
	}
	if (myIdentityType != "Mate" && myIdentityType != "Moderator"){
		return errors.New("SetMyNewestBroadcastPublicChatKeys called with Host identity.")
	}

	isValid := helpers.VerifyNetworkType(networkType)
	if (isValid == false){
		networkTypeString := helpers.ConvertByteToString(networkType)
		return errors.New("SetMyNewestBroadcastPublicChatKeys called with invalid networkType: " + networkTypeString)
	}

	myIdentityHashString, _, err := identity.EncodeIdentityHashBytesToString(myIdentityHash)
	if (err != nil){
		myIdentityHashHex := encoding.EncodeBytesToHexString(myIdentityHash[:])
		return errors.New("CheckIfIdentityHashIsMine not verifying my identity hash: " + myIdentityHashHex)
	}

	networkTypeString := helpers.ConvertByteToString(networkType)

	mapEntryKey := myIdentityHashString + "@" + networkTypeString

	naclKeyEncoded := encoding.EncodeBytesToBase64String(naclKey[:])
	mlkemKeyEncoded := encoding.EncodeBytesToBase64String(mlkemKey[:])
	csidhKeyEncoded := encoding.EncodeBytesToBase64String(csidhKey[:])

	newEntryValue := naclKeyEncoded + "+" + mlkemKeyEncoded + "+" + csidhKeyEncoded

	err = myNewestBroadcastPublicChatKeysMapDatastore.SetMapEntry(mapEntryKey, newEntryValue)
	if (err != nil) { return err }

	return nil
}

//Outputs:
//	-bool: My identity found
//	-bool: Any keys found
//	-[32]byte: Newest broadcast Nacl key (Encoded base64)
//	-[1568]byte: Newest broadcast Mlkem key (Encoded base64)
//	-[64]byte: Newest broadcast Csidh Key (Encoded base64)
//	-error
func GetMyNewestBroadcastPublicChatKeys(myIdentityHash [16]byte, networkType byte)(bool, bool, [32]byte, [1568]byte, [64]byte, error){

	isMine, myIdentityType, err := myIdentity.CheckIfIdentityHashIsMine(myIdentityHash)
	if (err != nil) { return false, false, [32]byte{}, [1568]byte{}, [64]byte{}, err }
	if (isMine == false){
		return false, false, [32]byte{}, [1568]byte{}, [64]byte{}, nil
	}
	if (myIdentityType != "Mate" && myIdentityType != "Moderator"){
		return false, false, [32]byte{}, [1568]byte{}, [64]byte{}, errors.New("GetMyNewestBroadcastPublicChatKeys called with Host identity.")
	}

	isValid := helpers.VerifyNetworkType(networkType)
	if (isValid == false){
		networkTypeString := helpers.ConvertByteToString(networkType)
		return false, false, [32]byte{}, [1568]byte{}, [64]byte{}, errors.New("GetMyNewestBroadcastPublicChatKeys called with invalid networkType: " + networkTypeString)
	}

	myIdentityHashString, _, err := identity.EncodeIdentityHashBytesToString(myIdentityHash)
	if (err != nil){
		myIdentityHashHex := encoding.EncodeBytesToHexString(myIdentityHash[:])
		return false, false, [32]byte{}, [1568]byte{}, [64]byte{}, errors.New("CheckIfIdentityHashIsMine not verifying my identity hash: " + myIdentityHashHex)
	}

	networkTypeString := helpers.ConvertByteToString(networkType)

	mapEntryKey := myIdentityHashString + "@" + networkTypeString

	exists, mapItemValue, err := myNewestBroadcastPublicChatKeysMapDatastore.GetMapEntry(mapEntryKey)
	if (err != nil) { return false, false, [32]byte{}, [1568]byte{}, [64]byte{}, err }
	if (exists == false){
		return true, false, [32]byte{}, [1568]byte{}, [64]byte{}, nil
	}

	keysList := strings.Split(mapItemValue, "+")

	if (len(keysList) != 3){
		return false, false, [32]byte{}, [1568]byte{}, [64]byte{}, errors.New("Malformed myNewestBroadcastPublicChatKeysMapDatastore item value: " + mapItemValue)
	}

	naclKeyString := keysList[0]
	mlkemKeyString := keysList[1]
	csidhKeyString := keysList[2]

	naclKey, err := nacl.ReadNaclPublicKeyString(naclKeyString)
	if (err != nil){
		return false, false, [32]byte{}, [1568]byte{}, [64]byte{}, errors.New("Malformed myNewestBroadcastPublicChatKeysMapDatastore item value: Invalid NaclKey: " + naclKeyString)
	}

	mlkemKey, err := mlkem.ReadMlkemPublicKeyString(mlkemKeyString)
	if (err != nil){
		return false, false, [32]byte{}, [1568]byte{}, [64]byte{}, errors.New("Malformed myNewestBroadcastPublicChatKeysMapDatastore item value: Invalid MlkemKey: " + mlkemKeyString)
	}

	csidhKey, err := csidh.ReadCsidhPublicKeyString(csidhKeyString)
	if (err != nil){
		return false, false, [32]byte{}, [1568]byte{}, [64]byte{}, errors.New("Malformed myNewestBroadcastPublicChatKeysMapDatastore item value: Invalid CsidhKey: " + csidhKeyString)
	}

	return true, true, naclKey, mlkemKey, csidhKey, nil
}



