package compatibility

import (
	"reflect"
	"testing"

	runtimev1 "github.com/pingcap-inc/nexuscrm-contract/wire-go/gen/runtime/v1"
	"google.golang.org/protobuf/reflect/protoreflect"
)

func TestApplicationResultWireIsSeparateFromCommandAcceptance(t *testing.T) {
	resultFile := (&runtimev1.GetApplicationResultRequest{}).ProtoReflect().Descriptor().ParentFile()
	if resultFile.Services().Len() != 1 {
		t.Fatalf("application-result services = %d, want 1", resultFile.Services().Len())
	}
	resultService := resultFile.Services().ByName("ApplicationResultService")
	if resultService == nil || resultService.Methods().Len() != 1 || resultService.Methods().Get(0).Name() != "GetApplicationResult" {
		t.Fatalf("application-result service = %#v", resultService)
	}

	commandFile := (&runtimev1.AcceptCommandRequest{}).ProtoReflect().Descriptor().ParentFile()
	commandService := commandFile.Services().ByName("CommandAcceptanceService")
	if commandService == nil || commandService.Methods().Len() != 1 || commandService.Methods().Get(0).Name() != "AcceptCommand" {
		t.Fatalf("command-acceptance service changed = %#v", commandService)
	}

	assertFieldNumbers(t, (&runtimev1.GetApplicationResultRequest{}).ProtoReflect().Descriptor(), []protoreflect.FieldNumber{1, 2})
	assertFieldNumbers(t, (&runtimev1.ApplicationResult{}).ProtoReflect().Descriptor(), []protoreflect.FieldNumber{1, 2, 3, 4, 5, 6})
	response := (&runtimev1.GetApplicationResultResponse{}).ProtoReflect().Descriptor()
	assertFieldNumbers(t, response, []protoreflect.FieldNumber{1, 2, 3, 4})
	if !response.Fields().ByName("result").HasPresence() {
		t.Fatal("terminal result lost protobuf message presence")
	}

	if got, want := []int32{
		int32(runtimev1.ApplicationResultState_APPLICATION_RESULT_STATE_UNSPECIFIED),
		int32(runtimev1.ApplicationResultState_APPLICATION_RESULT_STATE_PENDING),
		int32(runtimev1.ApplicationResultState_APPLICATION_RESULT_STATE_TERMINAL),
	}, []int32{0, 1, 2}; !reflect.DeepEqual(got, want) {
		t.Fatalf("result state values = %v, want %v", got, want)
	}
	if got, want := []int32{
		int32(runtimev1.ApplicationResultOutcome_APPLICATION_RESULT_OUTCOME_UNSPECIFIED),
		int32(runtimev1.ApplicationResultOutcome_APPLICATION_RESULT_OUTCOME_APPLIED),
		int32(runtimev1.ApplicationResultOutcome_APPLICATION_RESULT_OUTCOME_NOOP),
		int32(runtimev1.ApplicationResultOutcome_APPLICATION_RESULT_OUTCOME_REJECTED),
		int32(runtimev1.ApplicationResultOutcome_APPLICATION_RESULT_OUTCOME_FAILED),
	}, []int32{0, 1, 2, 3, 4}; !reflect.DeepEqual(got, want) {
		t.Fatalf("result outcome values = %v, want %v", got, want)
	}
	if got, want := []int32{
		int32(runtimev1.ApplicationResultFailureCode_APPLICATION_RESULT_FAILURE_CODE_UNSPECIFIED),
		int32(runtimev1.ApplicationResultFailureCode_APPLICATION_RESULT_FAILURE_CODE_APPLICATION_REJECTED),
		int32(runtimev1.ApplicationResultFailureCode_APPLICATION_RESULT_FAILURE_CODE_RUNTIME_EXECUTION_FAILED),
	}, []int32{0, 1, 2}; !reflect.DeepEqual(got, want) {
		t.Fatalf("result failure-code values = %v, want %v", got, want)
	}
}

func TestApplicationResultWireContainsNoSecondAuthorityOrInternalState(t *testing.T) {
	for _, message := range []protoreflect.MessageDescriptor{
		(&runtimev1.GetApplicationResultRequest{}).ProtoReflect().Descriptor(),
		(&runtimev1.ApplicationResult{}).ProtoReflect().Descriptor(),
		(&runtimev1.GetApplicationResultResponse{}).ProtoReflect().Descriptor(),
	} {
		for _, forbidden := range []protoreflect.Name{
			"execution_ref", "payload", "result_payload", "queue_state", "lease", "retry",
			"last_error", "receipts", "semantic_mapping_revision",
		} {
			if message.Fields().ByName(forbidden) != nil {
				t.Fatalf("forbidden field %q entered %s", forbidden, message.FullName())
			}
		}
	}
}

func assertFieldNumbers(t *testing.T, message protoreflect.MessageDescriptor, want []protoreflect.FieldNumber) {
	t.Helper()
	got := make([]protoreflect.FieldNumber, message.Fields().Len())
	for index := range message.Fields().Len() {
		got[index] = message.Fields().Get(index).Number()
	}
	if !reflect.DeepEqual(got, want) {
		t.Fatalf("%s field numbers = %v, want %v", message.FullName(), got, want)
	}
}
