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 {
			storageWalletPaths, _ := cmd.Flags().GetStringArray(commonflags.StorageWalletFlag)
			walletAddresses, _ := cmd.Flags().GetStringArray(walletAddressFlag)

			var gasReceivers []util.Uint160
			for _, walletAddress := range walletAddresses {
				addr, err := address.StringToUint160(walletAddress)
				if err != nil {
					return fmt.Errorf("invalid wallet address %s: %w", walletAddress, err)
				}

				gasReceivers = append(gasReceivers, addr)
			}
			for _, storageWalletPath := range storageWalletPaths {
				w, err := wallet.NewWalletFromFile(storageWalletPath)
				if err != nil {
					return fmt.Errorf("can't create wallet: %w", err)
				}

				gasReceivers = append(gasReceivers, w.Accounts[0].Contract.ScriptHash())
			}
			return refillGas(cmd, commonflags.RefillGasAmountFlag, gasReceivers...)
		},
	}
	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().StringArray(commonflags.StorageWalletFlag, nil, "Path to storage node wallet")
	RefillGasCmd.Flags().StringArray(walletAddressFlag, nil, "Address of wallet")
	RefillGasCmd.Flags().String(commonflags.RefillGasAmountFlag, "", "Additional amount of GAS to transfer")
	RefillGasCmd.MarkFlagsOneRequired(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()
}