package main

import (
	"fmt"
	"math"

	"github.com/filecoin-project/go-address"
	"github.com/filecoin-project/go-state-types/abi"
	"github.com/filecoin-project/go-state-types/big"
	"github.com/urfave/cli/v2"

	"github.com/filecoin-project/lotus/chain/types"
	lcli "github.com/filecoin-project/lotus/cli"
)

var noncefix = &cli.Command{
	Name: "noncefix",
	Flags: []cli.Flag{
		&cli.StringFlag{
			Name:    "repo",
			EnvVars: []string{"LOTUS_PATH"},
			Hidden:  true,
			Value:   "~/.lotus", // TODO: Consider XDG_DATA_HOME
		},
		&cli.Uint64Flag{
			Name: "start",
		},
		&cli.Uint64Flag{
			Name: "end",
		},
		&cli.StringFlag{
			Name: "addr",
		},
		&cli.BoolFlag{
			Name: "auto",
		},
		&cli.Int64Flag{
			Name:  "gas-fee-cap",
			Usage: "specify gas fee cap for nonce filling messages",
		},
	},
	Action: func(cctx *cli.Context) error {
		api, closer, err := lcli.GetFullNodeAPI(cctx)
		if err != nil {
			return err
		}

		defer closer()
		ctx := lcli.ReqContext(cctx)

		addr, err := address.NewFromString(cctx.String("addr"))
		if err != nil {
			return err
		}

		start := cctx.Uint64("start")
		end := cctx.Uint64("end")
		if end == 0 {
			end = math.MaxUint64
		}

		if cctx.Bool("auto") {
			a, err := api.StateGetActor(ctx, addr, types.EmptyTSK)
			if err != nil {
				return err
			}
			start = a.Nonce

			msgs, err := api.MpoolPending(ctx, types.EmptyTSK)
			if err != nil {
				return err
			}

			for _, msg := range msgs {
				if msg.Message.From != addr {
					continue
				}
				if msg.Message.Nonce < start {
					continue // past
				}
				if msg.Message.Nonce < end {
					end = msg.Message.Nonce
				}
			}

		}
		if end == math.MaxUint64 {
			fmt.Println("No nonce gap found or no --end flag specified")
			return nil
		}
		fmt.Printf("Creating %d filler messages (%d ~ %d)\n", end-start, start, end)

		ts, err := api.ChainHead(ctx)
		if err != nil {
			return err
		}

		feeCap := big.Mul(ts.Blocks()[0].ParentBaseFee, big.NewInt(2)) // default fee cap to 2 * parent base fee
		if fcf := cctx.Int64("gas-fee-cap"); fcf != 0 {
			feeCap = abi.NewTokenAmount(fcf)
		}

		for i := start; i < end; i++ {
			msg := &types.Message{
				From:       addr,
				To:         addr,
				Value:      types.NewInt(0),
				Nonce:      i,
				GasLimit:   1000000,
				GasFeeCap:  feeCap,
				GasPremium: abi.NewTokenAmount(5),
			}
			smsg, err := api.WalletSignMessage(ctx, addr, msg)
			if err != nil {
				return err
			}

			_, err = api.MpoolPush(ctx, smsg)
			if err != nil {
				return err
			}
		}

		return nil
	},
}
