
// moderatorRanking provides functions to calculate a moderator's rank, and to get a list of ranked moderators
// A moderator's rank is determined by ranking all moderators by their identity scores.

package moderatorRanking

//TODO: Add a bool to both functions to exclude banned moderators
// When a moderator is viewing another moderator's rank, they should be able to view both rankings: Banned excluded and banned included
// This might actually be a bad idea, because it could encourage moderators to ban other moderators so they can surpass them in rank without having to burn more funds.

import "seekia/internal/encoding"
import "seekia/internal/helpers"
import "seekia/internal/identity"
import "seekia/internal/moderation/enabledModerators"
import "seekia/internal/moderation/moderatorScores"

import "slices"
import "errors"

// This function returns the rank of a particular moderator
// This is faster than ranking all of the moderators, because we don't have to sort
//Outputs:
//	-bool: Moderator ranking known
//	-int: Moderator rank
//	-int: Total number of moderators
//	-error
func GetModeratorRanking(moderatorIdentityHash [16]byte, networkType byte)(bool, int, int, error){

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

	moderatorScoresMap, err := getEnabledModeratorScoresMap(networkType)
	if (err != nil) { return false, 0, 0, err }

	moderatorScore, exists := moderatorScoresMap[moderatorIdentityHash]
	if (exists == false){
		// We may not have downloaded their profile or their identity score
		// Their profile may also be disabled
		// We cannot determine their ranking
		return false, 0, 0, nil
	}

	moderatorIdentityHashString, _, err := identity.EncodeIdentityHashBytesToString(moderatorIdentityHash)
	if (err != nil) {
		moderatorIdentityHashHex := encoding.EncodeBytesToHexString(moderatorIdentityHash[:])
		return false, 0, 0, errors.New("moderatorScoresMap contains invalid moderator identity hash: " + moderatorIdentityHashHex)
	}

	totalNumberOfModerators := len(moderatorScoresMap)

	moderatorsWithLowerScoreCounter := 0

	for currentModeratorIdentityHash, currentModeratorScore := range moderatorScoresMap{

		if (currentModeratorIdentityHash == moderatorIdentityHash){
			continue
		}

		if (moderatorScore == currentModeratorScore){

			currentModeratorIdentityHashString, _, err := identity.EncodeIdentityHashBytesToString(currentModeratorIdentityHash)
			if (err != nil) {
				moderatorIdentityHashHex := encoding.EncodeBytesToHexString(currentModeratorIdentityHash[:])
				return false, 0, 0, errors.New("moderatorScoresMap contains invalid moderator identity hash: " + moderatorIdentityHashHex)
			}

			// We sort moderators with the same identity score in alphabetical order

			if (moderatorIdentityHashString < currentModeratorIdentityHashString){
				moderatorsWithLowerScoreCounter += 1
			}
			continue
		}

		if (moderatorScore > currentModeratorScore){
			moderatorsWithLowerScoreCounter += 1				
		}
	}

	moderatorRank := totalNumberOfModerators - moderatorsWithLowerScoreCounter

	return true, moderatorRank, totalNumberOfModerators, nil
}


// This function returns a list of all moderators ranked, starting from highest to lowest rank
func GetRankedModeratorsList(networkType byte)([][16]byte, error){

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

	moderatorScoresMap, err := getEnabledModeratorScoresMap(networkType)
	if (err != nil) { return nil, err }

	moderatorIdentitiesList := helpers.GetListOfMapKeys(moderatorScoresMap)

	compareIdentitiesFunction := func(moderatorIdentityA [16]byte, moderatorIdentityB [16]byte)int {

		if (moderatorIdentityA == moderatorIdentityB){
			panic("compareIdentitiesFunction called with identical moderator identities.")
		}

		moderatorAScore, exists := moderatorScoresMap[moderatorIdentityA]
		if (exists == false) {
			panic("Moderator score not found in moderatorScoresMap")
		}

		moderatorBScore, exists := moderatorScoresMap[moderatorIdentityB]
		if (exists == false) {
			panic("Moderator score not found in moderatorScoresMap")
		}

		if (moderatorAScore == moderatorBScore){

			// This will sort the moderators in unicode order
			// This is to make sure that everyone calculates moderator order the same way

			moderatorIdentityAString, _, err := identity.EncodeIdentityHashBytesToString(moderatorIdentityA)
			if (err != nil) {
				moderatorAIdentityHashHex := encoding.EncodeBytesToHexString(moderatorIdentityA[:])
				panic("moderatorScoresMap contains invalid moderator identity hash: " + moderatorAIdentityHashHex)
			}

			moderatorIdentityBString, _, err := identity.EncodeIdentityHashBytesToString(moderatorIdentityB)
			if (err != nil) {
				moderatorBIdentityHashHex := encoding.EncodeBytesToHexString(moderatorIdentityB[:])
				panic("moderatorScoresMap contains invalid moderator identity hash: " + moderatorBIdentityHashHex)
			}

			if (moderatorIdentityAString < moderatorIdentityBString){
				return -1
			}

			return 1
		}

		if (moderatorAScore < moderatorBScore){
			return 1
		}

		return -1
	}

	slices.SortFunc(moderatorIdentitiesList, compareIdentitiesFunction)

	return moderatorIdentitiesList, nil
}


// This function returns a map of all enabled (not disabled) moderators with their identity scores
// It will omit any moderators whose scores have not been downloaded or are not sufficient

//Outputs:
//	-map[[16]byte]float64: Identity Hash -> Moderator Score
//	-error
func getEnabledModeratorScoresMap(networkType byte)(map[[16]byte]float64, error){

	enabledModeratorsList, err := enabledModerators.GetEnabledModeratorsList(true, networkType)
	if (err != nil) { return nil, err }

	moderatorScoresMap := make(map[[16]byte]float64)

	for _, moderatorIdentityHash := range enabledModeratorsList{

		scoreIsKnown, moderatorScore, scoreIsSufficient, _, _, err := moderatorScores.GetModeratorIdentityScore(moderatorIdentityHash)
		if (err != nil) { return nil, err }
		if (scoreIsKnown == false){
			continue
		}
		if (scoreIsSufficient == false){
			// Moderator has not funded their identity sufficiently.
			continue
		}

		moderatorScoresMap[moderatorIdentityHash] = moderatorScore
	}

	return moderatorScoresMap, nil
}


