package simulation

import (
	"math/rand"

	"github.com/cosmos/cosmos-sdk/baseapp"
	sdk "github.com/cosmos/cosmos-sdk/types"
	moduletestutil "github.com/cosmos/cosmos-sdk/types/module/testutil"
	simtypes "github.com/cosmos/cosmos-sdk/types/simulation"
	"github.com/cosmos/cosmos-sdk/x/simulation"

	"<%= ModulePath %>/x/<%= ModuleName %>/keeper"
	"<%= ModulePath %>/x/<%= ModuleName %>/types"
)

func SimulateMsgCreate<%= TypeName.PascalCase %>(
	ak types.AuthKeeper,
	bk types.BankKeeper,
	k keeper.Keeper,
	txGen client.TxConfig,
) simtypes.Operation {
	return func(r *rand.Rand, app *baseapp.BaseApp, ctx sdk.Context, accs []simtypes.Account, chainID string,
	) (simtypes.OperationMsg, []simtypes.FutureOperation, error) {
		simAccount, _ := simtypes.RandomAcc(r, accs)

		msg := &types.MsgCreate<%= TypeName.PascalCase %>{
			<%= MsgSigner.UpperCamel %>: simAccount.Address.String(),
		}

		txCtx := simulation.OperationInput{
			R:               r,
			App:             app,
			TxGen:           txGen,
			Cdc:             nil,
			Msg:             msg,
			Context:         ctx,
			SimAccount:      simAccount,
			ModuleName:      types.ModuleName,
			CoinsSpentInMsg: sdk.NewCoins(),
			AccountKeeper:   ak,
			Bankkeeper:      bk,
		}
		return simulation.GenAndDeliverTxWithRandFees(txCtx)
	}
}

func SimulateMsgUpdate<%= TypeName.PascalCase %>(
	ak types.AuthKeeper,
	bk types.BankKeeper,
	k keeper.Keeper,
	txGen client.TxConfig,
) simtypes.Operation {
	return func(r *rand.Rand, app *baseapp.BaseApp, ctx sdk.Context, accs []simtypes.Account, chainID string,
	) (simtypes.OperationMsg, []simtypes.FutureOperation, error) {
		var (
			simAccount = simtypes.Account{}
			<%= TypeName.LowerCamel %> = types.<%= TypeName.PascalCase %>{}
			msg = &types.MsgUpdate<%= TypeName.PascalCase %>{}
			found = false
		)

		var all<%= TypeName.PascalCase %> []types.<%= TypeName.PascalCase %>
		err := k.<%= TypeName.UpperCamel %>.Walk(ctx, nil, func(key uint64, value types.<%= TypeName.PascalCase %>) (stop bool, err error) {
			all<%= TypeName.PascalCase %> = append(all<%= TypeName.PascalCase %>, value)
			return false, nil
		})
		if err != nil {
			panic(err)
		}
		
		for _, obj := range all<%= TypeName.PascalCase %> {
			acc, err := ak.AddressCodec().StringToBytes(obj.<%= MsgSigner.UpperCamel %>)
			if err != nil {
				return simtypes.OperationMsg{}, nil, err
			}
			
			simAccount, found = simtypes.FindAccount(accs, sdk.AccAddress(acc))
			if found {
				<%= TypeName.LowerCamel %> = obj
				break
			}
		}
		if !found {
			return simtypes.NoOpMsg(types.ModuleName, sdk.MsgTypeURL(msg), "<%= TypeName.LowerCamel %> <%= MsgSigner.LowerCamel %> not found"), nil, nil
		}
		msg.<%= MsgSigner.UpperCamel %> = simAccount.Address.String()
		msg.Id = <%= TypeName.LowerCamel %>.Id

		txCtx := simulation.OperationInput{
			R:               r,
			App:             app,
			TxGen:           txGen,
			Cdc:             nil,
			Msg:             msg,
			Context:         ctx,
			SimAccount:      simAccount,
			ModuleName:      types.ModuleName,
			CoinsSpentInMsg: sdk.NewCoins(),
			AccountKeeper:   ak,
			Bankkeeper:      bk,
		}
		return simulation.GenAndDeliverTxWithRandFees(txCtx)
	}
}

func SimulateMsgDelete<%= TypeName.PascalCase %>(
	ak types.AuthKeeper,
	bk types.BankKeeper,
	k keeper.Keeper,
	txGen client.TxConfig,
) simtypes.Operation {
	return func(r *rand.Rand, app *baseapp.BaseApp, ctx sdk.Context, accs []simtypes.Account, chainID string,
	) (simtypes.OperationMsg, []simtypes.FutureOperation, error) {
		var (
			simAccount = simtypes.Account{}
			<%= TypeName.LowerCamel %> = types.<%= TypeName.PascalCase %>{}
			msg = &types.MsgDelete<%= TypeName.PascalCase %>{}
			found = false
		)

		var all<%= TypeName.PascalCase %> []types.<%= TypeName.PascalCase %>
		err := k.<%= TypeName.UpperCamel %>.Walk(ctx, nil, func(key uint64, value types.<%= TypeName.PascalCase %>) (stop bool, err error) {
			all<%= TypeName.PascalCase %> = append(all<%= TypeName.PascalCase %>, value)
			return false, nil
		})
		if err != nil {
			panic(err)
		}

		for _, obj := range all<%= TypeName.PascalCase %> {
			acc, err := ak.AddressCodec().StringToBytes(obj.<%= MsgSigner.UpperCamel %>)
			if err != nil {
				return simtypes.OperationMsg{}, nil, err
			}

			simAccount, found = simtypes.FindAccount(accs, sdk.AccAddress(acc))
			if found {
				<%= TypeName.LowerCamel %> = obj
				break
			}
		}
		if !found {
			return simtypes.NoOpMsg(types.ModuleName, sdk.MsgTypeURL(msg), "<%= TypeName.LowerCamel %> <%= MsgSigner.LowerCamel %> not found"), nil, nil
		}
		msg.<%= MsgSigner.UpperCamel %> = simAccount.Address.String()
		msg.Id = <%= TypeName.LowerCamel %>.Id

		txCtx := simulation.OperationInput{
			R:               r,
			App:             app,
			TxGen:           txGen,
			Cdc:             nil,
			Msg:             msg,
			Context:         ctx,
			SimAccount:      simAccount,
			ModuleName:      types.ModuleName,
			CoinsSpentInMsg: sdk.NewCoins(),
			AccountKeeper:   ak,
			Bankkeeper:      bk,
		}
		return simulation.GenAndDeliverTxWithRandFees(txCtx)
	}
}
