package multisig

import (
	"golang.org/x/xerrors"

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

    {{if (le .v 7)}}
	    builtin{{.v}} "github.com/filecoin-project/specs-actors{{.import}}actors/builtin"
	    init{{.v}} "github.com/filecoin-project/specs-actors{{.import}}actors/builtin/init"
	    multisig{{.v}} "github.com/filecoin-project/specs-actors{{.import}}actors/builtin/multisig"
    {{else}}
        actorstypes "github.com/filecoin-project/go-state-types/actors"
        multisig{{.v}} "github.com/filecoin-project/go-state-types/builtin{{.import}}multisig"
        init{{.latestVersion}} "github.com/filecoin-project/go-state-types/builtin/v{{.latestVersion}}/init"
	    "github.com/filecoin-project/go-state-types/manifest"
    {{end}}

    builtintypes "github.com/filecoin-project/go-state-types/builtin"
	"github.com/filecoin-project/lotus/chain/actors"
	init_ "github.com/filecoin-project/lotus/chain/actors/builtin/init"
	"github.com/filecoin-project/lotus/chain/types"
)

type message{{.v}} struct{ {{if (ge .v 2)}}message0{{else}}from address.Address{{end}} }

func (m message{{.v}}) Create(
	signers []address.Address, threshold uint64,
	unlockStart, unlockDuration abi.ChainEpoch,
	initialAmount abi.TokenAmount,
) (*types.Message, error) {

	lenAddrs := uint64(len(signers))

	if lenAddrs < threshold {
		return nil, xerrors.Errorf("cannot require signing of more addresses than provided for multisig")
	}

	if threshold == 0 {
		threshold = lenAddrs
	}

	if m.from == address.Undef {
		return nil, xerrors.Errorf("must provide source address")
	}
{{if (le .v 1)}}
	if unlockStart != 0 {
		return nil, xerrors.Errorf("actors v0 does not support a non-zero vesting start time")
	}
{{end}}
	// Set up constructor parameters for multisig
	msigParams := &multisig{{.v}}.ConstructorParams{
		Signers:               signers,
		NumApprovalsThreshold: threshold,
		UnlockDuration:        unlockDuration,{{if (ge .v 2)}}
		StartEpoch:            unlockStart,{{end}}
	}

	enc, actErr := actors.SerializeParams(msigParams)
	if actErr != nil {
		return nil, actErr
	}

	{{if (le .v 7)}}
        // new actors are created by invoking 'exec' on the init actor with the constructor params
        execParams := &init{{.v}}.ExecParams{
            CodeCID:           builtin{{.v}}.MultisigActorCodeID,
            ConstructorParams: enc,
        }
	{{else}}
        code, ok := actors.GetActorCodeID(actorstypes.Version{{.v}}, manifest.MultisigKey)
        if !ok {
            return nil, xerrors.Errorf("failed to get multisig code ID")
        }

        // new actors are created by invoking 'exec' on the init actor with the constructor params
        execParams := &init{{.latestVersion}}.ExecParams{
            CodeCID:           code,
            ConstructorParams: enc,
        }
	{{end}}

	enc, actErr = actors.SerializeParams(execParams)
	if actErr != nil {
		return nil, actErr
	}

	return &types.Message{
		To:     init_.Address,
		From:   m.from,
		Method: builtintypes.MethodsInit.Exec,
		Params: enc,
		Value:  initialAmount,
	}, nil
}

{{if (le .v 1)}}

func (m message0) Propose(msig, to address.Address, amt abi.TokenAmount,
	method abi.MethodNum, params []byte) (*types.Message, error) {

	if msig == address.Undef {
		return nil, xerrors.Errorf("must provide a multisig address for proposal")
	}

	if to == address.Undef {
		return nil, xerrors.Errorf("must provide a target address for proposal")
	}

	if amt.Sign() == -1 {
		return nil, xerrors.Errorf("must provide a non-negative amount for proposed send")
	}

	if m.from == address.Undef {
		return nil, xerrors.Errorf("must provide source address")
	}

	enc, actErr := actors.SerializeParams(&multisig0.ProposeParams{
		To:     to,
		Value:  amt,
		Method: method,
		Params: params,
	})
	if actErr != nil {
		return nil, xerrors.Errorf("failed to serialize parameters: %w", actErr)
	}

	return &types.Message{
		To:     msig,
		From:   m.from,
		Value:  abi.NewTokenAmount(0),
		Method: builtin0.MethodsMultisig.Propose,
		Params: enc,
	}, nil
}

func (m message0) Approve(msig address.Address, txID uint64, hashData *ProposalHashData) (*types.Message, error) {
	enc, err := txnParams(txID, hashData)
	if err != nil {
		return nil, err
	}

	return &types.Message{
		To:     msig,
		From:   m.from,
		Value:  types.NewInt(0),
		Method: builtin0.MethodsMultisig.Approve,
		Params: enc,
	}, nil
}

func (m message0) Cancel(msig address.Address, txID uint64, hashData *ProposalHashData) (*types.Message, error) {
	enc, err := txnParams(txID, hashData)
	if err != nil {
		return nil, err
	}

	return &types.Message{
		To:     msig,
		From:   m.from,
		Value:  types.NewInt(0),
		Method: builtin0.MethodsMultisig.Cancel,
		Params: enc,
	}, nil
}
{{end}}
