package generate

import (
	"fmt"

	"git.frostfs.info/TrueCloudLab/frostfs-node/cmd/frostfs-adm/internal/commonflags"
	"github.com/nspcc-dev/neo-go/pkg/encoding/address"
	"github.com/nspcc-dev/neo-go/pkg/util"
	"github.com/nspcc-dev/neo-go/pkg/wallet"
	"github.com/spf13/cobra"
	"github.com/spf13/viper"
)

const (
	storageWalletLabelFlag = "label"
	storageGasCLIFlag      = "initial-gas"
	storageGasConfigFlag   = "storage.initial_gas"
	walletAddressFlag      = "wallet-address"
)

var (
	GenerateStorageCmd = &cobra.Command{
		Use:   "generate-storage-wallet",
		Short: "Generate storage node wallet for the morph network",
		PreRun: func(cmd *cobra.Command, _ []string) {
			_ = viper.BindPFlag(commonflags.AlphabetWalletsFlag, cmd.Flags().Lookup(commonflags.AlphabetWalletsFlag))
			_ = viper.BindPFlag(commonflags.EndpointFlag, cmd.Flags().Lookup(commonflags.EndpointFlag))
			_ = viper.BindPFlag(storageGasConfigFlag, cmd.Flags().Lookup(storageGasCLIFlag))
		},
		RunE: generateStorageCreds,
	}
	RefillGasCmd = &cobra.Command{
		Use:   "refill-gas",
		Short: "Refill GAS of storage node's wallet in the morph network",
		PreRun: func(cmd *cobra.Command, _ []string) {
			_ = viper.BindPFlag(commonflags.AlphabetWalletsFlag, cmd.Flags().Lookup(commonflags.AlphabetWalletsFlag))
			_ = viper.BindPFlag(commonflags.EndpointFlag, cmd.Flags().Lookup(commonflags.EndpointFlag))
			_ = viper.BindPFlag(commonflags.RefillGasAmountFlag, cmd.Flags().Lookup(commonflags.RefillGasAmountFlag))
		},
		RunE: func(cmd *cobra.Command, _ []string) error {
			// storage wallet path is not part of the config
			storageWalletPath, _ := cmd.Flags().GetString(commonflags.StorageWalletFlag)
			// wallet address is not part of the config
			walletAddress, _ := cmd.Flags().GetString(walletAddressFlag)

			var gasReceiver util.Uint160

			if len(walletAddress) != 0 {
				var err error
				gasReceiver, err = address.StringToUint160(walletAddress)
				if err != nil {
					return fmt.Errorf("invalid wallet address %s: %w", walletAddress, err)
				}
			} else {
				if storageWalletPath == "" {
					return fmt.Errorf("missing wallet path (use '--%s <wallet.json>')", commonflags.StorageWalletFlag)
				}

				w, err := wallet.NewWalletFromFile(storageWalletPath)
				if err != nil {
					return fmt.Errorf("can't create wallet: %w", err)
				}

				gasReceiver = w.Accounts[0].Contract.ScriptHash()
			}
			return refillGas(cmd, commonflags.RefillGasAmountFlag, gasReceiver)
		},
	}
	GenerateAlphabetCmd = &cobra.Command{
		Use:   "generate-alphabet",
		Short: "Generate alphabet wallets for consensus nodes of the morph network",
		PreRun: func(cmd *cobra.Command, _ []string) {
			// PreRun fixes https://github.com/spf13/viper/issues/233
			_ = viper.BindPFlag(commonflags.AlphabetWalletsFlag, cmd.Flags().Lookup(commonflags.AlphabetWalletsFlag))
		},
		RunE: AlphabetCreds,
	}
)

func initRefillGasCmd() {
	RefillGasCmd.Flags().String(commonflags.AlphabetWalletsFlag, "", commonflags.AlphabetWalletsFlagDesc)
	RefillGasCmd.Flags().StringP(commonflags.EndpointFlag, commonflags.EndpointFlagShort, "", commonflags.EndpointFlagDesc)
	RefillGasCmd.Flags().String(commonflags.StorageWalletFlag, "", "Path to storage node wallet")
	RefillGasCmd.Flags().String(walletAddressFlag, "", "Address of wallet")
	RefillGasCmd.Flags().String(commonflags.RefillGasAmountFlag, "", "Additional amount of GAS to transfer")
	RefillGasCmd.MarkFlagsMutuallyExclusive(walletAddressFlag, commonflags.StorageWalletFlag)
}

func initGenerateStorageCmd() {
	GenerateStorageCmd.Flags().String(commonflags.AlphabetWalletsFlag, "", commonflags.AlphabetWalletsFlagDesc)
	GenerateStorageCmd.Flags().StringP(commonflags.EndpointFlag, commonflags.EndpointFlagShort, "", commonflags.EndpointFlagDesc)
	GenerateStorageCmd.Flags().String(commonflags.StorageWalletFlag, "", "Path to new storage node wallet")
	GenerateStorageCmd.Flags().String(storageGasCLIFlag, "", "Initial amount of GAS to transfer")
	GenerateStorageCmd.Flags().StringP(storageWalletLabelFlag, "l", "", "Wallet label")
}

func initGenerateAlphabetCmd() {
	GenerateAlphabetCmd.Flags().String(commonflags.AlphabetWalletsFlag, "", commonflags.AlphabetWalletsFlagDesc)
	GenerateAlphabetCmd.Flags().Uint(commonflags.AlphabetSizeFlag, 7, "Amount of alphabet wallets to generate")
}

func init() {
	initRefillGasCmd()
	initGenerateStorageCmd()
	initGenerateAlphabetCmd()
}