package compatibility_test

import (
	"context"
	"errors"
	"math"
	"reflect"
	"strings"
	"testing"
	"time"

	semantic "github.com/pingcap-inc/nexuscrm-contract/pkg/runtime/queryv1"
	"github.com/pingcap-inc/nexuscrm-contract/wire-go/envelope"
	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"
)

// The semantic budget counts JSON framing and decimal expansion, whereas gRPC
// counts protobuf bytes. Exercise the real uncompressed transport at the exact
// semantic ceiling, rather than assuming the two byte counts are identical.
func TestQueryResponseLimitFitsExistingTransport(t *testing.T) {
	client, service, cleanup := startQueryTestServer(t)
	defer cleanup()
	response := queryResponseAtByteLimit(t)
	service.response = &response
	wire, err := mapping.ToWireQueryRecordsResponse(response)
	if err != nil {
		t.Fatal(err)
	}
	if size := proto.Size(wire); size > envelope.MaxResponseWireBytes {
		t.Fatalf("semantic ceiling does not fit transport: %d > %d", size, envelope.MaxResponseWireBytes)
	}
	t.Logf("semantic bytes=%d protobuf bytes=%d transport headroom=%d", semantic.MaxSemanticResponseBytes, proto.Size(wire), envelope.MaxResponseWireBytes-proto.Size(wire))
	ctx, cancel := context.WithTimeout(withCapability(context.Background(), semantic.CapabilityName), 5*time.Second)
	defer cancel()
	received, err := client.QueryRecords(ctx, &runtimev1.QueryRecordsRequest{ObjectId: "Issue", Selections: queryFieldSelections("Description"), Limit: 1})
	if err != nil {
		t.Fatal(err)
	}
	decoded, err := mapping.FromWireQueryRecordsResponse(received)
	if err != nil || !reflect.DeepEqual(decoded, response) {
		t.Fatalf("complete boundary response failed round trip: %v", err)
	}
	*response.Records[0].Fields[0].Value.String += "x"
	_, err = client.QueryRecords(ctx, &runtimev1.QueryRecordsRequest{ObjectId: "Issue", Selections: queryFieldSelections("Description"), Limit: 1})
	var failure *semantic.Failure
	if decodedErr := mapping.FromQueryStatusError(err); !errors.As(decodedErr, &failure) ||
		failure.Category != semantic.FailureResourceExhausted || failure.LimitKind != semantic.LimitSemanticResponse ||
		failure.Maximum != semantic.MaxSemanticResponseBytes {
		t.Fatalf("overflow lost typed byte limit in transport: %#v, %v", failure, err)
	}
}

func TestQueryHistoryResponseLimitFitsExistingTransport(t *testing.T) {
	text := ""
	response := semantic.HistoryResponse{TotalSize: 1, Done: true,
		Receipts: semantic.Receipts{ProtocolVersion: "1.0", CapabilityVersion: semantic.CapabilityName},
		Rows: []semantic.HistoryRow{{ID: "H1", ParentID: "I1", Field: "Description", NewValue: &text,
			DataType: "string", CreatedDate: "2026-09-08T00:00:00Z", CreatedByID: "U1"}},
	}
	size, err := semantic.SemanticHistoryResponseSize(response)
	if err != nil {
		t.Fatal(err)
	}
	text = strings.Repeat("x", semantic.MaxSemanticResponseBytes-size)
	wire, err := mapping.ToWireQueryHistoryResponse(response)
	if err != nil || proto.Size(wire) > envelope.MaxResponseWireBytes {
		t.Fatalf("history semantic ceiling does not fit transport: bytes=%d error=%v", proto.Size(wire), err)
	}
	t.Logf("history semantic bytes=%d protobuf bytes=%d", semantic.MaxSemanticResponseBytes, proto.Size(wire))
	client, service, cleanup := startQueryTestServer(t)
	defer cleanup()
	service.historyResponse = &response
	ctx, cancel := context.WithTimeout(withCapability(context.Background(), semantic.CapabilityName), 5*time.Second)
	defer cancel()
	received, err := client.QueryHistory(ctx, &runtimev1.QueryHistoryRequest{ParentObjectId: "Issue", SelectedFields: []string{"NewValue"}, Limit: 1})
	if err != nil {
		t.Fatal(err)
	}
	decoded, err := mapping.FromWireQueryHistoryResponse(received)
	if err != nil || !reflect.DeepEqual(decoded, response) {
		t.Fatalf("history boundary response failed round trip: %v", err)
	}
}

func queryResponseAtByteLimit(t *testing.T) semantic.Response {
	t.Helper()
	text := ""
	response := semantic.Response{TotalSize: math.MaxUint64, Done: true, Aggregate: true, TotalSizeUnknown: true,
		Receipts: semantic.Receipts{ProtocolVersion: "1.0", CapabilityVersion: semantic.CapabilityName,
			CatalogRevision: math.MaxUint64, SchemaGeneration: math.MaxUint64, EngineGeneration: math.MaxUint64, SecurityEpoch: math.MaxUint64},
		Records: []semantic.Record{{ObjectID: "Issue", Fields: []semantic.Field{{Name: "Description", Value: semantic.Value{Kind: semantic.ValueString, String: &text}}}}},
	}
	size, err := semantic.SemanticResponseSize(response)
	if err != nil {
		t.Fatal(err)
	}
	text = strings.Repeat("x", semantic.MaxSemanticResponseBytes-size)
	return response
}
