package mock

import (
	"context"
	"fmt"
	"math/rand"

	"github.com/ipfs/go-cid"

	"github.com/filecoin-project/go-address"
	"github.com/filecoin-project/go-state-types/abi"
	"github.com/filecoin-project/go-state-types/crypto"

	"github.com/filecoin-project/lotus/api"
	"github.com/filecoin-project/lotus/build"
	"github.com/filecoin-project/lotus/chain/types"
	"github.com/filecoin-project/lotus/chain/wallet"
)

func Address(i uint64) address.Address {
	a, err := address.NewIDAddress(i)
	if err != nil {
		panic(err)
	}
	return a
}

func MkMessage(from, to address.Address, nonce uint64, w *wallet.LocalWallet) *types.SignedMessage {
	msg := UnsignedMessage(from, to, nonce)

	sig, err := w.WalletSign(context.TODO(), from, msg.Cid().Bytes(), api.MsgMeta{})
	if err != nil {
		panic(err)
	}
	return &types.SignedMessage{
		Message:   *msg,
		Signature: *sig,
	}
}

func MkBlock(parents *types.TipSet, weightInc uint64, ticketNonce uint64) *types.BlockHeader {
	addr := Address(123561)

	c, err := cid.Decode("bafyreicmaj5hhoy5mgqvamfhgexxyergw7hdeshizghodwkjg6qmpoco7i")
	if err != nil {
		panic(err)
	}

	pstateRoot := c
	if parents != nil {
		pstateRoot = parents.Blocks()[0].ParentStateRoot
	}

	var pcids []cid.Cid
	var height abi.ChainEpoch
	weight := types.NewInt(weightInc)
	var timestamp uint64
	if parents != nil {
		pcids = parents.Cids()
		height = parents.Height() + 1
		timestamp = parents.MinTimestamp() + build.BlockDelaySecs
		weight = types.BigAdd(parents.Blocks()[0].ParentWeight, weight)
	}

	return &types.BlockHeader{
		Miner: addr,
		ElectionProof: &types.ElectionProof{
			VRFProof: []byte(fmt.Sprintf("====%d=====", ticketNonce)),
		},
		Ticket: &types.Ticket{
			VRFProof: []byte(fmt.Sprintf("====%d=====", ticketNonce)),
		},
		Parents:               pcids,
		ParentMessageReceipts: c,
		BLSAggregate:          &crypto.Signature{Type: crypto.SigTypeBLS, Data: []byte("boo! im a signature")},
		ParentWeight:          weight,
		Messages:              c,
		Height:                height,
		Timestamp:             timestamp,
		ParentStateRoot:       pstateRoot,
		BlockSig:              &crypto.Signature{Type: crypto.SigTypeBLS, Data: []byte("boo! im a signature")},
		ParentBaseFee:         types.NewInt(uint64(build.MinimumBaseFee)),
	}
}

func TipSet(blks ...*types.BlockHeader) *types.TipSet {
	ts, err := types.NewTipSet(blks)
	if err != nil {
		panic(err)
	}
	return ts
}

// Generates count new addresses using the provided seed, and returns them
func RandomActorAddresses(seed int64, count int) ([]*address.Address, error) {
	randAddrs := make([]*address.Address, count)
	source := rand.New(rand.NewSource(seed))
	for i := 0; i < count; i++ {
		bytes := make([]byte, 32)
		_, err := source.Read(bytes)
		if err != nil {
			return nil, err
		}

		addr, err := address.NewActorAddress(bytes)
		if err != nil {
			return nil, err
		}
		randAddrs[i] = &addr
	}
	return randAddrs, nil
}

func UnsignedMessage(from, to address.Address, nonce uint64) *types.Message {
	return &types.Message{
		To:         to,
		From:       from,
		Value:      types.NewInt(1),
		Nonce:      nonce,
		GasLimit:   1000000,
		GasFeeCap:  types.NewInt(100),
		GasPremium: types.NewInt(1),
	}
}
