package morph

import (
	"bytes"
	"fmt"
	"strconv"
	"strings"
	"text/tabwriter"

	commonCmd "git.frostfs.info/TrueCloudLab/frostfs-node/cmd/internal/common"
	"github.com/nspcc-dev/neo-go/pkg/io"
	"github.com/nspcc-dev/neo-go/pkg/rpcclient/invoker"
	"github.com/nspcc-dev/neo-go/pkg/rpcclient/policy"
	"github.com/nspcc-dev/neo-go/pkg/smartcontract/callflag"
	"github.com/nspcc-dev/neo-go/pkg/vm/emit"
	"github.com/spf13/cobra"
	"github.com/spf13/viper"
)

const (
	execFeeParam      = "ExecFeeFactor"
	storagePriceParam = "StoragePrice"
	setFeeParam       = "FeePerByte"
)

func setPolicyCmd(cmd *cobra.Command, args []string) error {
	wCtx, err := newInitializeContext(cmd, viper.GetViper())
	if err != nil {
		return fmt.Errorf("can't to initialize context: %w", err)
	}

	bw := io.NewBufBinWriter()
	for i := range args {
		k, v, found := strings.Cut(args[i], "=")
		if !found {
			return fmt.Errorf("invalid parameter format, must be Parameter=Value")
		}

		switch k {
		case execFeeParam, storagePriceParam, setFeeParam:
		default:
			return fmt.Errorf("parameter must be one of %s, %s and %s", execFeeParam, storagePriceParam, setFeeParam)
		}

		value, err := strconv.ParseUint(v, 10, 32)
		if err != nil {
			return fmt.Errorf("can't parse parameter value '%s': %w", args[1], err)
		}

		emit.AppCall(bw.BinWriter, policy.Hash, "set"+k, callflag.All, int64(value))
	}

	if err := wCtx.sendCommitteeTx(bw.Bytes(), false); err != nil {
		return err
	}

	return wCtx.awaitTx()
}

func dumpPolicyCmd(cmd *cobra.Command, _ []string) error {
	c, err := getN3Client(viper.GetViper())
	commonCmd.ExitOnErr(cmd, "can't create N3 client:", err)

	inv := invoker.New(c, nil)
	policyContract := policy.NewReader(inv)

	execFee, err := policyContract.GetExecFeeFactor()
	commonCmd.ExitOnErr(cmd, "can't get execution fee factor:", err)

	feePerByte, err := policyContract.GetFeePerByte()
	commonCmd.ExitOnErr(cmd, "can't get fee per byte:", err)

	storagePrice, err := policyContract.GetStoragePrice()
	commonCmd.ExitOnErr(cmd, "can't get storage price:", err)

	buf := bytes.NewBuffer(nil)
	tw := tabwriter.NewWriter(buf, 0, 2, 2, ' ', 0)

	_, _ = tw.Write([]byte(fmt.Sprintf("Execution Fee Factor:\t%d (int)\n", execFee)))
	_, _ = tw.Write([]byte(fmt.Sprintf("Fee Per Byte:\t%d (int)\n", feePerByte)))
	_, _ = tw.Write([]byte(fmt.Sprintf("Storage Price:\t%d (int)\n", storagePrice)))

	_ = tw.Flush()
	cmd.Print(buf.String())

	return nil
}