package util

import (
	"context"
	"fmt"
	"net"
	"strings"

	"google.golang.org/grpc/peer"
	"google.golang.org/protobuf/encoding/protojson"
	"google.golang.org/protobuf/proto"
)

var (
	moOpt protojson.MarshalOptions
	uoOpt protojson.UnmarshalOptions
)

func init() {
	moOpt = protojson.MarshalOptions{
		// Multiline:       true,
		AllowPartial:    true,
		UseProtoNames:   true,
		UseEnumNumbers:  true,
		EmitUnpopulated: true,
	}
	uoOpt = protojson.UnmarshalOptions{
		DiscardUnknown: true,
	}
}

// PbCopy
// @description: 将from对象的内容copy给to对象
// parameter:
//		@from:proto.Message
//		@to:proto.Message
// return:
//		@error:如果失败，则返回错误，否则返回nil
func PbCopy(from, to proto.Message) error {
	data, err := proto.Marshal(from)
	if err != nil {
		return err
	}

	return proto.Unmarshal(data, to)
}

// Pb2Json
// @description: 将pb对象转换为json字符串
// parameter:
//		@m:
// return:
//		@string:pb对象的json标识形式
//		@error:
func Pb2Json(m proto.Message) (string, error) {
	str, err := moOpt.Marshal(m)
	if err != nil {
		return "", err
	}

	return string(str), nil
}

// Json2Pb
// @description: 将json字符串转换成对应pb对象
// parameter:
//		@js:
//		@m:
// return:
//		@error:
func Json2Pb(js string, m proto.Message) error {
	return uoOpt.Unmarshal([]byte(js), m)
}

// Marshal
// @description: 序列化pb对象
// parameter:
//		@m:
// return:
//		@[]byte:
//		@error:
func Marshal(m proto.Message) ([]byte, error) {
	data, err := proto.Marshal(m)
	if err != nil {
		return nil, err
	}

	return data, nil
}

// Marshal_Panic
// @description: 序列化pb对象
// parameter:
//		@m:
// return:
//		@[]byte:
func Marshal_Panic(m proto.Message) []byte {
	data, err := Marshal(m)
	if err != nil {
		panic(fmt.Sprintf("pbUtil Marshal pb错误，err:%s", err))
	}

	return data
}

// Unmarshal
// @description: 将数据转换为pb对象
// parameter:
//		@data:
//		@m:
// return:
func Unmarshal(data []byte, m proto.Message) error {
	err := proto.Unmarshal(data, m)
	return err
}

// GetClientIP
// @description: 获取客户端Ip
// parameter:
//		@ctx:grpc底层传递过来的上下文对象
// return:
//		@string:客户端ip
//		@error:错误对象
func GetClientIP(ctx context.Context) (string, error) {
	pr, ok := peer.FromContext(ctx)
	if !ok {
		return "", fmt.Errorf("GetClietIP未获取到客户端ip")
	}
	if pr.Addr == net.Addr(nil) {
		return "", fmt.Errorf("GetClientIP 获取到的peer.Addr=nil")
	}
	addSlice := strings.Split(pr.Addr.String(), ":")

	return addSlice[0], nil
}
