package test

import (
	"context"
	"testing"
	"time"

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

	"github.com/filecoin-project/go-address"
	"github.com/filecoin-project/lotus/chain/types"
	"github.com/filecoin-project/lotus/miner"
)

func SendFunds(ctx context.Context, t *testing.T, sender TestNode, addr address.Address, amount abi.TokenAmount) {
	senderAddr, err := sender.WalletDefaultAddress(ctx)
	if err != nil {
		t.Fatal(err)
	}

	msg := &types.Message{
		From:  senderAddr,
		To:    addr,
		Value: amount,
	}

	sm, err := sender.MpoolPushMessage(ctx, msg, nil)
	if err != nil {
		t.Fatal(err)
	}
	res, err := sender.StateWaitMsg(ctx, sm.Cid(), 1)
	if err != nil {
		t.Fatal(err)
	}
	if res.Receipt.ExitCode != 0 {
		t.Fatal("did not successfully send money")
	}
}

func MineUntilBlock(ctx context.Context, t *testing.T, fn TestNode, sn TestStorageNode, cb func(abi.ChainEpoch)) {
	for i := 0; i < 1000; i++ {
		var success bool
		var err error
		var epoch abi.ChainEpoch
		wait := make(chan struct{})
		mineErr := sn.MineOne(ctx, miner.MineReq{
			Done: func(win bool, ep abi.ChainEpoch, e error) {
				success = win
				err = e
				epoch = ep
				wait <- struct{}{}
			},
		})
		if mineErr != nil {
			t.Fatal(mineErr)
		}
		<-wait
		if err != nil {
			t.Fatal(err)
		}
		if success {
			// Wait until it shows up on the given full nodes ChainHead
			nloops := 50
			for i := 0; i < nloops; i++ {
				ts, err := fn.ChainHead(ctx)
				if err != nil {
					t.Fatal(err)
				}
				if ts.Height() == epoch {
					break
				}
				if i == nloops-1 {
					t.Fatal("block never managed to sync to node")
				}
				time.Sleep(time.Millisecond * 10)
			}

			if cb != nil {
				cb(epoch)
			}
			return
		}
		t.Log("did not mine block, trying again", i)
	}
	t.Fatal("failed to mine 1000 times in a row...")
}