package mapping

import (
	"errors"
	"testing"

	semantic "github.com/pingcap-inc/nexuscrm-contract/pkg/runtime/queryv1"
	commonv1 "github.com/pingcap-inc/nexuscrm-contract/wire-go/gen/common/v1"
	"google.golang.org/grpc/codes"
	"google.golang.org/grpc/status"
)

func TestQueryResponseLimitErrorAcceptsPreviousPeerMaximum(t *testing.T) {
	for _, maximum := range []uint64{1024 * 1024, semantic.MaxSemanticResponseBytes} {
		err := queryResponseLimitStatus(t, maximum)
		var failure *semantic.Failure
		if decoded := FromQueryStatusError(err); !errors.As(decoded, &failure) ||
			failure.Category != semantic.FailureResourceExhausted || failure.LimitKind != semantic.LimitSemanticResponse || failure.Maximum != maximum {
			t.Fatalf("peer maximum %d was lost: %#v", maximum, failure)
		}
	}
	for _, maximum := range []uint64{0, 1024*1024 - 1, semantic.MaxSemanticResponseBytes + 1} {
		var failure *semantic.Failure
		decoded := FromQueryStatusError(queryResponseLimitStatus(t, maximum))
		if !errors.As(decoded, &failure) || failure.Category != semantic.FailureInternal {
			t.Fatalf("unapproved peer maximum %d accepted: %#v", maximum, failure)
		}
	}
	// Accepting the previous peer's error must not authorize new producers to
	// advertise an old budget after adopting the new contract.
	err := ToQueryStatusError(&semantic.Failure{Category: semantic.FailureResourceExhausted, LimitKind: semantic.LimitSemanticResponse, Maximum: 1024 * 1024})
	want := codes.Internal
	if semantic.MaxSemanticResponseBytes == 1024*1024 {
		// The independently released wire module is also tested against its
		// committed semantic pin before the root-first release is repinned.
		want = codes.ResourceExhausted
	}
	if status.Code(err) != want {
		t.Fatalf("new writer accepted previous maximum: %v", err)
	}
}

func queryResponseLimitStatus(t *testing.T, maximum uint64) error {
	t.Helper()
	s, err := status.New(codes.ResourceExhausted, "not public").WithDetails(&commonv1.RuntimeErrorDetail{
		Category: commonv1.ErrorCategory_ERROR_CATEGORY_RESOURCE_EXHAUSTED,
		Detail: &commonv1.RuntimeErrorDetail_LimitViolation{LimitViolation: &commonv1.LimitViolationDetail{
			Kind: commonv1.LimitKind_LIMIT_KIND_SEMANTIC_RESPONSE_BYTES, ApprovedMaximum: maximum,
		}},
	})
	if err != nil {
		t.Fatal(err)
	}
	return s.Err()
}

func TestQueryAggregateProbeRowLimitErrorRoundTrip(t *testing.T) {
	for _, maximum := range []uint64{2000, 2001} {
		err := ToQueryStatusError(&semantic.Failure{Category: semantic.FailureResourceExhausted, LimitKind: semantic.LimitRecordCount, Maximum: maximum})
		var failure *semantic.Failure
		if decoded := FromQueryStatusError(err); !errors.As(decoded, &failure) ||
			failure.Category != semantic.FailureResourceExhausted || failure.LimitKind != semantic.LimitRecordCount || failure.Maximum != maximum {
			t.Fatalf("ordinary/probe limit lost in transport: maximum=%d failure=%#v", maximum, failure)
		}
	}
}
