package tool

import (
	"fmt"
	"reflect"
	"strings"

	"google.golang.org/protobuf/proto"
	"google.golang.org/protobuf/types/known/anypb"
	"google.golang.org/protobuf/types/known/structpb"
)

// pb 转 json
func FuseProto(msg proto.Message) (*anypb.Any, error) {
	buf, err := proto.Marshal(msg)
	if err != nil {
		return nil, fmt.Errorf("proto.Marshal: %w", err)
	}
	return &anypb.Any{
		TypeUrl: string(msg.ProtoReflect().Descriptor().FullName()),
		Value:   buf,
	}, nil
}

// ToMap struct 转为 map
func ToMap(s reflect.Value) map[string]any {
	v := make(map[string]any, s.Type().NumField())

	for i := 0; i < s.Type().NumField(); i++ {
		f := s.Type().Field(i)
		if !f.IsExported() {
			continue
		}
		tagName := ``
		if tag, ok := f.Tag.Lookup(`json`); ok {
			tagName = strings.Split(tag, `,`)[0]
		} else {
			tagName = ToCamel(f.Name)
		}
		v[tagName] = ToNormal(s.Field(i))
	}

	return v
}

// ToNormal 映射为普通的类型，方便进行 structpb 转换
func ToNormal(v reflect.Value) any {
	switch v.Kind() {
	case reflect.Int8, reflect.Int16, reflect.Int, reflect.Int32, reflect.Int64:
		return v.Int()
	case reflect.Uint8, reflect.Uint16, reflect.Uint, reflect.Uint32, reflect.Uint64:
		return v.Uint()
	case reflect.Float32, reflect.Float64:
		return v.Float()
	case reflect.Bool:
		return v.Bool()
	case reflect.Invalid:
		return nil
	case reflect.String:
		return v.String()
	case reflect.Array, reflect.Slice:
		var c = make([]any, 0, v.Len())
		for i := 0; i < v.Len(); i++ {
			if v.Index(i).Kind() == reflect.Uint8 { // 字符串切片
				return v.Interface().([]byte) // 转为[]byte
			}
			c = append(c, ToNormal(v.Index(i)))
		}
		return c
	case reflect.Map:

		var c = make(map[string]any, v.Len())
		r := v.MapRange()
		for r.Next() {
			c[fmt.Sprintf(`%v`, r.Key())] = ToNormal(r.Value())
		}
		return c

	case reflect.Struct:
		return ToMap(v)
	case reflect.Ptr:
		return ToNormal(v.Elem())
	default:
		return nil
	}
}

// ToPBValue 转为 *structpb.Value
func ToPBValue(v any) (*structpb.Value, error) {
	return structpb.NewValue(ToNormal(reflect.ValueOf(v)))
}
