
// generate provides functions to generate fake content
// Examples of fake content include random text, messages, profiles, reviews and reports
// This package is used for testing and debugging purposes
// Add fake content to the database using /utilities/generateContent/generateContent.go

package generate

import "seekia/internal/contentMetadata"
import "seekia/internal/cryptography/blake3"
import "seekia/internal/cryptography/edwardsKeys"
import "seekia/internal/cryptography/mlkem"
import "seekia/internal/cryptography/csidh"
import "seekia/internal/cryptography/nacl"
import "seekia/internal/encoding"
import "seekia/internal/genetics/companyAnalysis"
import "seekia/internal/genetics/geneticReferences/traits"
import "seekia/internal/genetics/geneticReferences/monogenicDiseases"
import "seekia/internal/genetics/geneticReferences/polygenicDiseases"
import "seekia/internal/helpers"
import "seekia/internal/identity"
import "seekia/internal/imagery"
import "seekia/internal/messaging/chatMessageStorage"
import "seekia/internal/messaging/createMessages"
import "seekia/internal/messaging/inbox"
import "seekia/internal/messaging/myChatKeys"
import "seekia/internal/messaging/readMessages"
import "seekia/internal/moderation/createReports"
import "seekia/internal/moderation/createReviews"
import "seekia/internal/moderation/reportStorage"
import "seekia/internal/moderation/reviewStorage"
import "seekia/internal/myIdentity"
import "seekia/internal/parameters/createParameters"
import "seekia/internal/profiles/createProfiles"
import "seekia/internal/profiles/profileStorage"
import "seekia/internal/wordLists"
import "seekia/internal/imageFiles"
import "seekia/internal/currencies"

import "errors"
import "strings"
import "time"
import "unicode"


func GetFakeParameters(parametersType string, networkType byte)([]byte, error){

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

	networkTypeString := helpers.ConvertByteToString(networkType)

	parametersMap := make(map[string]string)

	//TODO: Build the parameters

	parametersMap["ParametersType"] = parametersType
	parametersMap["NetworkType"] = networkTypeString

	newIdentityPublicKey, newIdentityPrivateKey, err := identity.GetNewRandomPublicPrivateIdentityKeys()
	if (err != nil) { return nil, err }

	adminPublicKeysList := [][32]byte{newIdentityPublicKey}

	parametersContentBytes, err := createParameters.CreateParametersContentBytes(adminPublicKeysList, parametersMap)
	if (err != nil) { return nil, err }

	contentHash, err := blake3.Get32ByteBlake3Hash(parametersContentBytes)
	if (err != nil) { return nil, err }

	signatureBytes := edwardsKeys.CreateSignature(newIdentityPrivateKey, contentHash)

	adminSignaturesMap := map[[32]byte][64]byte{
		newIdentityPublicKey: signatureBytes,
	}

	parametersBytes, err := createParameters.CreateParameters(adminSignaturesMap, parametersContentBytes)
	if (err != nil) { return nil, err }

	return parametersBytes, nil
}

// This function will add fake profiles to the database
// They will never be disabled profiles
func GenerateFakeProfiles(profileType string, networkType byte, profilesToGenerate int)error{

	if (profileType != "Mate" && profileType != "Host" && profileType != "Moderator"){
		return errors.New("GenerateFakeProfiles called with invalid profileType: " + profileType)
	}

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

	for i := 0; i < profilesToGenerate; i++ {

		newIdentityPublicKey, newIdentityPrivateKey, err := identity.GetNewRandomPublicPrivateIdentityKeys()
		if (err != nil) { return err }

		fakeProfile, err := GetFakeProfile(profileType, newIdentityPublicKey, newIdentityPrivateKey, networkType)
		if (err != nil) { return err }

		wellFormed, _, err := profileStorage.AddUserProfile(fakeProfile)
		if (err != nil) { return err }
		if (wellFormed == false){
			return errors.New("Profile to add is malformed.")
		}	
	}

	//TODO: Add parameter to approve profiles so they will be visible for the user

	return nil
}

// This function will add disabled profiles to the database
func GenerateDisabledProfiles(profileType string, networkType byte, profilesToGenerate int)error{

	if (profileType != "Mate" && profileType != "Host" && profileType != "Moderator"){
		return errors.New("GenerateDisabledProfiles called with invalid profileType: " + profileType)
	}

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

	networkTypeString := helpers.ConvertByteToString(networkType)

	for i := 0; i < profilesToGenerate; i++ {

		newIdentityPublicKey, newIdentityPrivateKey, err := identity.GetNewRandomPublicPrivateIdentityKeys()
		if (err != nil) { return err }

		profileMap := map[string]string{
			"NetworkType": networkTypeString,
			"ProfileType": profileType,
			"Disabled": "Yes",
		}

		newProfile, err := createProfiles.CreateProfile(newIdentityPublicKey, newIdentityPrivateKey, profileMap)
		if (err != nil) { return err }

		wellFormed, _, err := profileStorage.AddUserProfile(newProfile)
		if (err != nil) { return err }
		if (wellFormed == false){
			return errors.New("Profile to add is malformed.")
		}
	}

	return nil
}

func GetRandomText(maximumCharacters int, addNewlines bool)(string, error){

	if (maximumCharacters == 0){
		return "", errors.New("GetRandomText called with 0 maximumCharacters")
	}
	if (maximumCharacters == 1){
		return "!", nil
	}
	if (maximumCharacters == 2){
		return "Hi", nil
	}
	if (maximumCharacters == 3){
		return "Hi!", nil
	}

	englishWordList, err := wordLists.GetWordListFromLanguage("English")
	if (err != nil) { return "", err }

	// This is the length of the randomText we will generate
	textCharacterLength := helpers.GetRandomIntWithinRange(1, maximumCharacters)

	addedCharactersCount := 0

	var newRandomTextBuilder strings.Builder

	for {

		neededCharacters := textCharacterLength - addedCharactersCount

		if (neededCharacters < 0){
			return "", errors.New("Failed to get random text: added too many characters.")
		} else if (neededCharacters == 0){
			break
		} else if (neededCharacters == 1){
			newRandomTextBuilder.WriteString("!")
			break
		} else if (neededCharacters == 2){
			newRandomTextBuilder.WriteString("!!")
			break		
		} else if (neededCharacters == 3){
			newRandomTextBuilder.WriteString("!!!")
			break
		}

		if (addNewlines == true){

			// We will add a newline with a probability of 3%

			addNewline, err := helpers.GetRandomBoolWithProbability(.03)
			if (err != nil) { return "", err }
			if (addNewline == true){

				newRandomTextBuilder.WriteString("\n")

				addedCharactersCount += 1
				continue
			}
		}

		randomWord, err := helpers.GetRandomItemFromList(englishWordList)
		if (err != nil) { return "", err }

		wordWithWhitespace := randomWord + " "
		wordWithWhitespaceCharacterCount := len(wordWithWhitespace)

		if (wordWithWhitespaceCharacterCount > neededCharacters){
			// Word is too long, get shorter word
			continue
		}

		newRandomTextBuilder.WriteString(wordWithWhitespace)

		addedCharactersCount += wordWithWhitespaceCharacterCount
	}

	newRandomText := newRandomTextBuilder.String()

	return newRandomText, nil
}

func GetFakeProfile(profileType string, identityPublicKey [32]byte, identityPrivateKey [64]byte, networkType byte)([]byte, error){

	if (profileType != "Mate" && profileType != "Host" && profileType != "Moderator"){
		return nil, errors.New("GetFakeProfile called with invalid profileType: " + profileType)	
	}

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

	networkTypeString := helpers.ConvertByteToString(networkType)

	profileMap := map[string]string{
		"ProfileType": profileType,
		"NetworkType": networkTypeString,
	}

	// This will return true 80% of the time
	getIncludeAttributeBool := func()(bool, error){

		includeAttribute, err := helpers.GetRandomBoolWithProbability(.8)

		return includeAttribute, err
	}

	getRandomNumberWithinRangeAsString := func(minValue int, maxValue int)string{

		newNumber := helpers.GetRandomIntWithinRange(minValue, maxValue)

		result := helpers.ConvertIntToString(newNumber)

		return result
	}

	includeUsername, err := getIncludeAttributeBool()
	if (err != nil) { return nil, err }
	if (includeUsername == true){

		englishWordList, err := wordLists.GetWordListFromLanguage("English")
		if (err != nil) { return nil, err }
		
		var newUsernameBuilder strings.Builder

		for i:=0; i < 3; i++{

			randomWord, err := helpers.GetRandomItemFromList(englishWordList)
			if (err != nil) { return nil, err }

			// We captialize the first letter of the word

			wordRunes := []rune(randomWord)
			wordFirstCharacter := wordRunes[0]
			wordFirstCharacterCapitalized := unicode.ToUpper(wordFirstCharacter)
			wordRunes[0] = wordFirstCharacterCapitalized

			randomWordCapitalized := string(wordRunes)

			newUsernameBuilder.WriteString(randomWordCapitalized)
		}

		profileUsername := newUsernameBuilder.String()

		profileMap["Username"] = profileUsername
	}

	includeDescription, err := getIncludeAttributeBool()
	if (err != nil) { return nil, err }
	if (includeDescription == true){

		getDescriptionMaximumLength := func()int{
			if (profileType == "Mate"){
				return 3000
			}
			if (profileType == "Host"){
				return 300
			}
			//profileType == "Moderator"
			return 500
		}

		descriptionMaximumLength := getDescriptionMaximumLength()

		profileDescription, err := GetRandomText(descriptionMaximumLength, true)
		if (err != nil) { return nil, err }

		profileMap["Description"] = profileDescription
	}

	if (profileType == "Mate" || profileType == "Moderator"){

		// We add chat keys

		randomNaclKey, err := nacl.GetNewRandomPublicNaclKey()
		if (err != nil) { return nil, err }

		randomMlkemKey, err := mlkem.GetNewRandomPublicMlkemKey()
		if (err != nil) { return nil, err }

		randomCsidhKey, _, err := csidh.GetNewRandomPublicPrivateCsidhKeys()
		if (err != nil) { return nil, err }

		randomNaclKeyString := encoding.EncodeBytesToBase64String(randomNaclKey[:])
		randomMlkemKeyString := encoding.EncodeBytesToBase64String(randomMlkemKey[:])
		randomCsidhKeyString := encoding.EncodeBytesToBase64String(randomCsidhKey[:])

		profileMap["NaclKey"] = randomNaclKeyString
		profileMap["MlkemKey"] = randomMlkemKeyString
		profileMap["CsidhKey"] = randomCsidhKeyString

		deviceIdentifier, err := helpers.GetNewRandomHexString(11)
		if (err != nil) { return nil, err }

		profileMap["DeviceIdentifier"] = deviceIdentifier

		chatKeysLatestUpdateTime := time.Now().Unix()
		chatKeysLatestUpdateTimeString := helpers.ConvertInt64ToString(chatKeysLatestUpdateTime)

		profileMap["ChatKeysLatestUpdateTime"] = chatKeysLatestUpdateTimeString
	}

	if (profileType == "Host"){

		//TODO: Uncomment this once we add these attributes to the profileFormat package
		/*

		hostingMateContent, err := helpers.GetRandomItemFromList([]string{"Yes", "No"})
		if (err != nil) { return nil, err }

		hostingHostContent, err := helpers.GetRandomItemFromList([]string{"Yes", "No"})
		if (err != nil) { return nil, err }

		hostingModeratorContent, err := helpers.GetRandomItemFromList([]string{"Yes", "No"})
		if (err != nil) { return nil, err }

		profileMap["HostingMateContent"] = hostingMateContent

		if (hostingMateContent == "Yes"){
			minimumBound, maximumBound := byteRange.GetMinimumMaximumIdentityHashBounds()
			profileMap["MateIdentitiesRangeStart"] = minimumBound
			profileMap["MateIdentitiesRangeEnd"] = maximumBound
		}
		
		profileMap["HostingHostContent"] = hostingHostContent
		profileMap["HostingModeratorContent"] = hostingModeratorContent
		*/
	}

	if (profileType == "Mate"){

		includeAge, err := getIncludeAttributeBool()
		if (err != nil) { return nil, err }
		if (includeAge == true){
		
			profileAge := getRandomNumberWithinRangeAsString(18, 150)
			profileMap["Age"] = profileAge
		}

		includeHeight, err := getIncludeAttributeBool()
		if (err != nil) { return nil, err }
		if (includeHeight == true){

			profileHeight := getRandomNumberWithinRangeAsString(30, 400)
			profileMap["Height"] = profileHeight
		}

		includeSex, err := getIncludeAttributeBool()
		if (err != nil) { return nil, err }
		if (includeSex == true){

			sexOptionsList := []string{"Male", "Female", "Intersex Male", "Intersex Female", "Intersex"}

			profileSex, err := helpers.GetRandomItemFromList(sexOptionsList)
			if (err != nil) { return nil, err }

			profileMap["Sex"] = profileSex
		}

		includePrimaryLocation, err := getIncludeAttributeBool()
		if (err != nil) { return nil, err }
		if (includePrimaryLocation == true){

			primaryLocationLatitude := getRandomNumberWithinRangeAsString(-90, 90)
			primaryLocationLongitude := getRandomNumberWithinRangeAsString(-180, 180)

			profileMap["PrimaryLocationLatitude"] = primaryLocationLatitude
			profileMap["PrimaryLocationLongitude"] = primaryLocationLongitude

			includePrimaryLocationCountry, err := getIncludeAttributeBool()
			if (err != nil) { return nil, err }
			if (includePrimaryLocationCountry == true){

				profileCountryIdentifier := getRandomNumberWithinRangeAsString(1, 218)

				profileMap["PrimaryLocationCountry"] = profileCountryIdentifier
			}

			includeSecondaryLocation, err := getIncludeAttributeBool()
			if (err != nil) { return nil, err }
			if (includeSecondaryLocation == true){

				secondaryLocationLatitude := getRandomNumberWithinRangeAsString(-90, 90)
				secondaryLocationLongitude := getRandomNumberWithinRangeAsString(-180, 180)

				profileMap["SecondaryLocationLatitude"] = secondaryLocationLatitude
				profileMap["SecondaryLocationLongitude"] = secondaryLocationLongitude

				includeSecondaryLocationCountry, err := getIncludeAttributeBool()
				if (err != nil) { return nil, err }
				if (includeSecondaryLocationCountry == true){

					profileCountryIdentifier := getRandomNumberWithinRangeAsString(1, 218)
				
					profileMap["SecondaryLocationCountry"] = profileCountryIdentifier
				}
			}
		}

		includeTags, err := getIncludeAttributeBool()
		if (err != nil) { return nil, err }
		if (includeTags == true){

			numberOfTags := helpers.GetRandomIntWithinRange(1, 10)

			// We use this map to make sure we don't add any duplicate tags
			tagsMap := make(map[string]struct{})

			// We use this to create the Tags attribute
			// It is a list of each tag, delimited by "+&"
			var tagsAttributeBuilder strings.Builder

			numberOfAddedTags := 0

			for {

				newTag, err := GetRandomText(40, false)
				if (err != nil) { return nil, err }

				_, exists := tagsMap[newTag]
				if (exists == true){
					continue
				}

				tagsMap[newTag] = struct{}{}

				tagsAttributeBuilder.WriteString(newTag)

				numberOfAddedTags += 1

				if (numberOfAddedTags == numberOfTags){
					break
				} else {
					tagsAttributeBuilder.WriteString("+&")
				}
			}

			tagsAttribute := tagsAttributeBuilder.String()

			profileMap["Tags"] = tagsAttribute
		}

		includePhotos, err := getIncludeAttributeBool()
		if (err != nil) { return nil, err }
		if (includePhotos == true){

			numberOfPhotos := helpers.GetRandomIntWithinRange(1, 4)

			// We use this to build the Photos attribute
			// It is a list of base64-encoded photo bytes, delimited by "+"
			var photosAttributeBuilder strings.Builder

			for i:=1; i <= numberOfPhotos; i++{

				randomJpegImageBase64, err := getRandomJpegBase64Image()
				if (err != nil) { return nil, err }

				photosAttributeBuilder.WriteString(randomJpegImageBase64)

				if (i != numberOfPhotos){
					photosAttributeBuilder.WriteString("+")
				}
			}

			photosAttribute := photosAttributeBuilder.String()

			profileMap["Photos"] = photosAttribute
		}

		//TODO: Add Questionnaire

		includeAvatar, err := getIncludeAttributeBool()
		if (err != nil) { return nil, err }
		if (includeAvatar == true){

			avatarIdentifier := getRandomNumberWithinRangeAsString(1, 3630)

			profileMap["Avatar"] = avatarIdentifier
		}

		includeSexuality, err := getIncludeAttributeBool()
		if (err != nil) { return nil, err }
		if (includeSexuality == true){

			sexualityOptionsList := []string{"Male", "Female", "Male And Female"}

			profileSexuality, err := helpers.GetRandomItemFromList(sexualityOptionsList)
			if (err != nil) { return nil, err }

			profileMap["Sexuality"] = profileSexuality
		}

		include23andMeAncestryComposition, err := getIncludeAttributeBool()
		if (err != nil) { return nil, err }
		if (include23andMeAncestryComposition == true){

			// We first add all locations with no sublocations to the maps
			// We then add random amounts to the locations

			// Map Structure: Location Name -> Percentage of total ancestry
			continentsMap := make(map[string]float64)
			regionsMap := make(map[string]float64)
			subregionsMap := make(map[string]float64)

			continentsList := companyAnalysis.GetAncestryContinentsList_23andMe()

			for _, continentName := range continentsList{

				continentRegionsList, err := companyAnalysis.GetAncestryContinentRegionsList_23andMe(continentName)
				if (err != nil) { return nil, err }

				if (len(continentRegionsList) == 0){

					continentsMap[continentName] = 0
					continue
				}

				for _, regionName := range continentRegionsList{

					subregionsList, err := companyAnalysis.GetAncestryRegionSubregionsList_23andMe(continentName, regionName)
					if (err != nil) { return nil, err }
					if (len(subregionsList) == 0){
						regionsMap[regionName] = 0
						continue
					}
					for _, subregionName := range subregionsList{

						subregionsMap[subregionName] = 0
					}
				}
			}

			continentNamesList := helpers.GetListOfMapKeys(continentsMap)
			regionNamesList := helpers.GetListOfMapKeys(regionsMap)
			subregionNamesList := helpers.GetListOfMapKeys(subregionsMap)

			percentageRemaining := 100

			for percentageRemaining > 0{

				//Outputs:
				//	-map[string]float64: Location name -> Percentage of total ancestry
				//	-[]string: List of all location type location names (map keys)
				//	-error
				getRandomLocationTypeMapAndLocationsList := func()(map[string]float64, []string){

					randomInt := helpers.GetRandomIntWithinRange(1, 3)

					if (randomInt == 1){
						return continentsMap, continentNamesList
					} else if (randomInt == 2){
						return regionsMap, regionNamesList
					}

					return subregionsMap, subregionNamesList
				}

				locationTypeMap, locationTypeNamesList := getRandomLocationTypeMapAndLocationsList()
				
				randomLocationName, err := helpers.GetRandomItemFromList(locationTypeNamesList)
				if (err != nil) { return nil, err }

				percentageToAdd := helpers.GetRandomIntWithinRange(1, percentageRemaining)

				locationTypeMap[randomLocationName] += float64(percentageToAdd)

				percentageRemaining -= percentageToAdd
			}

			inputsAreValid, ancestryAttribute, err := companyAnalysis.CreateAncestryCompositionAttribute_23andMe(continentsMap, regionsMap, subregionsMap)
			if (err != nil) { return nil, err }
			if (inputsAreValid == false){
				return nil, errors.New("Ancestry composition location maps are invalid when creating fake profile.")
			}

			profileMap["23andMe_AncestryComposition"] = ancestryAttribute
		}

		include23andMeNeanderthalVariants, err := getIncludeAttributeBool()
		if (err != nil) { return nil, err }
		if (include23andMeNeanderthalVariants == true){

			neanderthalVariants := getRandomNumberWithinRangeAsString(0, 7462)

			profileMap["23andMe_NeanderthalVariants"] = neanderthalVariants
		}

		include23andMeMaternalHaplogroup, err := getIncludeAttributeBool()
		if (err != nil) { return nil, err }
		if (include23andMeMaternalHaplogroup == true){

			maternalHaplogroup, err := GetRandomText(25, false)
			if (err != nil){ return nil, err }

			profileMap["23andMe_MaternalHaplogroup"] = maternalHaplogroup
		}

		include23andMePaternalHaplogroup, err := getIncludeAttributeBool()
		if (err != nil) { return nil, err }
		if (include23andMePaternalHaplogroup == true){

			paternalHaplogroup, err := GetRandomText(25, false)
			if (err != nil){ return nil, err }

			profileMap["23andMe_PaternalHaplogroup"] = paternalHaplogroup
		}

		includeBodyFat, err := getIncludeAttributeBool()
		if (err != nil) { return nil, err }
		if (includeBodyFat == true){

			bodyFat := getRandomNumberWithinRangeAsString(1, 4)

			profileMap["BodyFat"] = bodyFat
		}
		includeBodyMuscle, err := getIncludeAttributeBool()
		if (err != nil) { return nil, err }
		if (includeBodyMuscle == true){

			bodyMuscle := getRandomNumberWithinRangeAsString(1, 4)

			profileMap["BodyMuscle"] = bodyMuscle
		}

		includeEyeColor, err := getIncludeAttributeBool()
		if (err != nil) { return nil, err }
		if (includeEyeColor == true){

			//TODO: Combined colors
			eyeColorOptionsList := []string{"Blue", "Green", "Amber", "Brown"}

			eyeColor, err := helpers.GetRandomItemFromList(eyeColorOptionsList)
			if (err != nil) { return nil, err }

			profileMap["EyeColor"] = eyeColor
		}
		includeHairColor, err := getIncludeAttributeBool()
		if (err != nil) { return nil, err }
		if (includeHairColor == true){

			//TODO: Combined colors
			hairColorOptionsList := []string{"Black", "Brown", "Blonde", "Orange"}

			hairColor, err := helpers.GetRandomItemFromList(hairColorOptionsList)
			if (err != nil) { return nil, err }

			profileMap["HairColor"] = hairColor
		}

		includeHairTexture, err := getIncludeAttributeBool()
		if (err != nil) { return nil, err }
		if (includeHairTexture == true){

			hairTexture := getRandomNumberWithinRangeAsString(1, 6)

			profileMap["HairTexture"] = hairTexture
		}

		includeSkinColor, err := getIncludeAttributeBool()
		if (err != nil) { return nil, err }
		if (includeSkinColor == true){

			skinColor := getRandomNumberWithinRangeAsString(1, 6)

			profileMap["SkinColor"] = skinColor
		}

		includeHasHIV, err := getIncludeAttributeBool()
		if (err != nil) { return nil, err }
		if (includeHasHIV == true){

			optionsList := []string{"Yes", "No"}
	
			hasHIV, err := helpers.GetRandomItemFromList(optionsList)
			if (err != nil) { return nil, err }

			profileMap["HasHIV"] = hasHIV
		}

		includeHasGenitalHerpes, err := getIncludeAttributeBool()
		if (err != nil) { return nil, err }
		if (includeHasGenitalHerpes == true){

			optionsList := []string{"Yes", "No"}
	
			hasGenitalHerpes, err := helpers.GetRandomItemFromList(optionsList)
			if (err != nil) { return nil, err }

			profileMap["HasGenitalHerpes"] = hasGenitalHerpes
		}

		includeHobbies, err := getIncludeAttributeBool()
		if (err != nil) { return nil, err }
		if (includeHobbies == true){

			profileHobbies, err := GetRandomText(1000, true)
			if (err != nil) { return nil, err }

			profileMap["Hobbies"] = profileHobbies
		}

		includeWealth, err := getIncludeAttributeBool()
		if (err != nil) { return nil, err }
		if (includeWealth == true){

			allCurrencyObjectsMap, err := currencies.GetCurrencyObjectsMap()
			if (err != nil) { return nil, err }

			allCurrencyCodesList := helpers.GetListOfMapKeys(allCurrencyObjectsMap)

			randomCurrencyCode, err := helpers.GetRandomItemFromList(allCurrencyCodesList)
			if (err != nil) { return nil, err }

			profileWealth := helpers.GetRandomInt64WithinRange(0, 9223372036854775806)
			profileWealthString := helpers.ConvertInt64ToString(profileWealth)
			profileWealthIsLowerBound, err := helpers.GetRandomItemFromList([]string{"Yes", "No"})
			if (err != nil) { return nil, err }

			profileMap["Wealth"] = profileWealthString
			profileMap["WealthCurrency"] = randomCurrencyCode
			profileMap["WealthIsLowerBound"] = profileWealthIsLowerBound
		}

		includeJob, err := getIncludeAttributeBool()
		if (err != nil) { return nil, err }
		if (includeJob == true){
	
			profileJob, err := GetRandomText(100, true)
			if (err != nil) { return nil, err }

			profileMap["Job"] = profileJob
		}

		foodsList := []string{"Fruit", "Vegetables", "Nuts", "Grains", "Dairy", "Seafood", "Beef", "Pork", "Poultry", "Eggs", "Beans"}

		for _, foodName := range foodsList{

			includeFood, err := getIncludeAttributeBool()
			if (err != nil) { return nil, err }
			if (includeFood == true){

				foodRating := getRandomNumberWithinRangeAsString(1, 10)
				profileMap[foodName + "Rating"] = foodRating
			}
		}
	
		includeFame, err := getIncludeAttributeBool()
		if (err != nil) { return nil, err }
		if (includeFame == true){

			profileFame := getRandomNumberWithinRangeAsString(1, 10)

			profileMap["Fame"] = profileFame
		}

		drugsList := []string{"Alcohol", "Tobacco", "Cannabis"}

		for _, drugName := range drugsList{

			includeDrug, err := getIncludeAttributeBool()
			if (err != nil) { return nil, err }
			if (includeDrug == true){

				drugFrequency := getRandomNumberWithinRangeAsString(1, 10)

				profileMap[drugName + "Frequency"] = drugFrequency
			}
		}

		//TODO: Language

		includeBeliefs, err := getIncludeAttributeBool()
		if (err != nil) { return nil, err }
		if (includeBeliefs == true){

			profileBeliefs, err := GetRandomText(1000, true)
			if (err != nil) { return nil, err }

			profileMap["Beliefs"] = profileBeliefs
		}

		includeGenderIdentity, err := getIncludeAttributeBool()
		if (err != nil) { return nil, err }
		if (includeGenderIdentity == true){

			includeCanonical, err := getIncludeAttributeBool()
			if (err != nil) { return nil, err }
			if (includeCanonical == true){

				profileGender, err := helpers.GetRandomItemFromList([]string{"Man", "Woman"})
				if (err != nil) { return nil, err }

				profileMap["GenderIdentity"] = profileGender		
			} else {

				profileGender, err := GetRandomText(50, false)
				if (err != nil) { return nil, err }

				profileMap["GenderIdentity"] = profileGender
			}
		}

		includePetsRating, err := getIncludeAttributeBool()
		if (err != nil) { return nil, err }
		if (includePetsRating == true){

			profilePetsRating := getRandomNumberWithinRangeAsString(1, 10)

			profileMap["PetsRating"] = profilePetsRating
		}

		includeDogsRating, err := getIncludeAttributeBool()
		if (err != nil) { return nil, err }
		if (includeDogsRating == true){

			profileDogsRating := getRandomNumberWithinRangeAsString(1, 10)

			profileMap["DogsRating"] = profileDogsRating
		}

		includeCatsRating, err := getIncludeAttributeBool()
		if (err != nil) { return nil, err }
		if (includeCatsRating == true){

			profileCatsRating := getRandomNumberWithinRangeAsString(1, 10)

			profileMap["CatsRating"] = profileCatsRating
		}

		includeMonogenicDiseaseProbabilities, err := getIncludeAttributeBool()
		if (err != nil) { return nil, err }
		if (includeMonogenicDiseaseProbabilities == true){

			monogenicDiseaseObjectsList, err := monogenicDiseases.GetMonogenicDiseaseObjectsList()
			if (err != nil) { return nil, err }

			for _, diseaseObject := range monogenicDiseaseObjectsList{

				includeDisease, err := getIncludeAttributeBool()
				if (err != nil) { return nil, err }
				if (includeDisease == false){
					continue
				}

				monogenicDiseaseName := diseaseObject.DiseaseName
				diseaseVariantsList := diseaseObject.VariantsList

				diseaseNameWithUnderscores := strings.ReplaceAll(monogenicDiseaseName, " ", "_")

				probabilityOfPassingAVariantAttributeName := "MonogenicDisease_" + diseaseNameWithUnderscores + "_ProbabilityOfPassingAVariant"
				numberOfVariantsTestedAttributeName := "MonogenicDisease_" + diseaseNameWithUnderscores + "_NumberOfVariantsTested"

				probabilityOfPassingAVariantString, err := helpers.GetRandomItemFromList([]string{"0", "50", "100"})
				if (err != nil) { return nil, err }

				numberOfDiseaseVariants := len(diseaseVariantsList)

				numberOfVariantsTestedString := getRandomNumberWithinRangeAsString(1, numberOfDiseaseVariants)

				profileMap[probabilityOfPassingAVariantAttributeName] = probabilityOfPassingAVariantString
				profileMap[numberOfVariantsTestedAttributeName] = numberOfVariantsTestedString
			}
		}

		includeRSIDs, err := getIncludeAttributeBool()
		if (err != nil) { return nil, err }
		if (includeRSIDs == true){

			locusBasesList := []string{"C", "A", "T", "G", "I", "D"}

			// This map will store all rsIDs for traits and polygenic diseases
			shareableRSIDsMap := make(map[int64]struct{})

			traitObjectsList, err := traits.GetTraitObjectsList()
			if (err != nil){ return nil, err }

			for _, traitObject := range traitObjectsList{

				traitLociList := traitObject.LociList

				for _, rsID  := range traitLociList{

					shareableRSIDsMap[rsID] = struct{}{}
				}
			}

			polygenicDiseaseObjectsList, err := polygenicDiseases.GetPolygenicDiseaseObjectsList()
			if (err != nil) { return nil, err }

			for _, diseaseObject := range polygenicDiseaseObjectsList{

				diseaseLociList := diseaseObject.LociList

				for _, locusRSID := range diseaseLociList{

					shareableRSIDsMap[locusRSID] = struct{}{}
				}
			}			

			for rsID, _ := range shareableRSIDsMap{

				includeLocus, err := getIncludeAttributeBool()
				if (err != nil) { return nil, err }
				if (includeLocus == false){
					continue
				}

				rsidString := helpers.ConvertInt64ToString(rsID)

				locusValueAttributeName := "LocusValue_rs" + rsidString

				baseA, err := helpers.GetRandomItemFromList(locusBasesList)
				if (err != nil) { return nil, err }

				baseB, err := helpers.GetRandomItemFromList(locusBasesList)
				if (err != nil) { return nil, err }

				locusValueAttributeValue := baseA + ";" + baseB

				profileMap[locusValueAttributeName] = locusValueAttributeValue

				locusIsPhasedAttributeName := "LocusIsPhased_rs" + rsidString

				locusIsPhased := helpers.GetRandomBool()

				locusIsPhasedString := helpers.ConvertBoolToYesOrNoString(locusIsPhased)

				profileMap[locusIsPhasedAttributeName] = locusIsPhasedString
			}
		}
	}

	fakeProfile, err := createProfiles.CreateProfile(identityPublicKey, identityPrivateKey, profileMap)
	if (err != nil) { return nil, err }

	return fakeProfile, nil
}

func GenerateFakeProfilesWithMessages(profileType string, networkType byte, numberOfProfilesToGenerate int)error{

	if (profileType != "Mate" && profileType != "Moderator"){
		return errors.New("GenerateFakeProfilesWithMessages called with invalid profile type: " + profileType)
	}

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

	for i := 0; i < numberOfProfilesToGenerate; i++ {

		newIdentityPublicKey, newIdentityPrivateKey, err := identity.GetNewRandomPublicPrivateIdentityKeys()
		if (err != nil) { return err }

		fakeProfile, err := GetFakeProfile(profileType, newIdentityPublicKey, newIdentityPrivateKey, networkType)
		if (err != nil) { return err }

		wellFormed, _, err := profileStorage.AddUserProfile(fakeProfile)
		if (err != nil) { return err }
		if (wellFormed == false){
			return errors.New("Profile to add is malformed.")
		}

		textMessage, err := getFakeMessageSentToMe(profileType, newIdentityPublicKey, newIdentityPrivateKey, networkType, "Text")
		if (err != nil) { return err }

		imageMessage, err := getFakeMessageSentToMe(profileType, newIdentityPublicKey, newIdentityPrivateKey, networkType, "Image")
		if (err != nil) { return err }

		wellFormed, err = chatMessageStorage.AddMessage(textMessage)
		if (err != nil) { return err }
		if (wellFormed == false){
			return errors.New("Message to add is malformed.")
		}

		wellFormed, err = chatMessageStorage.AddMessage(imageMessage)
		if (err != nil) { return err }
		if (wellFormed == false){
			return errors.New("Message to add is malformed.")
		}
	}

	//TODO: Approve profiles so they will be visible for the user

	return nil	
}

func GetFakeReview(reviewType string, networkType byte)([]byte, error){

	if (reviewType != "Identity" && reviewType != "Profile" && reviewType != "Attribute" && reviewType != "Message"){
		return nil, errors.New("GetFakeReview called with invalid reviewType: " + reviewType)
	}

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

	networkTypeString := helpers.ConvertByteToString(networkType)

	if (reviewType == "Identity"){

		randomIdentityHash, err := identity.GetNewRandomIdentityHash(false, "")
		if (err != nil) { return nil, err }

		randomIdentityHashString, _, err := identity.EncodeIdentityHashBytesToString(randomIdentityHash)
		if (err != nil) { return nil, err }

		verdict, err := helpers.GetRandomItemFromList([]string{"Ban", "Ban", "Ban", "Ban", "Ban", "None"})
		if (err != nil) { return nil, err }

		newReviewMap := map[string]string{
			"NetworkType": networkTypeString,
			"ReviewedHash": randomIdentityHashString,
			"Verdict": verdict,
			"Reason": "Unruleful conduct.",
		}

		newIdentityPublicKey, newIdentityPrivateKey, err := identity.GetNewRandomPublicPrivateIdentityKeys()
		if (err != nil) { return nil, err }

		reviewBytes, err := createReviews.CreateReview(newIdentityPublicKey, newIdentityPrivateKey, newReviewMap)
		if (err != nil) { return nil, err }

		return reviewBytes, nil
	}
	if (reviewType == "Profile"){

		profileType, err := helpers.GetRandomItemFromList([]string{"Mate", "Host", "Moderator"})
		if (err != nil) { return nil, err }

		profileHash, err := helpers.GetNewRandomProfileHash(true, profileType, true, false)
		if (err != nil) { return nil, err }

		profileHashString := encoding.EncodeBytesToHexString(profileHash[:])

		verdict, err := helpers.GetRandomItemFromList([]string{"Ban", "Approve", "Approve", "Approve", "Approve", "None"})
		if (err != nil) { return nil, err }

		newReviewMap := map[string]string{
			"NetworkType": networkTypeString,
			"ReviewedHash": profileHashString,
			"Verdict": verdict,
		}

		if (verdict == "Ban"){
			newReviewMap["Reason"] = "Profile is unruleful."
		} else {
			newReviewMap["Reason"] = "Profile is good."
		}

		newIdentityPublicKey, newIdentityPrivateKey, err := identity.GetNewRandomPublicPrivateIdentityKeys()
		if (err != nil) { return nil, err }

		reviewBytes, err := createReviews.CreateReview(newIdentityPublicKey, newIdentityPrivateKey, newReviewMap)
		if (err != nil) { return nil, err }		

		return reviewBytes, nil
	}
	if (reviewType == "Attribute"){

		attributeHash, err := helpers.GetNewRandomAttributeHash(false, "", false, false)
		if (err != nil) { return nil, err }

		attributeHashHex := encoding.EncodeBytesToHexString(attributeHash[:])

		verdict, err := helpers.GetRandomItemFromList([]string{"Ban", "Approve", "Approve", "Approve", "Approve", "None"})
		if (err != nil) { return nil, err }

		newReviewMap := map[string]string{
			"NetworkType": networkTypeString,
			"ReviewedHash": attributeHashHex,
			"Verdict": verdict,
		}

		if (verdict == "Ban"){
			newReviewMap["Reason"] = "Attribute is unruleful."
		} else {
			newReviewMap["Reason"] = "Attribute is good."
		}

		newIdentityPublicKey, newIdentityPrivateKey, err := identity.GetNewRandomPublicPrivateIdentityKeys()
		if (err != nil) { return nil, err }

		reviewBytes, err := createReviews.CreateReview(newIdentityPublicKey, newIdentityPrivateKey, newReviewMap)
		if (err != nil) { return nil, err }		

		return reviewBytes, nil
	}
	if (reviewType == "Message"){

		imageOrTextList := []string{"Image", "Text"}
		imageOrText, err := helpers.GetRandomItemFromList(imageOrTextList)
		if (err != nil) { return nil, err }

		_, messageHash, messageCipherKey, err := GetFakeMessage(networkType, imageOrText)
		if (err != nil) { return nil, err }

		messageHashString := encoding.EncodeBytesToHexString(messageHash[:])

		messageCipherKeyHex := encoding.EncodeBytesToHexString(messageCipherKey[:])

		verdict, err := helpers.GetRandomItemFromList([]string{"Approve", "Ban", "Approve", "Ban", "None"})
		if (err != nil) { return nil, err }

		newReviewMap := map[string]string{
			"NetworkType": networkTypeString,
			"ReviewedHash": messageHashString,
			"MessageCipherKey": messageCipherKeyHex,
			"Verdict": verdict,
		}

		if (verdict == "Approve"){

			newReviewMap["Reason"] = "Message is ruleful."

		} else if (verdict == "Ban"){

			newReviewMap["Reason"] = "Message is unruleful."
		}

		newIdentityPublicKey, newIdentityPrivateKey, err := identity.GetNewRandomPublicPrivateIdentityKeys()
		if (err != nil) { return nil, err }

		reviewBytes, err := createReviews.CreateReview(newIdentityPublicKey, newIdentityPrivateKey, newReviewMap)
		if (err != nil) { return nil, err }		

		return reviewBytes, nil
	}

	return nil, errors.New("GetFakeReview called with invalid reviewType: " + reviewType)
}


// This function will generate fake moderator reviews for content stored in the database
func GenerateFakeReviews(reviewType string, networkType byte, reviewsToGenerate int)error{

	if (reviewType != "Identity" && reviewType != "Profile" && reviewType != "Attribute" && reviewType != "Message"){
		return errors.New("GenerateFakeReviews called with invalid reviewType: " + reviewType)
	}

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

	networkTypeString := helpers.ConvertByteToString(networkType)

	if (reviewType == "Identity"){

		storedIdentitiesList, err := profileStorage.GetAllStoredProfileIdentityHashes()
		if (err != nil) { return err }

		if (len(storedIdentitiesList) == 0) {
			return errors.New("No identities to create reviews for.")
		}

		for i := 0; i < reviewsToGenerate; i++ {

			randomIdentityHash, err := helpers.GetRandomItemFromList(storedIdentitiesList)
			if (err != nil) { return err }

			randomIdentityHashString, _, err := identity.EncodeIdentityHashBytesToString(randomIdentityHash)
			if (err != nil) { return err }

			// Generates 3 reviews for each identity
			for n := 0; n < 3; n++{

				newIdentityPublicKey, newIdentityPrivateKey, err := identity.GetNewRandomPublicPrivateIdentityKeys()
				if (err != nil) { return err }

				newProfile, err := GetFakeProfile("Moderator", newIdentityPublicKey, newIdentityPrivateKey, networkType)
				if (err != nil) { return err }

				wellFormed, _, err := profileStorage.AddUserProfile(newProfile)
				if (err != nil) { return err }
				if (wellFormed == false){
					return errors.New("Profile to add is malformed.")
				}

				verdict, err := helpers.GetRandomItemFromList([]string{"Ban", "Ban", "Ban", "Ban", "Ban", "None"})
				if (err != nil) { return err }

				newReviewMap := map[string]string{
					"NetworkType": networkTypeString,
					"ReviewedHash": randomIdentityHashString,
					"Verdict": verdict,
					"Reason": "Unruleful content.",
				}

				reviewBytes, err := createReviews.CreateReview(newIdentityPublicKey, newIdentityPrivateKey, newReviewMap)
				if (err != nil) { return err }

				wellFormed, err = reviewStorage.AddReview(reviewBytes)
				if (err != nil) { return err }
				if (wellFormed == false){
					return errors.New("Review to add is malformed.")
				}
			}
		}

		return nil
	}
	if (reviewType == "Profile" || reviewType == "Attribute"){

		allStoredProfileHashes, err := profileStorage.GetAllStoredProfileHashes()
		if (err != nil) { return err }

		if (len(allStoredProfileHashes) == 0) {
			return errors.New("No profiles to generate reviews for.")
		}

		helpers.RandomizeListOrder(allStoredProfileHashes)

		numberOfReviewsGenerated := 0

		// This list will store eligible profiles to review
		// Some profiles cannot be reviewed
		// Examples include disabled profiles, or profiles that belong to a different network type
		eligibleProfileHashesList := make([][28]byte, 0)

		// Map Structure: Profile hash -> Random profile attribute hash
		// Map will not contain any entries if reviewType == "Profile"
		eligibleProfileAttributeHashesMap := make(map[[28]byte][27]byte)

		for _, profileHash := range allStoredProfileHashes{

			metadataExists, _, profileNetworkType, _, _, profileIsDisabled, _, profileAttributeHashesMap, err := contentMetadata.GetProfileMetadata(profileHash)
			if (err != nil) { return err }
			if (metadataExists == false){
				// Profile must have been deleted after list was generated. Skip the profile.
				continue
			}
			if (profileNetworkType != networkType){
				// Profile was created for a different network type.
				continue
			}
			if (profileIsDisabled == true){
				// We cannot review disabled profiles
				continue
			}

			// Profile is eligible for review

			eligibleProfileHashesList = append(eligibleProfileHashesList, profileHash)

			if (reviewType == "Attribute"){

				// We get random profile attribute hash

				// This list stores all attributeHashes to review.
				allProfileAttributeHashesList := helpers.GetListOfMapValues(profileAttributeHashesMap)

				attributeHashToReview, err := helpers.GetRandomItemFromList(allProfileAttributeHashesList)
				if (err != nil) { return err }

				eligibleProfileAttributeHashesMap[profileHash] = attributeHashToReview
			}
		}

		if (len(eligibleProfileHashesList) == 0){
			return errors.New("No eligible profiles to generate reviews for.")
		}

		for numberOfReviewsGenerated < reviewsToGenerate {

			randomProfileHash, err := helpers.GetRandomItemFromList(eligibleProfileHashesList)
			if (err != nil) { return err }

			getRandomHashToReview := func()(string, error){

				if (reviewType == "Profile"){

					randomProfileHashHex := encoding.EncodeBytesToHexString(randomProfileHash[:])

					return randomProfileHashHex, nil
				}

				profileRandomAttributeHash, exists := eligibleProfileAttributeHashesMap[randomProfileHash]
				if (exists == false){
					return "", errors.New("profileAttributeHashesMap missing random profile hash entry.")
				}

				hashToReviewHex := encoding.EncodeBytesToHexString(profileRandomAttributeHash[:])

				return hashToReviewHex, nil
			}

			hashToReview, err := getRandomHashToReview()
			if (err != nil) { return err }

			// We generate 3 reviews for each profile/attribute
			for i:= 0; i < 3; i++{

				newIdentityPublicKey, newIdentityPrivateKey, err := identity.GetNewRandomPublicPrivateIdentityKeys()
				if (err != nil) { return err }

				newProfile, err := GetFakeProfile("Moderator", newIdentityPublicKey, newIdentityPrivateKey, networkType)
				if (err != nil) { return err }

				wellFormed, _, err := profileStorage.AddUserProfile(newProfile)
				if (err != nil) { return err }
				if (wellFormed == false){
					return errors.New("Profile to add is malformed.")
				}

				verdict, err := helpers.GetRandomItemFromList([]string{"Ban", "Approve", "Approve", "Approve", "Approve", "None"})
				if (err != nil) { return err }

				newReviewMap := map[string]string{
					"NetworkType": networkTypeString,
					"ReviewedHash": hashToReview,
					"Verdict": verdict,
				}

				if (verdict == "Ban"){
					newReviewMap["Reason"] = reviewType + " is unruleful."
				} else {
					newReviewMap["Reason"] = reviewType + " is good."
				}

				reviewBytes, err := createReviews.CreateReview(newIdentityPublicKey, newIdentityPrivateKey, newReviewMap)
				if (err != nil) { return err }

				wellFormed, err = reviewStorage.AddReview(reviewBytes)
				if (err != nil) { return err }
				if (wellFormed == false){
					return errors.New("Review to add is malformed.")
				}
			}

			numberOfReviewsGenerated += 1
		}

		return nil	
	}

	if (reviewType == "Message"){

		for i := 0; i < reviewsToGenerate; i++ {

			imageOrTextList := []string{"Image", "Text"}
			imageOrText, err := helpers.GetRandomItemFromList(imageOrTextList)
			if (err != nil) { return err }

			messageBytes, messageHash, messageCipherKey, err := GetFakeMessage(networkType, imageOrText)
			if (err != nil) { return err }

			messageHashString := encoding.EncodeBytesToHexString(messageHash[:])

			messageCipherKeyHex := encoding.EncodeBytesToHexString(messageCipherKey[:])

			// Generates 3 reviews for each message
			for n := 0; n < 3; n++{

				newIdentityPublicKey, newIdentityPrivateKey, err := identity.GetNewRandomPublicPrivateIdentityKeys()
				if (err != nil) { return err }

				newProfile, err := GetFakeProfile("Moderator", newIdentityPublicKey, newIdentityPrivateKey, networkType)
				if (err != nil) { return err }

				wellFormed, _, err := profileStorage.AddUserProfile(newProfile)
				if (err != nil) { return err }
				if (wellFormed == false){
					return errors.New("Profile to add is malformed.")
				}

				verdict, err := helpers.GetRandomItemFromList([]string{"Approve", "Ban", "Approve", "Ban", "None"})
				if (err != nil) { return err }

				newReviewMap := map[string]string{
					"NetworkType": networkTypeString,
					"ReviewedHash": messageHashString,
					"Verdict": verdict,
					"Reason": "Message is unruleful",
					"MessageCipherKey": messageCipherKeyHex,
				}

				reviewBytes, err := createReviews.CreateReview(newIdentityPublicKey, newIdentityPrivateKey, newReviewMap)
				if (err != nil) { return err }		

				wellFormed, err = reviewStorage.AddReview(reviewBytes)
				if (err != nil) { return err }
				if (wellFormed == false){
					return errors.New("Review to add is malformed.")
				}
			}

			wellFormed, err := chatMessageStorage.AddMessage(messageBytes)
			if (err != nil) { return err }
			if (wellFormed == false){
				return errors.New("Message to add is malformed.")
			}
		}

		return nil
	}

	return errors.New("GenerateFakeReviews called with invalid reviewType: " + reviewType)
}


// This function will create reports for existing identities, profiles, attributes, and messages, and add them to the database.
func GenerateFakeReports(reportType string, networkType byte, reportsToGenerate int)error{

	if (reportType != "Identity" && reportType != "Profile" && reportType != "Attribute" && reportType != "Message"){
		return errors.New("GenerateFakeReports called with invalid reportType: " + reportType)
	}

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

	networkTypeString := helpers.ConvertByteToString(networkType)

	if (reportType == "Identity"){

		storedIdentitiesList, err := profileStorage.GetAllStoredProfileIdentityHashes()
		if (err != nil) { return err }

		if (len(storedIdentitiesList) == 0) {
			return errors.New("No identities to create reports for.")
		}

		for i := 0; i < reportsToGenerate; i++ {

			randomIdentityHash, err := helpers.GetRandomItemFromList(storedIdentitiesList)
			if (err != nil) { return err }

			randomIdentityHashString, _, err := identity.EncodeIdentityHashBytesToString(randomIdentityHash)
			if (err != nil) { return err }

			newReportMap := map[string]string{
				"NetworkType": networkTypeString,
				"ReportedHash": randomIdentityHashString,
				"Reason": "Bad Conduct.",
			}

			reportBytes, err := createReports.CreateReport(newReportMap)
			if (err != nil) { return err }

			wellFormed, err := reportStorage.AddReport(reportBytes)
			if (err != nil) { return err }
			if (wellFormed == false){
				return errors.New("Report to add is malformed.")
			}
		}
		
		return nil
	}
	if (reportType == "Profile" || reportType == "Attribute"){

		allStoredProfileHashes, err := profileStorage.GetAllStoredProfileHashes()
		if (err != nil) { return err }

		if (len(allStoredProfileHashes) == 0) {
			return errors.New("No profiles to generate reports for.")
		}

		// This list will store eligible profiles to report
		// Some profiles cannot be reported
		// Examples include disabled profiles, or profiles that belong to a different network type
		eligibleProfileHashesList := make([][28]byte, 0)

		// Map Structure: Profile hash -> Random profile attribute hash
		// Map will not contain any entries if reportType == "Profile"
		eligibleProfileAttributeHashesMap := make(map[[28]byte][27]byte)

		for _, profileHash := range allStoredProfileHashes{

			metadataExists, _, profileNetworkType, _, _, profileIsDisabled, _, profileAttributeHashesMap, err := contentMetadata.GetProfileMetadata(profileHash)
			if (err != nil) { return err }
			if (metadataExists == false){
				// Profile must have been deleted after list was generated. Skip the profile.
				continue
			}
			if (profileNetworkType != networkType){
				// Profile was created for a different network type.
				continue
			}
			if (profileIsDisabled == true){
				// We cannot review disabled profiles
				continue
			}

			// Profile is eligible to be reported

			eligibleProfileHashesList = append(eligibleProfileHashesList, profileHash)

			if (reportType == "Attribute"){

				// We get random profile attribute hash

				// This list stores all attributeHashes to review.
				allProfileAttributeHashesList := helpers.GetListOfMapValues(profileAttributeHashesMap)

				attributeHashToReport, err := helpers.GetRandomItemFromList(allProfileAttributeHashesList)
				if (err != nil) { return err }

				eligibleProfileAttributeHashesMap[profileHash] = attributeHashToReport
			}
		}

		if (len(eligibleProfileHashesList) == 0){
			return errors.New("No eligible profiles to generate reports for.")
		}

		numberOfReportsGenerated := 0

		for numberOfReportsGenerated < reportsToGenerate {

			randomProfileHash, err := helpers.GetRandomItemFromList(eligibleProfileHashesList)
			if (err != nil) { return err }

			getRandomHashToReport := func()(string, error){

				if (reportType == "Profile"){

					randomProfileHashHex := encoding.EncodeBytesToHexString(randomProfileHash[:])

					return randomProfileHashHex, nil
				}

				attributeHashToReport, exists := eligibleProfileAttributeHashesMap[randomProfileHash]
				if (exists == false){
					return "", errors.New("eligibleProfileAttributeHashesMap missing random profile hash.")
				}

				attributeHashToReportHex := encoding.EncodeBytesToHexString(attributeHashToReport[:])

				return attributeHashToReportHex, nil
			}

			hashToReport, err := getRandomHashToReport()
			if (err != nil) { return err }

			newReportMap := map[string]string{
				"NetworkType": networkTypeString,
				"ReportedHash": hashToReport,
				"Reason": reportType + " is unruleful.",
			}

			reportBytes, err := createReports.CreateReport(newReportMap)
			if (err != nil) { return err }

			wellFormed, err := reportStorage.AddReport(reportBytes)
			if (err != nil) { return err }
			if (wellFormed == false){
				return errors.New("Report to add is malformed.")
			}

			numberOfReportsGenerated += 1
		}

		return nil
	}

	if (reportType == "Message"){
	
		for i := 0; i < reportsToGenerate; i++ {

			textOrImage, err := helpers.GetRandomItemFromList([]string{"Text", "Image"})
			if (err != nil) { return err }

			messageBytes, messageHash, messageCipherKey, err := GetFakeMessage(networkType, textOrImage)
			if (err != nil) { return err }

			messageHashString := encoding.EncodeBytesToHexString(messageHash[:])

			messageCipherKeyHex := encoding.EncodeBytesToHexString(messageCipherKey[:])

			newReportMap := map[string]string{
				"NetworkType": networkTypeString,
				"ReportedHash": messageHashString,
				"MessageCipherKey": messageCipherKeyHex,
				"Reason": "Message is unruleful",
			}

			reportBytes, err := createReports.CreateReport(newReportMap)
			if (err != nil) { return err }

			wellFormed, err := reportStorage.AddReport(reportBytes)
			if (err != nil) { return err }
			if (wellFormed == false){
				return errors.New("Report to add is malformed.")
			}

			wellFormed, err = chatMessageStorage.AddMessage(messageBytes)
			if (err != nil) { return err }
			if (wellFormed == false){
				return errors.New("Message to add is malformed.")
			}
		}

		return nil
	}

	return errors.New("GenerateFakeReports called with invalid reportType: " + reportType)
}


func GetFakeReport(reportType string, networkType byte)([]byte, error){

	if (reportType != "Identity" && reportType != "Profile" && reportType != "Attribute" && reportType != "Message"){
		return nil, errors.New("GetFakeReport called with invalid reportType: " + reportType)
	}

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

	networkTypeString := helpers.ConvertByteToString(networkType)

	if (reportType == "Identity"){

		randomIdentityHash, err := identity.GetNewRandomIdentityHash(false, "")
		if (err != nil) { return nil, err }

		randomIdentityHashString, _, err := identity.EncodeIdentityHashBytesToString(randomIdentityHash)
		if (err != nil) { return nil, err }

		newReportMap := map[string]string{
			"NetworkType": networkTypeString,
			"ReportedHash": randomIdentityHashString,
			"Reason": "Unruleful conduct.",
		}

		reportBytes, err := createReports.CreateReport(newReportMap)
		if (err != nil) { return nil, err }

		return reportBytes, nil
	}
	if (reportType == "Profile"){

		profileHash, err := helpers.GetNewRandomProfileHash(false, "", true, false)
		if (err != nil) { return nil, err }

		profileHashString := encoding.EncodeBytesToHexString(profileHash[:])

		newReportMap := map[string]string{
			"NetworkType": networkTypeString,
			"ReportedHash": profileHashString,
			"Reason": "Profile is unruleful",
		}

		reportBytes, err := createReports.CreateReport(newReportMap)
		if (err != nil) { return nil, err }

		return reportBytes, nil
	}
	if (reportType == "Attribute"){

		attributeHash, err := helpers.GetNewRandomAttributeHash(false, "", false, false)
		if (err != nil) { return nil, err }

		attributeHashString := encoding.EncodeBytesToHexString(attributeHash[:])

		newReportMap := map[string]string{
			"NetworkType": networkTypeString,
			"ReportedHash": attributeHashString,
			"Reason": "Attribute is unruleful",
		}

		reportBytes, err := createReports.CreateReport(newReportMap)
		if (err != nil) { return nil, err }	

		return reportBytes, nil
	}

	if (reportType == "Message"){

		textOrImage, err := helpers.GetRandomItemFromList([]string{"Text", "Image"})
		if (err != nil) { return nil, err }

		_, messageHash, messageCipherKey, err := GetFakeMessage(networkType, textOrImage)
		if (err != nil) { return nil, err }

		messageHashHex := encoding.EncodeBytesToHexString(messageHash[:])

		messageCipherKeyHex := encoding.EncodeBytesToHexString(messageCipherKey[:])

		newReportMap := map[string]string{
			"NetworkType": networkTypeString,
			"ReportedHash": messageHashHex,
			"MessageCipherKey": messageCipherKeyHex,
			"Reason": "Message is unruleful",
		}

		reportBytes, err := createReports.CreateReport(newReportMap)
		if (err != nil) { return nil, err }

		return reportBytes, nil
	}

	return nil, errors.New("GetFakeReport called with invalid reportType: " + reportType)
}

// For below function, we read random base64 images into a list in memory
// Then they can be read without having to generate the Base64 jpegs again

var jpegBase64ImagesList []string

func getRandomJpegBase64Image()(string, error){

	if (jpegBase64ImagesList == nil){

		jpegBase64ImagesList = make([]string, 0, 5)

		addImageToList := func(imageBytes []byte)error{

			imageObject, err := imagery.ConvertPNGImageFileBytesToGolangImage(imageBytes)
			if (err != nil) { return err }

			imageBase64, err := imagery.ConvertImageObjectToStandardJPEGBase64String(imageObject)
			if (err != nil) { return err }

			jpegBase64ImagesList = append(jpegBase64ImagesList, imageBase64)

			return nil
		}

		image1Bytes := imageFiles.PNG_Pumpkin
		image2Bytes := imageFiles.PNG_SmileyA
		image3Bytes := imageFiles.PNG_SmileyB
		image4Bytes := imageFiles.PNG_Ultrawide
		image5Bytes := imageFiles.PNG_Ultrathin

		err := addImageToList(image1Bytes)
		if (err != nil) { return "", err }
		err = addImageToList(image2Bytes)
		if (err != nil) { return "", err }
		err = addImageToList(image3Bytes)
		if (err != nil) { return "", err }
		err = addImageToList(image4Bytes)
		if (err != nil) { return "", err }
		err = addImageToList(image5Bytes)
		if (err != nil) { return "", err }
	}

	randomImageString, err := helpers.GetRandomItemFromList(jpegBase64ImagesList)
	if (err != nil) { return "", err }

	return randomImageString, nil
}

//Outputs:
//	-[]byte: Message Bytes
//	-[26]byte: Message Hash
//	-[32]byte: Message Cipher Key
//	-error
func GetFakeMessage(networkType byte, textOrImage string)([]byte, [26]byte, [32]byte, error){

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

	if (textOrImage != "Text" && textOrImage != "Image"){
		return nil, [26]byte{}, [32]byte{}, errors.New("GetFakeMessage called with invalid textOrImage: " + textOrImage)
	}

	identityType, err := helpers.GetRandomItemFromList([]string{"Mate", "Moderator"})
	if (err != nil) { return nil, [26]byte{}, [32]byte{}, err }

	senderIdentityPublicKey, senderIdentityPrivateKey, err := identity.GetNewRandomPublicPrivateIdentityKeys()
	if (err != nil) { return  nil, [26]byte{}, [32]byte{}, err }

	senderIdentityHash, err := identity.ConvertIdentityKeyToIdentityHash(senderIdentityPublicKey, identityType)
	if (err != nil) { return nil, [26]byte{}, [32]byte{}, err }

	messageCreationTime := time.Now().Unix()

	senderCurrentSecretInboxSeed, err := inbox.GetNewRandomSecretInboxSeed()
	if (err != nil) { return nil, [26]byte{}, [32]byte{}, err }

	senderNextSecretInboxSeed, err := inbox.GetNewRandomSecretInboxSeed()
	if (err != nil) { return nil, [26]byte{}, [32]byte{}, err }

	senderDeviceIdentifier, err := helpers.GetNewRandomDeviceIdentifier()
	if (err != nil) { return nil, [26]byte{}, [32]byte{}, err }

	senderChatKeysLatestUpdateTime := time.Now().Unix()

	getMessageCommunication := func()(string, error){

		if (textOrImage == "Text"){

			randomText, err := GetRandomText(200, true)
			if (err != nil) { return "", err }
			communication := "Hello! " + randomText
			return communication, nil
		}

		imageBase64, err := getRandomJpegBase64Image()
		if (err != nil) { return "", err }

		communication := ">!>Photo=" + imageBase64

		return communication, nil
	}

	messageCommunication, err := getMessageCommunication()
	if (err != nil) { return nil, [26]byte{}, [32]byte{}, err }

	recipientIdentityHash, err := identity.GetNewRandomIdentityHash(true, identityType)
	if (err != nil) { return nil, [26]byte{}, [32]byte{}, err }

	doubleSealedKeysSealerKey, err := helpers.GetNewRandom32ByteArray()
	if (err != nil) { return nil, [26]byte{}, [32]byte{}, err }

	recipientNaclPublicKey, recipientNaclPrivateKey, err := nacl.GetNewRandomPublicPrivateNaclKeys()
	if (err != nil) { return nil, [26]byte{}, [32]byte{}, err }
	recipientMlkemPublicKey, recipientMlkemPrivateKey, err := mlkem.GetNewRandomPublicPrivateMlkemKeys()
	if (err != nil) { return nil, [26]byte{}, [32]byte{}, err }

	recipientCsidhPublicKey, recipientCsidhPrivateKey, err := csidh.GetNewRandomPublicPrivateCsidhKeys()
	if (err != nil) { return nil, [26]byte{}, [32]byte{}, err }

	recipientDecryptionKeySetObject := readMessages.ChatKeySet{
		NaclPublicKey: recipientNaclPublicKey,
		NaclPrivateKey: recipientNaclPrivateKey,
		MlkemPrivateKey: recipientMlkemPrivateKey,
		CsidhPrivateKey: recipientCsidhPrivateKey,
	}

	recipientChatDecryptionKeySetsList := []readMessages.ChatKeySet{recipientDecryptionKeySetObject}

	recipientInbox, err := inbox.GetPublicInboxFromIdentityHash(recipientIdentityHash)
	if (err != nil) { return nil, [26]byte{}, [32]byte{}, err }

	newMessage, _, err := createMessages.CreateChatMessage(networkType, senderIdentityHash, senderIdentityPublicKey, senderIdentityPrivateKey, messageCreationTime, senderCurrentSecretInboxSeed, senderNextSecretInboxSeed, senderDeviceIdentifier, senderChatKeysLatestUpdateTime, messageCommunication, recipientIdentityHash, recipientInbox, recipientNaclPublicKey, recipientMlkemPublicKey, recipientCsidhPublicKey, doubleSealedKeysSealerKey)
	if (err != nil) { return nil, [26]byte{}, [32]byte{}, err }

	ableToRead, messageHash, _, _, messageCipherKey, _, _, _, _, _, _, _, _, err := readMessages.ReadChatMessage(newMessage, doubleSealedKeysSealerKey, recipientChatDecryptionKeySetsList)
	if (err != nil) { return nil, [26]byte{}, [32]byte{}, err }
	if (ableToRead == false) {
		return nil, [26]byte{}, [32]byte{}, errors.New("Unable to read chat message during generate.")
	}

	return newMessage, messageHash, messageCipherKey, nil	
}

func getFakeMessageSentToMe(identityType string, senderIdentityPublicKey [32]byte, senderIdentityPrivateKey [64]byte, networkType byte, textOrImage string)([]byte, error){

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

	if (textOrImage != "Text" && textOrImage != "Image"){
		return nil, errors.New("getFakeMessageSentToMe called with invalid textOrImage: " + textOrImage)	
	}

	senderIdentityHash, err := identity.ConvertIdentityKeyToIdentityHash(senderIdentityPublicKey, identityType)
	if (err != nil) { return nil, err }

	messageCreationTime := time.Now().Unix()

	senderLatestChatKeysUpdateTime := time.Now().Unix()

	senderCurrentSecretInboxSeed, err := inbox.GetNewRandomSecretInboxSeed()
	if (err != nil) { return nil, err }

	senderNextSecretInboxSeed, err := inbox.GetNewRandomSecretInboxSeed()
	if (err != nil) { return nil, err }

	senderDeviceIdentifier, err := helpers.GetNewRandomDeviceIdentifier()
	if (err != nil) { return nil, err }

	getMessageCommunication := func()(string, error){

		if (textOrImage == "Text"){

			//TODO: Greet/reject

			randomText, err := GetRandomText(300, true)
			if (err != nil) { return "", err }

			communication := "Hello " + identityType + "! " + randomText

			return communication, nil
		}

		imageBase64, err := getRandomJpegBase64Image()
		if (err != nil) { return "", err }

		communication := ">!>Photo=" + imageBase64

		return communication, nil
	}

	messageCommunication, err := getMessageCommunication()
	if (err != nil) { return nil, err }

	exists, myIdentityHash, err := myIdentity.GetMyIdentityHash(identityType)
	if (err != nil) { return nil, err }
	if (exists == false) {
		return nil, errors.New("getFakeMessageSentToMe called when my " + identityType + " identity does not exist.")
	}

	myInbox, err := inbox.GetPublicInboxFromIdentityHash(myIdentityHash)
	if (err != nil) { return nil, err }

	myInboxDoubleSealedKeysSealerKey, err := inbox.GetPublicInboxSealerKeyFromIdentityHash(myIdentityHash)
	if (err != nil) { return nil, err }

	myIdentityExists, myNaclKey, myMlkemKey, myCsidhKey, err := myChatKeys.GetMyNewestPublicChatKeys(myIdentityHash, networkType)
	if (err != nil) { return nil, err }
	if (myIdentityExists == false) {
		return nil, errors.New("My identity not found after being found already.")
	}

	newMessage, _, err := createMessages.CreateChatMessage(networkType, senderIdentityHash, senderIdentityPublicKey, senderIdentityPrivateKey, messageCreationTime, senderCurrentSecretInboxSeed, senderNextSecretInboxSeed, senderDeviceIdentifier, senderLatestChatKeysUpdateTime, messageCommunication, myIdentityHash, myInbox, myNaclKey, myMlkemKey, myCsidhKey, myInboxDoubleSealedKeysSealerKey)
	if (err != nil) { return nil, err }

	return newMessage, nil
}


