
// myDesireStatistics provides functions to calculate a user's mate desire statistics
// These are statistics about a user's mate desires.
// For each desire, we calculate:
//	-The number/percentage of all downloaded profiles who pass the desire
//	-The number/percentage of a user's matches who pass the desire (excluding the desire)

package myDesireStatistics

//TODO: Add desire statistics about how many users fulfill each desire
// This would enable users to view fulfillment statistics without requiring them to enable FilterAll and RequireResponse for each desire

import "seekia/internal/appMemory"
import "seekia/internal/badgerDatabase"
import "seekia/internal/desires/myMateDesires"
import "seekia/internal/helpers"
import "seekia/internal/myBlockedUsers"
import "seekia/internal/myIdentity"
import "seekia/internal/profiles/viewableProfiles"

import "errors"

//Outputs:
//	-int64: Number of downloaded mate identities with a viewable profile
//	-int64:	Number of mate identities whose viewable profile passes desire
//	-float64: Percentage of all mate identities whose viewable profile passes desire
//	-int64: Number of matches (Excluding input desire)
//	-int64: Number of matches
//	-float64: Percentage of all matches (excluding input desire) that pass input desire
//	-error
func GetMyDesireStatistics(desireName string, networkType byte)(int64, int64, float64, int64, int64, float64, error){

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

	myIdentityExists, myIdentityHash, err := myIdentity.GetMyIdentityHash("Mate")
	if (err != nil) { return 0, 0, 0, 0, 0, 0, err }

	mateIdentityHashesList, err := badgerDatabase.GetAllProfileIdentityHashes("Mate")
	if (err != nil) { return 0, 0, 0, 0, 0, 0, err }

	numberOfMateIdentities := int64(0)
	numberOfMateIdentitiesWhoPassDesire := int64(0)
	numberOfMatches := int64(0)
	numberOfMatchesExcludingDesire := int64(0)

	for _, peerIdentityHash := range mateIdentityHashesList{

		if (myIdentityExists == true && peerIdentityHash == myIdentityHash){
			continue
		}

		userIsBlocked, _, _, _, err := myBlockedUsers.CheckIfUserIsBlocked(peerIdentityHash)
		if (err != nil) { return 0, 0, 0, 0, 0, 0, err }
		if (userIsBlocked == true){
			// We don't include blocked users in our statistics calculation
			// This is because "Blocked" is not a desire, so it won't be shown on the Desire Statistics page
			// We show the user how many users they have blocked on the Match Statistics page
			continue
		}

		profileExists, _, getAnyProfileAttributeFunction, err := viewableProfiles.GetRetrieveAnyNewestViewableUserProfileAttributeFunction(peerIdentityHash, networkType, true, false, true)
		if (err != nil) { return 0, 0, 0, 0, 0, 0, err }
		if (profileExists == false) {
			// Profile must have been deleted, or user's profile is not viewable
			continue
		}

		exists, _, _, err := getAnyProfileAttributeFunction("Disabled")
		if (err != nil) { return 0, 0, 0, 0, 0, 0, err }
		if (exists == true){
			// Profile is disabled
			continue
		}

		numberOfMateIdentities += 1

		userPassesMyDesire, err := myMateDesires.CheckIfMateProfilePassesMyDesire(desireName, getAnyProfileAttributeFunction)
		if (err != nil) { return 0, 0, 0, 0, 0, 0, err }
		if (userPassesMyDesire == true){
			numberOfMateIdentitiesWhoPassDesire += 1
		}

		userIsAMatchExcludingDesire, err := myMateDesires.CheckIfMateProfilePassesAllMyDesires(true, desireName, getAnyProfileAttributeFunction)
		if (err != nil) { return 0, 0, 0, 0, 0, 0, err }
		if (userIsAMatchExcludingDesire == false){
			continue
		}
		numberOfMatchesExcludingDesire += 1

		if (userPassesMyDesire == true){
			numberOfMatches += 1
		}
	}

	getPercentageOfMateIdentitiesWhoPassDesire := func()float64{
		if (numberOfMateIdentities == 0){
			return 0
		}
		percentageOfMateIdentitiesWhoPassDesire := 100 * (float64(numberOfMateIdentitiesWhoPassDesire)/float64(numberOfMateIdentities))

		return percentageOfMateIdentitiesWhoPassDesire
	}

	percentageOfMateIdentitiesWhoPassDesire := getPercentageOfMateIdentitiesWhoPassDesire()

	getPercentageOfMatchesWhoPassDesire := func()float64{
		if (numberOfMatchesExcludingDesire == 0){
			return 0
		}
		percentageOfMatchesWhoPassDesire := 100 * (float64(numberOfMatches)/float64(numberOfMatchesExcludingDesire))

		return percentageOfMatchesWhoPassDesire
	}

	percentageOfMatchesWhoPassDesire := getPercentageOfMatchesWhoPassDesire()

	return numberOfMateIdentities, numberOfMateIdentitiesWhoPassDesire, percentageOfMateIdentitiesWhoPassDesire, numberOfMatchesExcludingDesire, numberOfMatches, percentageOfMatchesWhoPassDesire, nil
}


type DesireStatisticsItem struct{

	// Example: "Age"
	DesireName string

	// This is the number of mate identities who pass the desire
	// Their newest viewable profile is used to check if they pass the desire
	NumberOfUsersWhoPassDesire int64

	// This is the percentage of mate identities who pass the desire
	// Blocked and disabled users are excluded from the denominator.
	PercentageOfUsersWhoPassDesire float64

	// This is the number of matches the user would have if this desire was excluded
	NumberOfDesireExcludedMatches int64

	// This is the percentage of desire-excluded matches who pass this desire
	PercentageOfDesireExcludedMatchesWhoPassDesire float64
}

//Outputs:
//	-int64: Number of mate identities
//	-int64: Number of matches
//	-[]DesireStatisticsItem
//	-error
func GetAllMyDesireStatistics(progressIdentifier string, networkType byte)(int64, int64, []DesireStatisticsItem, error){

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

	myIdentityExists, myIdentityHash, err := myIdentity.GetMyIdentityHash("Mate")
	if (err != nil) { return 0, 0, nil, err }

	mateIdentityHashesList, err := badgerDatabase.GetAllProfileIdentityHashes("Mate")
	if (err != nil) { return 0, 0, nil, err }

	numberOfPeerIdentitiesString := helpers.ConvertIntToString(len(mateIdentityHashesList))

	numberOfMateIdentities := int64(0)
	numberOfMatches := int64(0)

	//Map Structure: Desire name -> Number of mate identities who pass desire
	numberOfMateIdentitiesWhoPassDesireMap := make(map[string]int64)

	//Map Structure: Desire name -> Number of matches the user would have excluding the desire
	numberOfDesireExcludedMatchesMap := make(map[string]int64)

	for index, peerIdentityHash := range mateIdentityHashesList{

		progressIndex := helpers.ConvertIntToString(index + 1)
		newProgressString := "Calculated " + progressIndex + "/" + numberOfPeerIdentitiesString + " Users"
		appMemory.SetMemoryEntry(progressIdentifier, newProgressString)

		if (myIdentityExists == true && peerIdentityHash == myIdentityHash){
			continue
		}

		userIsBlocked, _, _, _, err := myBlockedUsers.CheckIfUserIsBlocked(peerIdentityHash)
		if (err != nil) { return 0, 0, nil, err }
		if (userIsBlocked == true){
			// We don't include blocked users in our statistics calculation
			// This is because "Blocked" is not a desire, so it won't be shown on the Desire Statistics page
			// We show the user how many users they have blocked on the Match Statistics page
			continue
		}

		profileExists, _, getAnyProfileAttributeFunction, err := viewableProfiles.GetRetrieveAnyNewestViewableUserProfileAttributeFunction(peerIdentityHash, networkType, true, false, true)
		if (err != nil) { return 0, 0, nil, err }
		if (profileExists == false) {
			// Profile must have been deleted, or user's profile is not viewable
			continue
		}

		exists, _, _, err := getAnyProfileAttributeFunction("Disabled")
		if (err != nil) { return 0, 0, nil, err }
		if (exists == true){
			// Profile is disabled
			continue
		}

		numberOfMateIdentities += 1

		userPassesMyDesiresMap, err := myMateDesires.GetMateProfilePassesAllMyDesiresMap(getAnyProfileAttributeFunction)
		if (err != nil) { return 0, 0, nil, err }

		getUserIsAMatchBool := func()bool{

			for _, userPassesDesire := range userPassesMyDesiresMap{
				if (userPassesDesire == false){
					return false
				}
			}
			return true
		}

		userIsAMatch := getUserIsAMatchBool()
		if (userIsAMatch == true){
			numberOfMatches += 1
		}

		for desireName, userPassesDesire := range userPassesMyDesiresMap{

			if (userPassesDesire == true){

				numberOfMateIdentitiesWhoPassDesireMap[desireName] += 1
			}

			// Now we find out if user would be a match excluding the current desire

			getUserIsAMatchExcludingCurrentDesireBool := func()bool{

				if (userIsAMatch == true){
					return true
				}

				for desireNameInner, userPassesDesireInner := range userPassesMyDesiresMap{

					if (desireNameInner == desireName){
						continue
					}
					if (userPassesDesireInner == false){
						return false
					}
				}
				return true
			}

			userIsAMatchExcludingCurrentDesire := getUserIsAMatchExcludingCurrentDesireBool()
			if (userIsAMatchExcludingCurrentDesire == true){
				numberOfDesireExcludedMatchesMap[desireName] += 1
			}
		}
	}

	allMyDesiresList := myMateDesires.GetAllMyDesiresList(false)

	allMyDesireStatisticsItemsList := make([]DesireStatisticsItem, 0, len(allMyDesiresList))

	for _, desireName := range allMyDesiresList{

		getNumberOfMateIdentitiesWhoPassDesire := func()int64{

			numberOfMateIdentitiesWhoPassDesire, exists := numberOfMateIdentitiesWhoPassDesireMap[desireName]
			if (exists == false) {
				return 0
			}

			return numberOfMateIdentitiesWhoPassDesire
		}

		numberOfMateIdentitiesWhoPassDesire := getNumberOfMateIdentitiesWhoPassDesire()

		getNumberOfDesireExcludedMatches := func()int64{

			numberOfDesireExcludedMatches, exists := numberOfDesireExcludedMatchesMap[desireName]
			if (exists == false) {
				return 0
			}
			return numberOfDesireExcludedMatches
		}

		numberOfDesireExcludedMatches := getNumberOfDesireExcludedMatches()

		getPercentageOfMateIdentitiesWhoPassDesire := func()float64{
			if (numberOfMateIdentities == 0){
				return 0
			}

			percentageOfMateIdentitiesWhoPassDesire := 100 * (float64(numberOfMateIdentitiesWhoPassDesire)/float64(numberOfMateIdentities))

			return percentageOfMateIdentitiesWhoPassDesire
		}

		percentageOfMateIdentitiesWhoPassDesire := getPercentageOfMateIdentitiesWhoPassDesire()

		getPercentageOfDesireExcludedMatchesWhoPassDesire := func()float64{
			if (numberOfDesireExcludedMatches == 0){
				return 0
			}

			percentageOfDesireExcludedMatchesWhoPassDesire := 100 * (float64(numberOfMatches)/float64(numberOfDesireExcludedMatches))

			return percentageOfDesireExcludedMatchesWhoPassDesire
		}

		percentageOfDesireExcludedMatchesWhoPassDesire := getPercentageOfDesireExcludedMatchesWhoPassDesire()

		newDesireStatisticsItem := DesireStatisticsItem{
			DesireName: desireName,
			NumberOfUsersWhoPassDesire: numberOfMateIdentitiesWhoPassDesire,
			PercentageOfUsersWhoPassDesire: percentageOfMateIdentitiesWhoPassDesire,
			NumberOfDesireExcludedMatches: numberOfDesireExcludedMatches,
			PercentageOfDesireExcludedMatchesWhoPassDesire: percentageOfDesireExcludedMatchesWhoPassDesire,
		}

		allMyDesireStatisticsItemsList = append(allMyDesireStatisticsItemsList, newDesireStatisticsItem)
	}

	return numberOfMateIdentities, numberOfMatches, allMyDesireStatisticsItemsList, nil
}




