
// createParameters provides functions to create network parameters

package createParameters

// We encode fields using integers to save space

// ParametersVersion = 1
// NetworkType = 2
// AdminPublicKeys = 3
// CreationTime = 4
// ParametersType = 5

import "seekia/internal/cryptography/blake3"
import "seekia/internal/cryptography/edwardsKeys"
import "seekia/internal/encoding"
import "seekia/internal/helpers"
import "seekia/internal/parameters/readParameters"

import "time"
import messagepack "github.com/vmihailenco/msgpack/v5"
import "errors"

//TODO: Make sure unnecessary rates are pruned
// For example, message cost rates for messages that are more than 2 weeks old can be pruned, if at least 2 rates exists
// This is because messages cannot be funded for longer than 2 weeks.

//Inputs:
//	-map[[32]byte][64]byte: Admin key -> Admin signature
//	-[]byte: Parameters Bytes
//	-error
func CreateParameters(adminSignaturesMap map[[32]byte][64]byte, contentBytes messagepack.RawMessage)([]byte, error){

	parametersContentMap := make(map[int]messagepack.RawMessage)

	err := encoding.DecodeMessagePackBytes(false, contentBytes, &parametersContentMap)
	if (err != nil) { return nil, err }

	contentHash, err := blake3.Get32ByteBlake3Hash(contentBytes)
	if (err != nil) { return nil, err }

	adminKeysListEncoded, exists := parametersContentMap[3]
	if (exists == false){
		return nil, errors.New("CreateParameters called with parameters missing AdminPublicKeys")
	}

	var adminKeysList [][32]byte

	err = encoding.DecodeMessagePackBytes(false, adminKeysListEncoded, &adminKeysList)
	if (err != nil) {
		return nil, errors.New("CreateParameters called with parameters containing invalid AdminPublicKeys")
	}

	if (len(adminKeysList) != len(adminSignaturesMap)){
		return nil, errors.New("Cannot create parameters: Admin signatures map is not the same length as adminKeysList")
	}

	signaturesList := make([][64]byte, 0, len(adminKeysList))

	for _, adminPublicKey := range adminKeysList{

		keySignature, exists := adminSignaturesMap[adminPublicKey]
		if (exists == false) {
			return nil, errors.New("Cannot create parameters: Admin signatures map missing adminPublicKey entry.")
		}

		signatureIsValid := edwardsKeys.VerifySignature(adminPublicKey, keySignature, contentHash)
		if (signatureIsValid == false){
			return nil, errors.New("Cannot create parameters: Invalid signature.")
		}

		signaturesList = append(signaturesList, keySignature)
	}

	signaturesListEncoded, err := encoding.EncodeMessagePackBytes(signaturesList)
	if (err != nil){ return nil, err }

	parametersSlice := []messagepack.RawMessage{signaturesListEncoded, contentBytes}

	parametersBytes, err := encoding.EncodeMessagePackBytes(parametersSlice)
	if (err != nil) { return nil, err }

	isValid, err := readParameters.VerifyParameters(parametersBytes)
	if (err != nil) { return nil, err }
	if (isValid == false) {
		return nil, errors.New("Cannot create parameters: Invalid result.")
	}

	return parametersBytes, nil
}

// This returns the content that the admin(s) are signing, encoded with messagepack
// We need this so the admins can share the content prior to signing it
// Within the gui, they should be able to copy the raw Base64 encoded bytes, share them among eachother, paste them into the GUI, and sign them

func CreateParametersContentBytes(adminPublicKeysList [][32]byte, parametersMap map[string]string)([]byte, error){

	if (len(adminPublicKeysList) == 0){
		return nil, errors.New("CreateParametersContentBytes called with empty adminPublicKeys list")
	}

	networkTypeString, exists := parametersMap["NetworkType"]
	if (exists == false){
		return nil, errors.New("CreateParametersContentBytes called with parametersMap missing NetworkType")
	}

	networkTypeByte, err := helpers.ConvertNetworkTypeStringToByte(networkTypeString)
	if (err != nil){
		return nil, errors.New("CreateParametersContentBytes called with parametersMap containing invalid NetworkType: " + networkTypeString)
	}

	parametersType, exists := parametersMap["ParametersType"]
	if (exists == false){
		return nil, errors.New("CreateParametersContentBytes called with parametersMap missing ParametersType")
	}

	parametersVersion := 1

	creationTime := time.Now().Unix()

	parametersVersionEncoded, err := encoding.EncodeMessagePackBytes(parametersVersion)
	if (err != nil) { return nil, err }

	networkTypeByteEncoded, err := encoding.EncodeMessagePackBytes(networkTypeByte)
	if (err != nil) { return nil, err }

	adminPublicKeysEncoded, err := encoding.EncodeMessagePackBytes(adminPublicKeysList)
	if (err != nil) { return nil, err }

	creationTimeEncoded, err := encoding.EncodeMessagePackBytes(creationTime)
	if (err != nil) { return nil, err }

	parametersTypeEncoded, err := encoding.EncodeMessagePackBytes(parametersType)
	if (err != nil) { return nil, err }

	parametersContentMap := map[int]messagepack.RawMessage{
		1: parametersVersionEncoded,
		2: networkTypeByteEncoded,
		3: adminPublicKeysEncoded,
		4: creationTimeEncoded,
		5: parametersTypeEncoded,
	}

	parametersContentBytes, err := encoding.EncodeMessagePackBytes(parametersContentMap)
	if (err != nil) { return nil, err }

	return parametersContentBytes, nil
}




