package container

import (
	"bytes"
	"errors"
	"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"
	"github.com/spf13/cobra"
)

var flagVarsSetEACL struct {
	noPreCheck bool

	srcPath string
}

var setExtendedACLCmd = &cobra.Command{
	Use:   "set-eacl",
	Short: "Set new extended ACL table for container",
	Long: `Set new extended ACL table for container.
Container ID in EACL table will be substituted with ID from the CLI.`,
	Run: func(cmd *cobra.Command, _ []string) {
		id := parseContainerID(cmd)
		eaclTable := common.ReadEACL(cmd, flagVarsSetEACL.srcPath)

		tok := getSession(cmd)

		eaclTable.SetCID(id)

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

		if !flagVarsSetEACL.noPreCheck {
			cmd.Println("Checking the ability to modify access rights in the container...")

			extendable, err := internalclient.IsACLExtendable(cmd.Context(), cli, id)
			commonCmd.ExitOnErr(cmd, "Extensibility check failure: %w", err)

			if !extendable {
				commonCmd.ExitOnErr(cmd, "", errors.New("container ACL is immutable"))
			}

			cmd.Println("ACL extension is enabled in the container, continue processing.")
		}

		setEACLPrm := internalclient.SetEACLPrm{
			Client: cli,
			ClientParams: client.PrmContainerSetEACL{
				Table:   eaclTable,
				Session: tok,
			},
		}

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

		if containerAwait {
			exp, err := eaclTable.Marshal()
			commonCmd.ExitOnErr(cmd, "broken EACL table: %w", err)

			cmd.Println("awaiting...")

			getEACLPrm := internalclient.EACLPrm{
				Client: cli,
				ClientParams: client.PrmContainerEACL{
					ContainerID: &id,
				},
			}

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

				res, err := internalclient.EACL(cmd.Context(), getEACLPrm)
				if err == nil {
					// compare binary values because EACL could have been set already
					table := res.EACL()
					got, err := table.Marshal()
					if err != nil {
						continue
					}

					if bytes.Equal(exp, got) {
						cmd.Println("EACL has been persisted on sidechain")
						return
					}
				}
			}

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

func initContainerSetEACLCmd() {
	commonflags.Init(setExtendedACLCmd)

	flags := setExtendedACLCmd.Flags()
	flags.StringVar(&containerID, commonflags.CIDFlag, "", commonflags.CIDFlagUsage)
	flags.StringVar(&flagVarsSetEACL.srcPath, "table", "", "path to file with JSON or binary encoded EACL table")
	flags.BoolVar(&containerAwait, "await", false, "block execution until EACL is persisted")
	flags.BoolVar(&flagVarsSetEACL.noPreCheck, "no-precheck", false, "do not pre-check the extensibility of the container ACL")
}