// Copyright 2018 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package main

import (
	"fmt"
	"strings"
	"testing"

	"google.golang.org/protobuf/encoding/prototext"
	"google.golang.org/protobuf/proto"
	pref "google.golang.org/protobuf/reflect/protoreflect"

	"google.golang.org/protobuf/types/descriptorpb"
)

func mustMakeMessage(s string) *descriptorpb.DescriptorProto {
	s = fmt.Sprintf(`name:"test.proto" syntax:"proto2" message_type:[{%s}]`, s)
	pb := new(descriptorpb.FileDescriptorProto)
	if err := prototext.Unmarshal([]byte(s), pb); err != nil {
		panic(err)
	}
	return pb.MessageType[0]
}

func TestFields(t *testing.T) {
	type fieldsKind struct {
		kind   pref.Kind
		fields string
	}
	tests := []struct {
		inFields []fieldsKind
		wantMsg  *descriptorpb.DescriptorProto
		wantErr  string
	}{{
		inFields: []fieldsKind{{pref.MessageKind, ""}},
		wantMsg:  mustMakeMessage(`name:"X"`),
	}, {
		inFields: []fieldsKind{{pref.MessageKind, "987654321"}},
		wantErr:  "invalid field: 987654321",
	}, {
		inFields: []fieldsKind{{pref.MessageKind, "-1"}},
		wantErr:  "invalid field: -1",
	}, {
		inFields: []fieldsKind{{pref.MessageKind, "k"}},
		wantErr:  "invalid field: k",
	}, {
		inFields: []fieldsKind{{pref.MessageKind, "1.2"}, {pref.Int32Kind, "1"}},
		wantErr:  "field 1 of int32 type cannot have sub-fields",
	}, {
		inFields: []fieldsKind{{pref.Int32Kind, "1"}, {pref.MessageKind, "1.2"}},
		wantErr:  "field 1 of int32 type cannot have sub-fields",
	}, {
		inFields: []fieldsKind{{pref.Int32Kind, "30"}, {pref.Int32Kind, "30"}},
		wantErr:  "field 30 already set as int32 type",
	}, {
		inFields: []fieldsKind{
			{pref.Int32Kind, "10.20.31"},
			{pref.MessageKind, "  10.20.30, 10.21   "},
			{pref.GroupKind, "10"},
		},
		wantMsg: mustMakeMessage(`
			name: "X"
			field: [
				{name:"x10" number:10 label:LABEL_OPTIONAL type:TYPE_GROUP type_name:".X.X10"}
			]
			nested_type: [{
				name: "X10"
				field: [
					{name:"x20" number:20 label:LABEL_OPTIONAL type:TYPE_MESSAGE type_name:".X.X10.X20"},
					{name:"x21" number:21 label:LABEL_OPTIONAL type:TYPE_MESSAGE type_name:".X.X10.X21"}
				]
				nested_type: [{
					name: "X20"
					field:[
						{name:"x30" number:30 label:LABEL_OPTIONAL type:TYPE_MESSAGE, type_name:".X.X10.X20.X30"},
						{name:"x31" number:31 label:LABEL_REPEATED type:TYPE_INT32 options:{packed:true}}
					]
					nested_type: [{
						name: "X30"
					}]
				}, {
					name: "X21"
				}]
			}]
		`),
	}}

	for _, tt := range tests {
		t.Run("", func(t *testing.T) {
			var fields fields
			for i, tc := range tt.inFields {
				gotErr := fields.Set(tc.fields, tc.kind)
				if gotErr != nil {
					if tt.wantErr == "" || !strings.Contains(fmt.Sprint(gotErr), tt.wantErr) {
						t.Fatalf("fields %d, Set(%q, %v) = %v, want %v", i, tc.fields, tc.kind, gotErr, tt.wantErr)
					}
					return
				}
			}
			if tt.wantErr != "" {
				t.Errorf("all Set calls succeeded, want %v error", tt.wantErr)
			}
			gotMsg := fields.messageDescriptor("X")
			if !proto.Equal(gotMsg, tt.wantMsg) {
				t.Errorf("messageDescriptor() mismatch:\ngot  %v\nwant %v", gotMsg, tt.wantMsg)
			}
			if _, err := fields.Descriptor(); err != nil {
				t.Errorf("Descriptor() = %v, want nil error", err)
			}
		})
	}
}
