
// reportStorage provides functions to manage stored reports.
// Reports are created by users to report unruleful content.

package reportStorage

import "seekia/internal/badgerDatabase"
import "seekia/internal/encoding"
import "seekia/internal/helpers"
import "seekia/internal/messaging/readMessages"
import "seekia/internal/moderation/readReports"
import "seekia/internal/mySettings"

import "bytes"
import "errors"


func GetNumberOfStoredReports()(int64, error){
	
	numberOfReports, err := badgerDatabase.GetNumberOfReports()
	if (err != nil) { return 0, err }

	return numberOfReports, nil
}


//Outputs:
//	-bool: Report is well formed
//	-error
func AddReport(newReport []byte)(bool, error){

	ableToRead, reportHash, _, _, _, reportType, reportedHash, _, err := readReports.ReadReportAndHash(true, newReport)
	if (err != nil){ return false, err }
	if (ableToRead == false){
		// Report is malformed.
		// Host who sent report must be malicious.
		return false, nil
	}

	exists, _, err := badgerDatabase.GetReport(reportHash)
	if (err != nil) { return false, err }
	if (exists == true){
		// Report is already imported.		
		return true, nil
	}

	err = badgerDatabase.AddReport(reportHash, newReport)
	if (err != nil) { return false, err }

	err = mySettings.SetSetting("ViewedContentNeedsRefreshYesNo", "Yes")
	if (err != nil) { return false, err }

	if (reportType == "Identity"){

		if (len(reportedHash) != 16){
			return false, errors.New("ReadReport returning invalid length reportedHash for Identity report.")
		}

		reportedIdentityHash := [16]byte(reportedHash)

		err := badgerDatabase.AddIdentityReportToList(reportedIdentityHash, reportHash)
		if (err != nil) { return false, err }

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

		if (len(reportedHash) != 28){
			return false, errors.New("ReadReport returning invalid length reportedHash for Profile report.")
		}

		reportedProfileHash := [28]byte(reportedHash)

		err = badgerDatabase.AddProfileReportToList(reportedProfileHash, reportHash)
		if (err != nil) { return false, err }

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

		if (len(reportedHash) != 27){
			return false, errors.New("ReadReport returning invalid length reportedHash for Attribute report.")
		}

		reportedAttributeHash := [27]byte(reportedHash)

		err = badgerDatabase.AddProfileAttributeReportToList(reportedAttributeHash, reportHash)
		if (err != nil) { return false, err }

		return true, nil
	}
	if (reportType == "Message"){

		if (len(reportedHash) != 26){
			return false, errors.New("ReadReport returning invalid length reportedHash for Message report.")
		}

		reportedMessageHash := [26]byte(reportedHash)

		err = badgerDatabase.AddMessageReportToList(reportedMessageHash, reportHash)
		if (err != nil) { return false, err }

		return true, nil
	}

	return false, errors.New("ReadReport returning invalid reportType: " + reportType)
}


func GetNumberOfReportsForReportedHash(reportedHash []byte, networkType byte)(int64, error){

	reportedType, err := helpers.GetReportedTypeFromReportedHash(reportedHash)
	if (err != nil){
		reportedHashHex := encoding.EncodeBytesToHexString(reportedHash)
		return 0, errors.New("GetNumberOfReportsForReportedHash called with invalid reportedHash: " + reportedHashHex)
	}

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

	//Outputs:
	//	-bool: Any report hashes found
	//	-[][30]byte: Report hashes list for reportedHash 
	//	-error
	getReportHashesList := func()(bool, [][30]byte, error){

		if (reportedType == "Identity"){

			if (len(reportedHash) != 16){
				return false, nil, errors.New("GetReportedTypeFromReportedHash returning Identity for invalid length identity hash.")
			}

			reportedIdentityHash := [16]byte(reportedHash)

			exists, reportHashesList, err := badgerDatabase.GetIdentityReportsList(reportedIdentityHash)

			return exists, reportHashesList, err
		}
		if (reportedType == "Profile"){

			if (len(reportedHash) != 28){
				return false, nil, errors.New("GetReportedTypeFromReportedHash returning Profile for invalid length profile hash.")
			}

			reportedProfileHash := [28]byte(reportedHash)

			exists, reportHashesList, err := badgerDatabase.GetProfileReportsList(reportedProfileHash)
			
			return exists, reportHashesList, err
		}
		if (reportedType == "Attribute"){
	
			if (len(reportedHash) != 27){
				return false, nil, errors.New("GetReportedTypeFromReportedHash returning Attribute for invalid length attribute hash.")
			}

			reportedAttributeHash := [27]byte(reportedHash)

			exists, reportedHashesList, err := badgerDatabase.GetProfileAttributeReportsList(reportedAttributeHash)
			
			return exists, reportedHashesList, err		
		}
		if (reportedType == "Message"){

			if (len(reportedHash) != 26){
				return false, nil, errors.New("GetReportedTypeFromReportedHash returning Message for invalid length message hash.")
			}

			reportedMessageHash := [26]byte(reportedHash)

			exists, reportHashesList, err := badgerDatabase.GetMessageReportsList(reportedMessageHash)

			return exists, reportHashesList, err
		}

		return false, nil, errors.New("GetReportedTypeFromReportedHash returning invalid reportedType: " + reportedType)
	}

	exists, reportHashesList, err := getReportHashesList()
	if (err != nil) { return 0, err }
	if (exists == false){
		return 0, nil
	}

	numberOfReports := int64(0)
	
	for _, reportHash := range reportHashesList{

		exists, reportBytes, err := badgerDatabase.GetReport(reportHash)
		if (err != nil) { return 0, err }
		if (exists == false){
			// Report has been deleted, reports list will be updated in the background
			continue
		}

		ableToRead, _, reportNetworkType, _, reportType, reportReportedHash, _, err := readReports.ReadReport(false, reportBytes)
		if (err != nil) { return 0, err }
		if (ableToRead == false){
			return 0, errors.New("Database corrupt: Contains invalid report.")
		}
		if (reportNetworkType != networkType){
			continue
		}
		if (reportType != reportedType){
			return 0, errors.New("getReportHashesList returning report of different reportedType.")
		}
		areEqual := bytes.Equal(reportedHash, reportReportedHash)
		if (areEqual == false){
			return 0, errors.New("getReportHashesList returning report for different reportedHash.")
		}

		numberOfReports += 1
	}

	return numberOfReports, nil
}


// We use this function to find a valid message cipher key to decrypt a message for moderation
// We verify the message CipherKeyHash to make sure the author of the report has seen the message
// If the cipher key does not decrypt the message, then we know the author of the report and the message are malicious
//Outputs:
//	-bool: Message cipher key found
//	-[32]byte: Message Cipher key
//	-error
func GetMessageCipherKeyFromAnyReport(messageHash [26]byte, messageNetworkType byte, messageCipherKeyHash [25]byte)(bool, [32]byte, error){

	isValid := helpers.VerifyNetworkType(messageNetworkType)
	if (isValid == false){
		messageNetworkTypeString := helpers.ConvertByteToString(messageNetworkType)
		return false, [32]byte{}, errors.New("GetMessageCipherKeyFromAnyReport called with invalid messageNetworkType: " + messageNetworkTypeString)
	}

	exists, reportHashesList, err := badgerDatabase.GetMessageReportsList(messageHash)
	if (err != nil) { return false, [32]byte{}, err }
	if (exists == false){
		return false, [32]byte{}, nil
	}
	if (len(reportHashesList) == 0){
		return false, [32]byte{}, nil
	}

	for _, reportHash := range reportHashesList{

		exists, reportBytes, err := badgerDatabase.GetReport(reportHash)
		if (err != nil) { return false, [32]byte{}, err }
		if (exists == false){
			// Report must have been deleted. Database will be updated automatically.
			continue
		}
		ableToRead, _, reportNetworkType, _, reportType, reportedHash, reportMap, err := readReports.ReadReport(false, reportBytes)
		if (err != nil) { return false, [32]byte{}, err }
		if (ableToRead == false){
			return false, [32]byte{}, errors.New("Database corrupt: Contains invalid report.")
		}
		if (reportNetworkType != messageNetworkType){
			// Person who created this report must be malicious
			// We skip the report, even if we could use it to retrieve the message cipher key hash
			// This is because we want all moderators to see the same reports, regardless of if they previously accessed a different network
			continue
		}

		if (reportType != "Message") {
			return false, [32]byte{}, errors.New("Corrupt database: ReportType does not match reports list.")
		}

		areEqual := bytes.Equal(reportedHash, messageHash[:])
		if (areEqual == false){
			return false, [32]byte{}, errors.New("Corrupt database: Reported message does not match reports list.")
		}

		messageCipherKeyString, exists := reportMap["MessageCipherKey"]
		if (exists == false) {
			return false, [32]byte{}, errors.New("Corrupt database: Contains report missing MessageCipherKey")
		}

		messageCipherKey, err := readMessages.ReadMessageCipherKeyHex(messageCipherKeyString)
		if (err != nil){
			return false, [32]byte{}, errors.New("Corrupt database: Contains report with invalid MessageCipherKey: " + messageCipherKeyString + ". Reason: " + err.Error())
		}

		currentMessageCipherKeyHash, err := readMessages.ConvertMessageCipherKeyToCipherKeyHash(messageCipherKey)
		if (err != nil){ return false, [32]byte{}, err }

		if (currentMessageCipherKeyHash != messageCipherKeyHash){
			// Report author is malicious.
			continue
		}

		return true, messageCipherKey, nil
	}

	// No valid reports found for message

	return false, [32]byte{}, nil
}




