package modules

import (
	"context"

	"go.uber.org/fx"
	"golang.org/x/xerrors"

	"github.com/filecoin-project/lotus/node/impl/full"

	"github.com/filecoin-project/lotus/chain/messagesigner"
	"github.com/filecoin-project/lotus/chain/types"

	"github.com/filecoin-project/go-address"
)

// MpoolNonceAPI substitutes the mpool nonce with an implementation that
// doesn't rely on the mpool - it just gets the nonce from actor state
type MpoolNonceAPI struct {
	fx.In

	StateAPI full.StateAPI
}

// GetNonce gets the nonce from current chain head.
func (a *MpoolNonceAPI) GetNonce(addr address.Address) (uint64, error) {
	ts := a.StateAPI.Chain.GetHeaviestTipSet()

	// make sure we have a key address so we can compare with messages
	keyAddr, err := a.StateAPI.StateManager.ResolveToKeyAddress(context.TODO(), addr, ts)
	if err != nil {
		return 0, err
	}

	// Load the last nonce from the state, if it exists.
	highestNonce := uint64(0)
	if baseActor, err := a.StateAPI.StateManager.LoadActorRaw(context.TODO(), addr, ts.ParentState()); err != nil {
		if !xerrors.Is(err, types.ErrActorNotFound) {
			return 0, err
		}
	} else {
		highestNonce = baseActor.Nonce
	}

	// Otherwise, find the highest nonce in the tipset.
	msgs, err := a.StateAPI.Chain.MessagesForTipset(ts)
	if err != nil {
		return 0, err
	}
	for _, msg := range msgs {
		vmmsg := msg.VMMessage()
		if vmmsg.From != keyAddr {
			continue
		}
		if vmmsg.Nonce >= highestNonce {
			highestNonce = vmmsg.Nonce + 1
		}
	}
	return highestNonce, nil
}

var _ messagesigner.MpoolNonceAPI = (*MpoolNonceAPI)(nil)