package chaindb

import (
	"fmt"
	"github.com/ethereum/go-ethereum/common"
	"github.com/holiman/uint256"
	"strings"
)

const (
	BlockNumKeyPrefix      = "bn-"
	BlockHeaderPrefix      = "bh-"
	BlockBodyPrefix        = "bd-"
	BlockReceiptsPrefix    = "br-"
	BlockAccountsPrefix    = "bcc-"
	TransactionEntryPrefix = "te"
	TransactionKeyPrefix   = "tr-"
	ReceiptKeyPrefix       = "re-"
	HeightKey              = "hei-chain"
	ChainIdKey             = "chain-id"
	WithdrawalKeyPrefix    = "wd-"
)

func chainIdKey() string {
	return ChainIdKey
}

func chainHeightKey() string {
	return HeightKey
}

func blockHeaderKey(number *uint256.Int) string {
	n := number.String()
	s := make([]byte, 0, len(n)+len(BlockHeaderPrefix))
	s = append(s, BlockHeaderPrefix...)
	s = append(s, n...)
	return string(s)
}

func blockBodyKey(number *uint256.Int) string {
	n := number.String()
	s := make([]byte, 0, len(n)+len(BlockBodyPrefix))
	s = append(s, BlockBodyPrefix...)
	s = append(s, n...)
	return string(s)
}

func blockReceiptsKey(number *uint256.Int) string {
	n := number.String()
	s := make([]byte, 0, len(n)+len(BlockReceiptsPrefix))
	s = append(s, BlockReceiptsPrefix...)
	s = append(s, n...)
	return string(s)
}

func blockNumKey(hash common.Hash) string {
	n := strings.ToLower(hash.String())
	s := make([]byte, 0, len(n)+len(BlockNumKeyPrefix))
	s = append(s, BlockNumKeyPrefix...)
	s = append(s, n...)
	return string(s)
}

func txEntryKey(hash common.Hash) string {
	h := hash.String()
	s := make([]byte, 0, len(h)+len(TransactionEntryPrefix))
	s = append(s, TransactionEntryPrefix...)
	s = append(s, h...)
	return string(s)
}

func transactionKey(hash common.Hash) string {
	h := hash.String()
	s := make([]byte, 0, len(h)+len(TransactionKeyPrefix))
	s = append(s, TransactionKeyPrefix...)
	s = append(s, h...)
	return string(s)
}

func receiptKey(hash common.Hash) string {
	h := hash.String()
	s := make([]byte, 0, len(h)+len(ReceiptKeyPrefix))
	s = append(s, ReceiptKeyPrefix...)
	s = append(s, h...)
	return string(s)
}

var (
	checkpointInterval = uint256.NewInt(120)
)

func withdrawalKey(number *uint256.Int, hash common.Hash) string {
	h := hash.String()
	cp := new(uint256.Int).Div(number, checkpointInterval).Uint64()
	s := make([]byte, 0)
	checkPointPrefix := fmt.Sprintf("%s-%d-", WithdrawalKeyPrefix, cp+1)
	s = append(s, checkPointPrefix...)
	s = append(s, h...)
	return string(s)
}
