package exchain

import (
	nebulav1 "github.com/exchain/go-exchain/exchain/protocol/gen/go/nebula/v1"
	"github.com/google/uuid"
	"github.com/holiman/uint256"
	"github.com/oklog/ulid/v2"
	"math/rand"
	"time"
)

var (
	randReader = rand.New(rand.NewSource(time.Now().UnixNano()))
)

func getHash() []byte {
	d := make([]byte, 32)
	for i := 0; i < 32; i++ {
		d[i] = byte(rand.Intn(128))
	}
	return d
}
func getAddr() []byte {
	d := make([]byte, 20)
	for i := 0; i < 20; i++ {
		d[i] = byte(rand.Intn(128))
	}
	return d
}

func TestMakeBlock(txcount int) *nebulav1.Block {
	block := new(nebulav1.Block)
	block.Header = new(nebulav1.BlockHeader)
	block.Header.Height = uint64(rand.Intn(1000000))
	block.Header.Hash = getHash()
	block.Header.ParentHash = getHash()
	block.Header.Proposer = getAddr()
	block.Header.Timestamp = uint64(time.Now().UnixNano() / 1000 / 1000)
	txs := TestRandomTxs(txcount)
	block.Transactions = &txs
	return block
}

func TestMakeLimitTx() *nebulav1.LimitOrderTransaction {
	price := rand.Intn(1000) + 500
	tx := new(nebulav1.LimitOrderTransaction)

	tx.Pair = "eth_usdt"
	tx.Side = nebulav1.OrderSide_BUY
	tx.Price = uint256.NewInt(uint64(price)).Bytes()
	tx.Quantity = uint256.NewInt(uint64(rand.Intn(1000) + 100)).Bytes()
	return tx
}

func TestMakeMarketTx() *nebulav1.MarketOrderTransaction {
	tx := new(nebulav1.MarketOrderTransaction)

	tx.Pair = "eth_usdt"
	tx.Side = nebulav1.OrderSide_BUY
	tx.Quantity = uint256.NewInt(uint64(rand.Intn(1000) + 100)).Bytes()
	return tx
}

func TestMakeCancelTx() *nebulav1.CancelOrderTransaction {
	tx := new(nebulav1.CancelOrderTransaction)

	tx.OrderId = uuid.NewString()
	return tx
}

func TestMakeTx(tp nebulav1.TxType) *nebulav1.Transaction {
	tx := new(nebulav1.Transaction)
	tx.Proxy = false
	uld, err := ulid.New(ulid.Timestamp(time.Now()), randReader)
	if err != nil {
		panic(err)
	}
	tx.Nonce = uld[:]
	tx.Signature = &nebulav1.Signature{
		// random 32 bytes
		R: getHash(),
		S: getHash(),
		V: 1,
	}
	switch tp {
	case nebulav1.TxType_LimitTx:
		txdata := TestMakeLimitTx()
		tx.TxType = nebulav1.TxType_LimitTx
		tx.Tx = &nebulav1.Transaction_LimitTx{LimitTx: txdata}
	case nebulav1.TxType_MarketTx:
		txdata := TestMakeMarketTx()
		tx.TxType = nebulav1.TxType_MarketTx
		tx.Tx = &nebulav1.Transaction_MarketTx{MarketTx: txdata}
	case nebulav1.TxType_CancelTx:
		txdata := TestMakeCancelTx()
		tx.TxType = nebulav1.TxType_CancelTx
		tx.Tx = &nebulav1.Transaction_CancelTx{CancelTx: txdata}
	default:
		panic("unknown tx type")
	}
	return tx
}

func TestMakeTxs(tp nebulav1.TxType, count int) nebulav1.TransactionList {
	txlist := nebulav1.TransactionList{
		Txs: make([]*nebulav1.Transaction, 0, count),
	}
	for i := 0; i < count; i++ {
		txlist.Txs = append(txlist.Txs, TestMakeTx(tp))
	}
	return txlist
}

func TestRandomTxs(count int) nebulav1.TransactionList {
	txlist := nebulav1.TransactionList{
		Txs: make([]*nebulav1.Transaction, 0, count),
	}
	for i := 0; i < count; i++ {
		tp := nebulav1.TxType_LimitTx + nebulav1.TxType(rand.Intn(3))
		txlist.Txs = append(txlist.Txs, TestMakeTx(tp))
	}
	return txlist
}
