
// contentControversy provides functions to calculate the controversy of a piece of reviewed content
// Controversy is a numeric rating of how much disagreement exists among moderators.
// Moderators can view the most controversial content to find moderators to ban or debate with

package contentControversy

// The content controversy calculation can be adjusted using several options:

// 1. Multiply moderator identity scores
// 		This will multiply the controversy by the sum of all reviewer identity scores, rather than by the number of moderators
//		Toggling this on and off is useful to find controversial moderators who do and do not have high identity scores
// 2. Use enabled only or include banned moderators
//		-This is useful to find moderators who have already been banned
//		-We want as many moderators as possible to ban unruleful moderators, even if they have been banned already
//		-This defends against an unruleful moderator increasing their score to evade a ban
//		-It also defends against a powerful moderator disabling their profile, resulting in the unbanning of many unruleful moderators
// 3. TODO: Time range
//		-This will enable only showing controversial content that was created during a certain time range
//		-This will make it possible to only view content that was created recently or long ago

//TODO: Add filters
//	Example:
//	-Hide content that I have already reviewed

import "seekia/internal/contentMetadata"
import "seekia/internal/encoding"
import "seekia/internal/helpers"
import "seekia/internal/moderation/bannedModeratorConsensus"
import "seekia/internal/moderation/enabledModerators"
import "seekia/internal/moderation/moderatorScores"
import "seekia/internal/moderation/reviewStorage"
import "seekia/internal/network/appNetworkType/getAppNetworkType"
import "seekia/internal/network/backgroundDownloads"

import "errors"

//Outputs:
//	-bool: Controversy is known (content metadata exists, required reviews are being downloaded, parameters exist)
//	-int64: Content controversy rating
//	-error
func GetContentControversyRating(contentHash []byte)(bool, int64, error){

	contentType, err := helpers.GetContentTypeFromContentHash(contentHash)
	if (err != nil){
		contentHashHex := encoding.EncodeBytesToHexString(contentHash)
		return false, 0, errors.New("GetContentControversyRating called with invalid contentHash: " + contentHashHex)
	}
	if (contentType != "Profile" && contentType != "Message"){
		contentHashHex := encoding.EncodeBytesToHexString(contentHash)
		return false, 0, errors.New("GetContentControversyRating called with invalid contentHash: Not profile/message: " + contentHashHex)
	}

	appNetworkType, err := getAppNetworkType.GetAppNetworkType()
	if (err != nil) { return false, 0, err }

	//TODO: Check if parameters exist

	//TODO: Fix below to retrieve from settings
	multiplyIdentityScores := true
	includeBannedModerators := true

	//Outputs:
	//	-bool: Required data exists (Metadata exists and downloading required reviews/moderator profiles)
	//	-map[[16]byte]int64: Approve advocates map (identity hash -> Time of approval)
	//	-map[[16]byte]int64: Ban advocates map (identity hash -> Time of ban)
	//	-error
	getApproveAndBanAdvocatesMaps := func()(bool, map[[16]byte]int64, map[[16]byte]int64, error){

		if (contentType == "Message"){

			if (len(contentHash) != 26){
				contentHashHex := encoding.EncodeBytesToHexString(contentHash)
				return false, nil, nil, errors.New("GetContentTypeFromContentHash returning Message for different length contentHash: " + contentHashHex)
			}

			messageHash := [26]byte(contentHash)

			metadataExists, _, messageNetworkType, _, messageInbox, messageCipherKeyHash, err := contentMetadata.GetMessageMetadata(messageHash)
			if (err != nil) { return false, nil, nil, err }
			if (metadataExists == false){
				return false, nil, nil, nil
			}
			if (messageNetworkType != appNetworkType){
				// We cannot determine controversy for content from a different networkType
				return false, nil, nil, nil
			}

			downloadingRequiredContent, err := backgroundDownloads.CheckIfAppCanDetermineMessageVerdict(messageNetworkType, messageInbox, true, messageHash)
			if (err != nil) { return false, nil, nil, err }
			if (downloadingRequiredContent == false){
				return true, nil, nil, nil				
			}

			approveAdvocatesMap, banAdvocatesMap, err := reviewStorage.GetMessageVerdictMaps(messageHash, messageNetworkType, messageCipherKeyHash)
			if (err != nil) { return false, nil, nil, err }

			return true, approveAdvocatesMap, banAdvocatesMap, nil
		}

		// contentType is Profile

		if (len(contentHash) != 28){

			contentHashHex := encoding.EncodeBytesToHexString(contentHash)
			return false, nil, nil, errors.New("GetContentTypeFromContentHash returning Profile for different length contentHash: " + contentHashHex)
		}

		profileHash := [28]byte(contentHash)

		profileMetadataExists, _, profileNetworkType, profileIdentityHash, _, profileIsDisabled, _, profileAttributeHashesMap, err := contentMetadata.GetProfileMetadata(profileHash)
		if (err != nil) { return false, nil, nil, err }
		if (profileMetadataExists == false){
			return false, nil, nil, nil
		}
		if (profileNetworkType != appNetworkType){
			// We cannot determine controversy for content from a different networkType.
			return false, nil, nil, nil
		}
		if (profileIsDisabled == true){
			// Profile cannot be reviewed, content controversy is not applicable
			return false, nil, nil, nil
		}

		downloadingRequiredReviews, err := backgroundDownloads.CheckIfAppCanDetermineIdentityVerdicts(profileIdentityHash)
		if (err != nil){ return false, nil, nil, err }
		if (downloadingRequiredReviews == false){
			return false, nil, nil, nil
		}

		// We have to check for any moderators who banned an attribute after approving the full profile
		// This would negate their full profile approval

		attributeProfileHashesList := helpers.GetListOfMapValues(profileAttributeHashesMap)

		approveAdvocatesMap, banAdvocatesMap, err := reviewStorage.GetProfileVerdictMaps(profileHash, profileNetworkType, true, attributeProfileHashesList)
		if (err != nil) { return false, nil, nil, err }

		return true, approveAdvocatesMap, banAdvocatesMap, nil
	}

	requiredDataExists, approveAdvocatesMap, banAdvocatesMap, err := getApproveAndBanAdvocatesMaps()
	if (err != nil) { return false, 0, err }
	if (requiredDataExists == false){
		return false, 0, nil
	}

	if (len(approveAdvocatesMap) == 0 && len(banAdvocatesMap) == 0){
		// No eligible reviewers exist.
		// Thus, no controversy exists.
		return true, 0, nil
	}

	//Outputs:
	//	-bool: Parameters and required data exists to determine moderator is banned status
	//	-float64: Moderators value (either number of moderators or a sum of identity scores)
	//	-error
	getModeratorsValue := func(inputModeratorsMap map[[16]byte]int64)(bool, float64, error){

		moderatorsValue := float64(0)

		for identityHash, _ := range inputModeratorsMap{

			moderatorIsEnabled, err := enabledModerators.CheckIfModeratorIsEnabled(true, identityHash, appNetworkType)
			if (err != nil){ return false, 0, err }
			if (moderatorIsEnabled == false){
				continue
			}
			if (includeBannedModerators == false){

				requiredDataExists, parametersExist, moderatorIsBanned, err := bannedModeratorConsensus.GetModeratorIsBannedStatus(true, identityHash, appNetworkType)
				if (err != nil){ return false, 0, err }
				if (requiredDataExists == false || parametersExist == false){
					return false, 0, nil
				}
				if (moderatorIsBanned == true){
					continue
				}
			}

			if (multiplyIdentityScores == false){
				moderatorsValue += 1
				continue
			}
			
			scoreIsKnown, moderatorScore, scoreIsSufficient, _, _, err := moderatorScores.GetModeratorIdentityScore(identityHash)
			if (err != nil) { return false, 0, err }
			if (scoreIsKnown == false){
				// Client has not downloaded moderator score, will do so automatically, skip for now.
				continue
			}
			if (scoreIsSufficient == false){
				// Moderator is not funded enough to make reviews
				continue
			}
			moderatorsValue += moderatorScore
		}

		return true, moderatorsValue, nil
	}

	requiredDataExists, approveValue, err := getModeratorsValue(approveAdvocatesMap)
	if (err != nil) { return false, 0, err }
	if (requiredDataExists == false){
		// We cannot determine content controversy
		// Client should automatically download parameters in the background, and then content controversy will be calculable
		return false, 0, nil
	}

	requiredDataExists, banValue, err := getModeratorsValue(banAdvocatesMap)
	if (err != nil) { return false, 0, err }
	if (requiredDataExists == false){
		// We cannot determine content controversy
		// Client should automatically download parameters in the background, and then content controversy will be calculable
		return false, 0, nil
	}

	getValuesRatio := func()float64{

		if (approveValue == banValue){
			// Highest possible controversy
			return 1
		}
		if (approveValue == 0 || banValue == 0){
			// Lowest possible controversy
			return 0
		}
		if (approveValue > banValue){
			ratio := banValue/approveValue
			return ratio
		}

		ratio := approveValue/banValue
		return ratio
	}

	valuesRatio := getValuesRatio()

	contentControversyFloat := valuesRatio * (approveValue + banValue)

	contentControversyInt, err := helpers.FloorFloat64ToInt64(contentControversyFloat)
	if (err != nil) { return false, 0, err }

	return true, contentControversyInt, nil
}



