package compatibility

import (
	"context"
	"testing"
	"time"

	searchsemantic "github.com/pingcap-inc/nexuscrm-contract/pkg/runtime/searchv1"
	runtimev1 "github.com/pingcap-inc/nexuscrm-contract/wire-go/gen/runtime/v1"
	"github.com/pingcap-inc/nexuscrm-contract/wire-go/mapping"
	"google.golang.org/protobuf/proto"
	"google.golang.org/protobuf/reflect/protodesc"
	"google.golang.org/protobuf/reflect/protoregistry"
	"google.golang.org/protobuf/types/dynamicpb"
)

func TestSearchObjectPlansAreAdditiveAndFailClosedAcrossNAndNMinusOne(t *testing.T) {
	ctx, cancel := context.WithTimeout(context.Background(), time.Second)
	defer cancel()
	planned := searchsemantic.Request{
		Term: "acme", Limit: 20,
		ObjectPlans: []searchsemantic.ObjectPlan{{
			ObjectID: "Account", SearchFields: []string{"Name"},
			SelectedFields: []string{"Id", "Name"}, Limit: 20,
		}},
	}
	wirePlanned, err := mapping.ToWireSearchRequest(ctx, planned)
	if err != nil {
		t.Fatal(err)
	}
	encoded, err := proto.Marshal(wirePlanned)
	if err != nil {
		t.Fatal(err)
	}

	file := protodesc.ToFileDescriptorProto(runtimev1.File_runtime_v1_search_proto)
	for _, message := range file.GetMessageType() {
		if message.GetName() != "SearchRecordsRequest" {
			continue
		}
		fields := message.Field[:0]
		for _, field := range message.GetField() {
			if field.GetNumber() != 7 {
				fields = append(fields, field)
			}
		}
		message.Field = fields
	}
	oldFile, err := protodesc.NewFile(file, protoregistry.GlobalFiles)
	if err != nil {
		t.Fatal(err)
	}
	oldDescriptor := oldFile.Messages().ByName("SearchRecordsRequest")
	oldRequest := dynamicpb.NewMessage(oldDescriptor)
	if err := (proto.UnmarshalOptions{DiscardUnknown: true}).Unmarshal(encoded, oldRequest); err != nil {
		t.Fatalf("N-1 decode of N request: %v", err)
	}
	oldEncoded, err := proto.Marshal(oldRequest)
	if err != nil {
		t.Fatal(err)
	}
	roundWire := &runtimev1.SearchRecordsRequest{}
	if err := proto.Unmarshal(oldEncoded, roundWire); err != nil {
		t.Fatal(err)
	}
	if _, err := mapping.FromWireSearchRequest(ctx, roundWire); err == nil {
		t.Fatal("N object-plan request was silently reinterpreted by the N-1 shape")
	}

	legacy := searchsemantic.Request{
		ObjectIDs: []string{"Account"}, Term: "acme",
		SearchFields: []string{"Name"}, SelectedFields: []string{"Id", "Name"}, Limit: 20,
	}
	wireLegacy, err := mapping.ToWireSearchRequest(ctx, legacy)
	if err != nil {
		t.Fatal(err)
	}
	if _, err := mapping.FromWireSearchRequest(ctx, wireLegacy); err != nil {
		t.Fatalf("N server rejected the released N-1 request shape: %v", err)
	}
}
