
// bannedModeratorConsensus provides functions to determine which moderators are banned

package bannedModeratorConsensus

import "seekia/internal/badgerDatabase"
import "seekia/internal/encoding"
import "seekia/internal/helpers"
import "seekia/internal/identity"
import "seekia/internal/moderation/moderatorRanking"
import "seekia/internal/moderation/moderatorScores"
import "seekia/internal/moderation/readReviews"
import "seekia/internal/parameters/getParameters"

import "slices"
import "sync"
import "errors"

var bannedModeratorsListMutex sync.RWMutex

// This list stores all banned moderators
// It will not necessarily be up to date
var bannedModeratorsList [][16]byte

// This variable keeps track of the networkType of the bannedModeratorsList
var bannedModeratorsListNetworkType byte


// This function should be called whenever the app network type is changed.
func UpdateBannedModeratorsList(networkType byte)error{

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

	bannedModeratorsListMutex.Lock()
	bannedModeratorsList = nil
	bannedModeratorsListNetworkType = 0
	bannedModeratorsListMutex.Unlock()

	_, _, _, err := GetBannedModeratorsList(false, networkType)
	if (err != nil) { return err }

	return nil
}


//Outputs:
//	-bool: Moderator reviews and profiles are being downloaded (required to check if a moderator is banned)
//	-bool: Parameters exist
//	-bool: Moderator is banned
//	-error
func GetModeratorIsBannedStatus(allowCache bool, identityHash [16]byte, networkType byte)(bool, bool, bool, error){

	isValid, err := identity.VerifyIdentityHash(identityHash, true, "Moderator")
	if (err != nil) { return false, false, false, err }
	if (isValid == false){
		identityHashHex := encoding.EncodeBytesToHexString(identityHash[:])
		return false, false, false, errors.New("GetModeratorIsBannedStatus called with invalid identity hash: " + identityHashHex)
	}

	//TODO: Make it possible to retrieve the isBanned status without copying the cache list

	downloadingRequiredData, parametersExist, bannedModeratorsList, err := GetBannedModeratorsList(allowCache, networkType)
	if (err != nil) { return false, false, false, err }
	if (downloadingRequiredData == false){
		return false, false, false, nil
	}
	if (parametersExist == false){
		return true, false, false, nil
	}

	isBanned := slices.Contains(bannedModeratorsList, identityHash)

	return true, true, isBanned, nil
}

//Outputs:
//	-bool: Moderator reviews and profiles are being downloaded (required to calculate the banned moderators list)
//	-bool: Parameters exist
//	-[][16]byte: List of banned moderators
//	-error
func GetBannedModeratorsList(allowCache bool, networkType byte)(bool, bool, [][16]byte, error){

	//TODO: Check if parameters exist and moderator reviews/profiles are being downloaded

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

	if (allowCache == true){

		bannedModeratorsListMutex.RLock()

		if (bannedModeratorsList != nil && bannedModeratorsListNetworkType == networkType){

			// Cache list has been generated and belongs to the specified network type.

			listCopy := slices.Clone(bannedModeratorsList)

			bannedModeratorsListMutex.RUnlock()

			return true, true, listCopy, nil
		}

		bannedModeratorsListMutex.RUnlock()
	}

	// We must generate a new banned moderators cache list

	rankedModeratorsList, err := moderatorRanking.GetRankedModeratorsList(networkType)
	if (err != nil) { return false, false, nil, err }

	// We start with the highest ranked moderator, and ban moderators as we traverse the list
	// If an identity is found to be banned, all of its reviews are ignored

	// Supermoderators are all-powerful moderators who can only be banned by higher-ranked supermoderators
	// They are appointed by the admin(s)

	parametersExist, supermoderatorsList, err := getParameters.GetSupermoderatorIdentityHashesList(networkType)
	if (err != nil) { return false, false, nil, err }
	if (parametersExist == false){
		return true, false, nil, nil
	}

	// Supermoderators are added to the beginning of the ranked moderators list
	rankedModeratorsList = append(supermoderatorsList, rankedModeratorsList...)

	// Approved Moderators are the moderators who were not banned by anyone of a higher rank
	// Map structure: Approved Moderator Identity Hash -> Nothing
	approvedModeratorsMap := make(map[[16]byte]struct{})

	// This map stores all banned identities
	// Map structure: Banned Moderator Identity Hash -> Nothing
	bannedModeratorsMap := make(map[[16]byte]struct{})

	for _, moderatorIdentityHash := range rankedModeratorsList{

		_, exists := bannedModeratorsMap[moderatorIdentityHash]
		if (exists == true){
			// This moderator has been banned, none of their reviews are counted. Skip them.
			continue
		}

		// We make sure moderator is eligible

		moderatorIsSupermoderator := slices.Contains(supermoderatorsList, moderatorIdentityHash)
		if (moderatorIsSupermoderator == false){

			// Moderator is not a supermoderator, so we make sure they are funded enough to be able to ban other moderators

			scoreIsKnown, _, scoreIsSufficient, canBanModerators, _, err := moderatorScores.GetModeratorIdentityScore(moderatorIdentityHash)
			if (err != nil) { return false, false, nil, err }
			if (scoreIsKnown == false || scoreIsSufficient == false || canBanModerators == false){
				// This moderator is not funded, or cannot ban moderators
				continue
			}
		}

		// This moderator is approved (not banned by any moderators with a higher rank)
		approvedModeratorsMap[moderatorIdentityHash] = struct{}{}

		// We create a list of all identity reviews created by this moderator

		exists, reviewHashesList, err := badgerDatabase.GetReviewerReviewHashesList(moderatorIdentityHash, "Identity")
		if (err != nil) { return false, false, nil, err }
		if (exists == false){
			// This moderator has not authored any reviews
			continue
		}

		moderatorReviewsList := make([]readReviews.ReviewWithHash, 0)

		for _, reviewHash := range reviewHashesList{

			exists, reviewBytes, err := badgerDatabase.GetReview(reviewHash)
			if (err != nil) { return false, false, nil, err }
			if (exists == false){
				// Review has been deleted. The missing entry will be removed automatically in the background.
				continue
			}

			reviewObject := readReviews.ReviewWithHash{
				ReviewHash: reviewHash,
				ReviewBytes: reviewBytes,
			}

			moderatorReviewsList = append(moderatorReviewsList, reviewObject)
		}

		moderatorReviewsMap, err := readReviews.GetNewestModeratorReviewsMapFromReviewsList(moderatorReviewsList, moderatorIdentityHash, networkType, true, "Identity")
		if (err != nil){ return false, false, nil, err }

		for reviewedIdentityHashString, _ := range moderatorReviewsMap{

			reviewedIdentityHashBytes := []byte(reviewedIdentityHashString)

			if (len(reviewedIdentityHashBytes) != 16){
				reviewedIdentityHashHex := encoding.EncodeBytesToHexString(reviewedIdentityHashBytes)
				return false, false, nil, errors.New("GetNewestModeratorReviewsMapFromReviewsList returning non-identity reviewedHash: " + reviewedIdentityHashHex)
			}

			reviewedIdentityHash := [16]byte(reviewedIdentityHashBytes)

			// Each reviewedIdentityHash is an identity this moderator has banned

			identityType, err := identity.GetIdentityTypeFromIdentityHash(reviewedIdentityHash)
			if (err != nil) {
				reviewedIdentityHashHex := encoding.EncodeBytesToHexString(reviewedIdentityHash[:])
				return false, false, nil, errors.New("GetNewestModeratorReviewsMapFromReviewsList returning non-identity reviewedHash: " + reviewedIdentityHashHex)
			}
			if (identityType != "Moderator"){
				continue
			}

			_, exists = approvedModeratorsMap[reviewedIdentityHash]
			if (exists == true){
				//The current moderator cannot ban this moderator, because they are of a lower rank.
				continue
			}
			bannedModeratorsMap[reviewedIdentityHash] = struct{}{}
		}
	}

	// bannedModeratorsMap is a map whose keys are the identity hashes of banned moderators
	newBannedModeratorsList := helpers.GetListOfMapKeys(bannedModeratorsMap)

	bannedModeratorsListMutex.Lock()

	bannedModeratorsList = newBannedModeratorsList
	bannedModeratorsListNetworkType = networkType

	listCopy := slices.Clone(bannedModeratorsList)

	bannedModeratorsListMutex.Unlock()

	return true, true, listCopy, nil
}


