util.gno

package staker

import (
	"strconv"

	gnsmath "gno.land/p/gnoswap/gnsmath"
	bptree "gno.land/p/nt/bptree/v0"
	ufmt "gno.land/p/nt/ufmt/v0"
	"gno.land/p/onbloc/json"
)

// marshal data to json string
func marshal(data *json.Node) string {
	b, err := json.Marshal(data)
	if err != nil {
		panic(err.Error())
	}

	return string(b)
}

// getUint64FromTree returns the uint64 value from the tree
func getUint64FromTree(tree *bptree.BPTree, key string) uint64 {
	value := tree.Get(key)
	if value == nil {
		return 0
	}

	v, ok := value.(uint64)
	if !ok {
		panic(ufmt.Sprintf("failed to cast value to uint64: %T", value))
	}

	return v
}

// updateUint64InTree updates the uint64 value in the tree
func updateUint64InTree(tree *bptree.BPTree, key string, delta uint64, add bool) uint64 {
	current := getUint64FromTree(tree, key)
	var newValue uint64
	if add {
		newValue = gnsmath.SafeAddUint64(current, delta)
	} else {
		if current < delta {
			panic(makeErrorWithDetails(
				errNotEnoughBalance,
				ufmt.Sprintf("not enough balance: current(%d) < requested(%d)", current, delta),
			))
		}
		newValue = gnsmath.SafeSubUint64(current, delta)
	}

	tree.Set(key, newValue)

	return newValue
}

// milliToSec converts milliseconds to seconds
func milliToSec(ms int64) int64 {
	var msPerSec int64 = 1000
	return ms / msPerSec
}

// userHistoryKeySeparator is chosen to be lexicographically smaller than any
const (
	userHistoryKeySeparator         = "|"
	userHistoryKeySeparatorNextWord = string(int32('|') + 1)
)

// userHistoryTimestampWidth is the zero-padded width that fits the maximum
const userHistoryTimestampWidth = 20

// makeUserHistoryKey builds the composite key "addr|paddedTimestamp" used in the user delegation history BPTree.
func makeUserHistoryKey(addrStr string, timestamp int64) string {
	return addrStr + userHistoryKeySeparator + padTimestamp(timestamp)
}

// userHistoryKeyRange returns the half-open prefix range that covers all composite keys belonging to addrStr.
func userHistoryKeyRange(addrStr string) (lo, hi string) {
	lo = addrStr + userHistoryKeySeparator
	hi = addrStr + userHistoryKeySeparatorNextWord

	return lo, hi
}

// padTimestamp left-pads timestamp with zeros to userHistoryTimestampWidth so
// that lexicographic ordering of the resulting strings matches numeric order.
func padTimestamp(timestamp int64) string {
	s := strconv.FormatInt(timestamp, 10)
	if len(s) >= userHistoryTimestampWidth {
		return s
	}

	pad := make([]byte, userHistoryTimestampWidth-len(s))
	for i := range pad {
		pad[i] = '0'
	}

	return string(pad) + s
}

// parseUserHistoryKey splits a composite key produced by makeUserHistoryKey back into its address and timestamp components.
func parseUserHistoryKey(key string) (addrStr string, timestamp int64, ok bool) {
	for i := len(key) - 1; i >= 0; i-- {
		if key[i] == userHistoryKeySeparator[0] {
			ts, err := strconv.ParseInt(key[i+1:], 10, 64)
			if err != nil {
				return "", 0, false
			}

			return key[:i], ts, true
		}
	}

	return "", 0, false
}