package container

import (
	internalclient "git.frostfs.info/TrueCloudLab/frostfs-node/cmd/frostfs-cli/internal/client"
	"git.frostfs.info/TrueCloudLab/frostfs-node/cmd/frostfs-cli/internal/commonflags"
	"git.frostfs.info/TrueCloudLab/frostfs-node/cmd/frostfs-cli/internal/key"
	commonCmd "git.frostfs.info/TrueCloudLab/frostfs-node/cmd/internal/common"
	containerSDK "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/container"
	cid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/container/id"
	"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/user"
	"github.com/spf13/cobra"
	"google.golang.org/grpc/codes"
	"google.golang.org/grpc/status"
)

// flags of list command.
const (
	flagListPrintAttr      = "with-attr"
	flagListContainerOwner = "owner"
	flagListName           = "name"

	generateKeyContainerUsage = commonflags.GenerateKeyUsage + ", should be used with --owner flag"
)

// flag vars of list command.
var (
	flagVarListPrintAttr      bool
	flagVarListContainerOwner string
	flagVarListName           string
)

var listContainersCmd = &cobra.Command{
	Use:   "list",
	Short: "List all created containers",
	Long:  "List all created containers",
	Run: func(cmd *cobra.Command, _ []string) {
		var idUser user.ID

		generateKey, _ := cmd.Flags().GetBool(commonflags.GenerateKey)
		if flagVarListContainerOwner == "" && generateKey {
			cmd.PrintErrln("WARN: using -g without --owner - output will be empty")
		}

		key := key.GetOrGenerate(cmd)

		if flagVarListContainerOwner == "" {
			user.IDFromKey(&idUser, key.PublicKey)
		} else {
			err := idUser.DecodeString(flagVarListContainerOwner)
			commonCmd.ExitOnErr(cmd, "invalid user ID: %w", err)
		}

		cli := internalclient.GetSDKClientByFlag(cmd, key, commonflags.RPC)

		var prm internalclient.ListContainersPrm
		prm.SetClient(cli)
		prm.OwnerID = idUser
		prmGet := internalclient.GetContainerPrm{
			Client: cli,
		}
		var containerIDs []cid.ID

		err := internalclient.ListContainersStream(cmd.Context(), prm, func(id cid.ID) bool {
			printContainer(cmd, prmGet, id)
			return false
		})
		if err == nil {
			return
		}

		if e, ok := status.FromError(err); ok && e.Code() == codes.Unimplemented {
			res, err := internalclient.ListContainers(cmd.Context(), prm)
			commonCmd.ExitOnErr(cmd, "rpc error: %w", err)
			containerIDs = res.SortedIDList()
		} else {
			commonCmd.ExitOnErr(cmd, "rpc error: %w", err)
		}

		for _, cnrID := range containerIDs {
			printContainer(cmd, prmGet, cnrID)
		}
	},
}

func printContainer(cmd *cobra.Command, prmGet internalclient.GetContainerPrm, id cid.ID) {
	if flagVarListName == "" && !flagVarListPrintAttr {
		cmd.Println(id.String())
		return
	}

	prmGet.ClientParams.ContainerID = &id
	res, err := internalclient.GetContainer(cmd.Context(), prmGet)
	if err != nil {
		cmd.Printf("  failed to read attributes: %v\n", err)
		return
	}

	cnr := res.Container()
	if cnrName := containerSDK.Name(cnr); flagVarListName != "" && cnrName != flagVarListName {
		return
	}
	cmd.Println(id.String())

	if flagVarListPrintAttr {
		cnr.IterateUserAttributes(func(key, val string) {
			cmd.Printf("  %s: %s\n", key, val)
		})
	}
}

func initContainerListContainersCmd() {
	commonflags.Init(listContainersCmd)

	flags := listContainersCmd.Flags()

	flags.StringVar(&flagVarListName, flagListName, "",
		"List containers by the attribute name",
	)
	flags.StringVar(&flagVarListContainerOwner, flagListContainerOwner, "",
		"Owner of containers (omit to use owner from private key)",
	)
	flags.BoolVar(&flagVarListPrintAttr, flagListPrintAttr, false,
		"Request and print attributes of each container",
	)
	flags.Lookup(commonflags.GenerateKey).Usage = generateKeyContainerUsage
}