package rpc

import (
	"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/api/rpc/client"
	"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/api/rpc/common"
	"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/api/tree"
)

const serviceTree = "tree.TreeService"

const (
	rpcTreeAdd           = "Add"
	rpcTreeAddByPath     = "AddByPath"
	rpcTreeRemove        = "Remove"
	rpcTreeMove          = "Move"
	rpcTreeGetNodeByPath = "GetNodeByPath"
	rpcTreeGetSubTree    = "GetSubTree"
	rpcTreeList          = "TreeList"
	rpcTreeApply         = "Apply"
	rpcTreeGetOpLog      = "GetOpLog"
	rpcTreeHealthcheck   = "Healthcheck"
)

func Add(
	cli *client.Client,
	req *tree.AddRequest,
	opts ...client.CallOption,
) (*tree.AddResponse, error) {
	resp := new(tree.AddResponse)
	err := client.SendUnary(cli, common.CallMethodInfoUnary(serviceTree, rpcTreeAdd), req, resp, opts...)
	if err != nil {
		return nil, err
	}

	return resp, nil
}

func AddByPath(
	cli *client.Client,
	req *tree.AddByPathRequest,
	opts ...client.CallOption,
) (*tree.AddByPathResponse, error) {
	resp := new(tree.AddByPathResponse)
	err := client.SendUnary(cli, common.CallMethodInfoUnary(serviceTree, rpcTreeAddByPath), req, resp, opts...)
	if err != nil {
		return nil, err
	}

	return resp, nil
}

func Remove(cli *client.Client,
	req *tree.RemoveRequest,
	opts ...client.CallOption,
) (*tree.RemoveResponse, error) {
	resp := new(tree.RemoveResponse)
	err := client.SendUnary(cli, common.CallMethodInfoUnary(serviceTree, rpcTreeRemove), req, resp, opts...)
	if err != nil {
		return nil, err
	}

	return resp, nil
}

func Move(cli *client.Client,
	req *tree.MoveRequest,
	opts ...client.CallOption,
) (*tree.MoveResponse, error) {
	resp := new(tree.MoveResponse)
	err := client.SendUnary(cli, common.CallMethodInfoUnary(serviceTree, rpcTreeMove), req, resp, opts...)
	if err != nil {
		return nil, err
	}

	return resp, nil
}

func GetNodeByPath(cli *client.Client,
	req *tree.GetNodeByPathRequest,
	opts ...client.CallOption,
) (*tree.GetNodeByPathResponse, error) {
	resp := new(tree.GetNodeByPathResponse)
	err := client.SendUnary(cli, common.CallMethodInfoUnary(serviceTree, rpcTreeGetNodeByPath), req, resp, opts...)
	if err != nil {
		return nil, err
	}

	return resp, nil
}

type GetSubTreeResponseReader struct {
	r client.MessageReader
}

// Read reads response from the stream.
//
// Returns io.EOF of streaming is finished.
func (r *GetSubTreeResponseReader) Read(resp *tree.GetSubTreeResponse) error {
	return r.r.ReadMessage(resp)
}

func GetSubTree(cli *client.Client,
	req *tree.GetSubTreeRequest,
	opts ...client.CallOption,
) (*GetSubTreeResponseReader, error) {
	wc, err := client.OpenServerStream(cli, common.CallMethodInfoServerStream(serviceTree, rpcTreeGetSubTree), req, opts...)
	if err != nil {
		return nil, err
	}

	return &GetSubTreeResponseReader{
		r: wc,
	}, nil
}

func TreeList(cli *client.Client,
	req *tree.ListRequest,
	opts ...client.CallOption,
) (*tree.ListResponse, error) {
	resp := new(tree.ListResponse)
	err := client.SendUnary(cli, common.CallMethodInfoUnary(serviceTree, rpcTreeList), req, resp, opts...)
	if err != nil {
		return nil, err
	}

	return resp, nil
}

func Apply(cli *client.Client,
	req *tree.ApplyRequest,
	opts ...client.CallOption,
) (*tree.ApplyResponse, error) {
	resp := new(tree.ApplyResponse)
	err := client.SendUnary(cli, common.CallMethodInfoUnary(serviceTree, rpcTreeApply), req, resp, opts...)
	if err != nil {
		return nil, err
	}

	return resp, nil
}

type TreeServiceGetOpLogResponseReader struct {
	r client.MessageReader
}

// Read reads response from the stream.
//
// Returns io.EOF of streaming is finished.
func (r *TreeServiceGetOpLogResponseReader) Read(resp *tree.GetOpLogResponse) error {
	return r.r.ReadMessage(resp)
}

func GetOpLog(cli *client.Client,
	req *tree.GetOpLogRequest,
	opts ...client.CallOption,
) (*TreeServiceGetOpLogResponseReader, error) {
	wc, err := client.OpenServerStream(cli, common.CallMethodInfoServerStream(serviceTree, rpcTreeGetOpLog), req, opts...)
	if err != nil {
		return nil, err
	}

	return &TreeServiceGetOpLogResponseReader{
		r: wc,
	}, nil
}

func Healthcheck(cli *client.Client,
	req *tree.HealthcheckRequest,
	opts ...client.CallOption,
) (*tree.HealthcheckResponse, error) {
	resp := new(tree.HealthcheckResponse)
	err := client.SendUnary(cli, common.CallMethodInfoUnary(serviceTree, rpcTreeHealthcheck), req, resp, opts...)
	if err != nil {
		return nil, err
	}

	return resp, nil
}