package tree

import (
	"crypto/sha256"
	"errors"
	"io"

	"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-node/pkg/local_object_storage/pilorama"
	"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/services/tree"
	cid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/container/id"
	"github.com/spf13/cobra"
)

var getOpLogCmd = &cobra.Command{
	Use:   "get-op-log",
	Short: "Get logged operations starting with some height",
	Run:   getOpLog,
	PersistentPreRun: func(cmd *cobra.Command, _ []string) {
		commonflags.Bind(cmd)
	},
}

func initGetOpLogCmd() {
	commonflags.Init(getOpLogCmd)
	initCTID(getOpLogCmd)

	ff := getOpLogCmd.Flags()
	ff.Uint64(heightFlagKey, 0, "Height to start with")
	ff.Uint64(countFlagKey, 10, "Logged operations count")

	_ = cobra.MarkFlagRequired(ff, commonflags.RPC)
}

func getOpLog(cmd *cobra.Command, _ []string) {
	pk := key.GetOrGenerate(cmd)

	cidRaw, _ := cmd.Flags().GetString(commonflags.CIDFlag)

	var cnr cid.ID
	err := cnr.DecodeString(cidRaw)
	commonCmd.ExitOnErr(cmd, "decode container ID string: %w", err)

	tid, _ := cmd.Flags().GetString(treeIDFlagKey)
	ctx := cmd.Context()

	cli, err := _client(ctx)
	commonCmd.ExitOnErr(cmd, "failed to create client: %w", err)

	rawCID := make([]byte, sha256.Size)
	cnr.Encode(rawCID)

	height, _ := cmd.Flags().GetUint64(heightFlagKey)
	count, _ := cmd.Flags().GetUint64(countFlagKey)

	req := &tree.GetOpLogRequest{
		Body: &tree.GetOpLogRequest_Body{
			ContainerId: rawCID,
			TreeId:      tid,
			Height:      height,
			Count:       count,
		},
	}

	commonCmd.ExitOnErr(cmd, "signing message: %w", tree.SignMessage(req, pk))

	resp, err := cli.GetOpLog(ctx, req)
	commonCmd.ExitOnErr(cmd, "get op log: %w", err)

	opLogResp, err := resp.Recv()
	for ; err == nil; opLogResp, err = resp.Recv() {
		o := opLogResp.GetBody().GetOperation()

		cmd.Println("Parent ID: ", o.GetParentId())

		cmd.Println("\tChild ID: ", o.GetChildId())

		m := &pilorama.Meta{}
		err = m.FromBytes(o.GetMeta())
		commonCmd.ExitOnErr(cmd, "could not unmarshal meta: %w", err)
		cmd.Printf("\tMeta:\n")
		cmd.Printf("\t\tTime: %d\n", m.Time)
		for _, item := range m.Items {
			cmd.Printf("\t\t%s: %s\n", item.Key, item.Value)
		}
	}
	if !errors.Is(err, io.EOF) {
		commonCmd.ExitOnErr(cmd, "get op log response stream: %w", err)
	}
}