package runtime

import (
	
	
	
	
	

	
	
	field_mask 
)

func getFieldByName( protoreflect.FieldDescriptors,  string) protoreflect.FieldDescriptor {
	 := .ByName(protoreflect.Name())
	if  != nil {
		return 
	}

	return .ByJSONName()
}

// FieldMaskFromRequestBody creates a FieldMask printing all complete paths from the JSON body.
func ( io.Reader,  proto.Message) (*field_mask.FieldMask, error) {
	 := &field_mask.FieldMask{}
	var  interface{}

	if  := json.NewDecoder().Decode(&);  != nil {
		if errors.Is(, io.EOF) {
			return , nil
		}
		return nil, 
	}

	 := []fieldMaskPathItem{{node: , msg: .ProtoReflect()}}
	for len() > 0 {
		// dequeue an item
		 := [0]
		 = [1:]

		,  := .node.(map[string]interface{})
		switch {
		case  && len() > 0:
			// if the item is an object, then enqueue all of its children
			for ,  := range  {
				if .msg == nil {
					return nil, errors.New("JSON structure did not match request type")
				}

				 := getFieldByName(.msg.Descriptor().Fields(), )
				if  == nil {
					return nil, fmt.Errorf("could not find field %q in %q", , .msg.Descriptor().FullName())
				}

				if isDynamicProtoMessage(.Message()) {
					for ,  := range buildPathsBlindly(string(.FullName().Name()), ) {
						 := 
						if .path != "" {
							 = .path + "." + 
						}
						 = append(, fieldMaskPathItem{path: })
					}
					continue
				}

				if isProtobufAnyMessage(.Message()) && !.IsList() {
					,  := .(map[string]interface{})["@type"]
					if  {
						 = append(, fieldMaskPathItem{path: })
						continue
					} else {
						return nil, fmt.Errorf("could not find field @type in %q in message %q", , .msg.Descriptor().FullName())
					}

				}

				 := fieldMaskPathItem{
					node: ,
				}
				if .path == "" {
					.path = string(.FullName().Name())
				} else {
					.path = .path + "." + string(.FullName().Name())
				}

				switch {
				case .IsList(), .IsMap():
					// As per: https://github.com/protocolbuffers/protobuf/blob/master/src/google/protobuf/field_mask.proto#L85-L86
					// Do not recurse into repeated fields. The repeated field goes on the end of the path and we stop.
					.Paths = append(.Paths, .path)
				case .Message() != nil:
					.msg = .msg.Get().Message()
					fallthrough
				default:
					 = append(, )
				}
			}
		case  && len() == 0:
			fallthrough
		case len(.path) > 0:
			// otherwise, it's a leaf node so print its path
			.Paths = append(.Paths, .path)
		}
	}

	// Sort for deterministic output in the presence
	// of repeated fields.
	sort.Strings(.Paths)

	return , nil
}

func isProtobufAnyMessage( protoreflect.MessageDescriptor) bool {
	return  != nil && (.FullName() == "google.protobuf.Any")
}

func isDynamicProtoMessage( protoreflect.MessageDescriptor) bool {
	return  != nil && (.FullName() == "google.protobuf.Struct" || .FullName() == "google.protobuf.Value")
}

// buildPathsBlindly does not attempt to match proto field names to the
// json value keys.  Instead it relies completely on the structure of
// the unmarshalled json contained within in.
// Returns a slice containing all subpaths with the root at the
// passed in name and json value.
func buildPathsBlindly( string,  interface{}) []string {
	,  := .(map[string]interface{})
	if ! {
		return []string{}
	}

	var  []string
	 := []fieldMaskPathItem{{path: , node: }}
	for len() > 0 {
		 := [0]
		 = [1:]

		,  := .node.(map[string]interface{})
		if ! {
			// This should never happen since we should always check that we only add
			// nodes of type map[string]interface{} to the queue.
			continue
		}
		for ,  := range  {
			if ,  := .(map[string]interface{});  {
				 = append(, fieldMaskPathItem{path: .path + "." + , node: })
			} else {
				// This is not a struct, so there are no more levels to descend.
				 := .path + "." + 
				 = append(, )
			}
		}
	}
	return 
}

// fieldMaskPathItem stores an in-progress deconstruction of a path for a fieldmask
type fieldMaskPathItem struct {
	// the list of prior fields leading up to node connected by dots
	path string

	// a generic decoded json object the current item to inspect for further path extraction
	node interface{}

	// parent message
	msg protoreflect.Message
}