package container

import (
	"fmt"
	"time"

	internalclient "git.frostfs.info/TrueCloudLab/frostfs-node/cmd/frostfs-cli/internal/client"
	"git.frostfs.info/TrueCloudLab/frostfs-node/cmd/frostfs-cli/internal/common"
	"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"
	"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/client"
	objectSDK "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/object"
	"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/user"
	"github.com/spf13/cobra"
)

var deleteContainerCmd = &cobra.Command{
	Use:   "delete",
	Short: "Delete existing container",
	Long: `Delete existing container.
Only owner of the container has a permission to remove container.`,
	Run: func(cmd *cobra.Command, args []string) {
		id := parseContainerID(cmd)

		tok := getSession(cmd)

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

		if force, _ := cmd.Flags().GetBool(commonflags.ForceFlag); !force {
			common.PrintVerbose(cmd, "Reading the container to check ownership...")

			getPrm := internalclient.GetContainerPrm{
				Client: cli,
				ClientParams: client.PrmContainerGet{
					ContainerID: &id,
				},
			}

			resGet, err := internalclient.GetContainer(cmd.Context(), getPrm)
			commonCmd.ExitOnErr(cmd, "can't get the container: %w", err)

			owner := resGet.Container().Owner()

			if tok != nil {
				common.PrintVerbose(cmd, "Checking session issuer...")

				if !tok.Issuer().Equals(owner) {
					commonCmd.ExitOnErr(cmd, "", fmt.Errorf("session issuer differs with the container owner: expected %s, has %s", owner, tok.Issuer()))
				}
			} else {
				common.PrintVerbose(cmd, "Checking provided account...")

				var acc user.ID
				user.IDFromKey(&acc, pk.PublicKey)

				if !acc.Equals(owner) {
					commonCmd.ExitOnErr(cmd, "", fmt.Errorf("provided account differs with the container owner: expected %s, has %s", owner, acc))
				}
			}

			common.PrintVerbose(cmd, "Account matches the container owner.")

			if tok != nil {
				common.PrintVerbose(cmd, "Skip searching for LOCK objects - session provided.")
			} else {
				fs := objectSDK.NewSearchFilters()
				fs.AddTypeFilter(objectSDK.MatchStringEqual, objectSDK.TypeLock)

				var searchPrm internalclient.SearchObjectsPrm
				searchPrm.SetClient(cli)
				searchPrm.SetContainerID(id)
				searchPrm.SetFilters(fs)
				searchPrm.SetTTL(2)

				common.PrintVerbose(cmd, "Searching for LOCK objects...")

				res, err := internalclient.SearchObjects(cmd.Context(), searchPrm)
				commonCmd.ExitOnErr(cmd, "can't search for LOCK objects: %w", err)

				if len(res.IDList()) != 0 {
					commonCmd.ExitOnErr(cmd, "",
						fmt.Errorf("container wasn't removed because LOCK objects were found, "+
							"use --%s flag to remove anyway", commonflags.ForceFlag))
				}
			}
		}

		delPrm := internalclient.DeleteContainerPrm{
			Client: cli,
			ClientParams: client.PrmContainerDelete{
				ContainerID: &id,
				Session:     tok,
			},
		}

		_, err := internalclient.DeleteContainer(cmd.Context(), delPrm)
		commonCmd.ExitOnErr(cmd, "rpc error: %w", err)

		cmd.Println("container delete method invoked")

		if containerAwait {
			cmd.Println("awaiting...")

			getPrm := internalclient.GetContainerPrm{
				Client: cli,
				ClientParams: client.PrmContainerGet{
					ContainerID: &id,
				},
			}

			for i := 0; i < awaitTimeout; i++ {
				time.Sleep(1 * time.Second)

				_, err := internalclient.GetContainer(cmd.Context(), getPrm)
				if err != nil {
					cmd.Println("container has been removed:", containerID)
					return
				}
			}

			commonCmd.ExitOnErr(cmd, "", errDeleteTimeout)
		}
	},
}

func initContainerDeleteCmd() {
	flags := deleteContainerCmd.Flags()

	flags.StringP(commonflags.WalletPath, commonflags.WalletPathShorthand, commonflags.WalletPathDefault, commonflags.WalletPathUsage)
	flags.StringP(commonflags.Account, commonflags.AccountShorthand, commonflags.AccountDefault, commonflags.AccountUsage)
	flags.StringP(commonflags.RPC, commonflags.RPCShorthand, commonflags.RPCDefault, commonflags.RPCUsage)
	flags.Bool(commonflags.TracingFlag, false, commonflags.TracingFlagUsage)

	flags.StringVar(&containerID, commonflags.CIDFlag, "", commonflags.CIDFlagUsage)
	flags.BoolVar(&containerAwait, "await", false, "Block execution until container is removed")
	flags.BoolP(commonflags.ForceFlag, commonflags.ForceFlagShorthand, false, "Skip validation checks (ownership, presence of LOCK objects)")
}