package compatibility_test

import (
	"encoding/json"
	"math"
	"os"
	"reflect"
	"strings"
	"testing"

	semantic "github.com/pingcap-inc/nexuscrm-contract/pkg/runtime/relatedcountsv1"
	"github.com/pingcap-inc/nexuscrm-contract/wire-go/envelope"
	commonv1 "github.com/pingcap-inc/nexuscrm-contract/wire-go/gen/common/v1"
	runtimev1 "github.com/pingcap-inc/nexuscrm-contract/wire-go/gen/runtime/v1"
	"google.golang.org/protobuf/proto"
)

type relatedCountsEnvelopeGolden struct {
	Schema            string `json:"schema"`
	EvidenceRevisions struct {
		NexusCRM string `json:"nexuscrm"`
		AppPacks string `json:"nexuscrm_app_packs"`
		Contract string `json:"contract"`
	} `json:"evidence_revisions"`
	CurrentRoute struct {
		MaxSubmittedCounts  int    `json:"max_submitted_counts"`
		GenericBodyCapBytes int    `json:"generic_body_cap_bytes"`
		BodyCapClass        string `json:"body_cap_class"`
	} `json:"current_route"`
	BusinessEnvelope struct {
		SemanticRequestCapBytes int `json:"semantic_request_cap_bytes"`
		MaxRequestWireBytes     int `json:"max_request_wire_bytes"`
		OneOverRequestWireBytes int `json:"one_over_request_wire_bytes"`
		MaxResponseWireBytes    int `json:"max_response_wire_bytes"`
	} `json:"business_envelope"`
	Transport struct {
		ReceiveCapBytes      int    `json:"receive_cap_bytes"`
		ReceiveHeadroomBytes int    `json:"receive_headroom_bytes"`
		SendCapBytes         int    `json:"send_cap_bytes"`
		SendHeadroomBytes    int    `json:"send_headroom_bytes"`
		Compression          string `json:"compression"`
		ResponseLimitClass   string `json:"response_limit_class"`
	} `json:"transport"`
}

func TestRelatedCountsBusinessAndTransportEnvelope(t *testing.T) {
	request, oneOver := boundaryRelatedCountRequests(t)
	wireRequest := wireRelatedCountRequest(request)
	wireOneOver := wireRelatedCountRequest(oneOver)
	wireResponse := maximumRelatedCountResponse()

	var got relatedCountsEnvelopeGolden
	got.Schema = "runtime-related-counts-wire-envelope.v1"
	got.EvidenceRevisions.NexusCRM = "b61372e33b32f35e378c23b3e60f12f31fecfcf2"
	got.EvidenceRevisions.AppPacks = "647d7161ee1c9b0b4cbf8035706998fd3ac809f2"
	got.EvidenceRevisions.Contract = "88784e6a0a27340c86755dbfc400bc0a1bde60c8"
	got.CurrentRoute.MaxSubmittedCounts = semantic.MaxCounts
	got.CurrentRoute.GenericBodyCapBytes = 25 << 20
	got.CurrentRoute.BodyCapClass = "existing generic HTTP buffering safety cap; not the related-count business contract"
	got.BusinessEnvelope.SemanticRequestCapBytes = semantic.MaxSemanticRequestBytes
	got.BusinessEnvelope.MaxRequestWireBytes = proto.Size(wireRequest)
	got.BusinessEnvelope.OneOverRequestWireBytes = proto.Size(wireOneOver)
	got.BusinessEnvelope.MaxResponseWireBytes = proto.Size(wireResponse)
	got.Transport.ReceiveCapBytes = envelope.MaxRequestWireBytes
	got.Transport.ReceiveHeadroomBytes = envelope.MaxRequestWireBytes - got.BusinessEnvelope.MaxRequestWireBytes
	got.Transport.SendCapBytes = envelope.MaxResponseWireBytes
	got.Transport.SendHeadroomBytes = envelope.MaxResponseWireBytes - got.BusinessEnvelope.MaxResponseWireBytes
	got.Transport.Compression = "disabled"
	got.Transport.ResponseLimitClass = "structurally bounded by 50 fixed-shape results and receipts; no separate semantic byte cap"
	if got.Transport.ReceiveHeadroomBytes <= 0 || got.Transport.SendHeadroomBytes <= 0 {
		t.Fatalf("related-count wire shape exceeds transport envelope: %#v", got.Transport)
	}

	data, err := os.ReadFile("../testdata/related_counts_envelope_golden.json")
	if err != nil {
		t.Fatal(err)
	}
	var want relatedCountsEnvelopeGolden
	if err := json.Unmarshal(data, &want); err != nil {
		t.Fatal(err)
	}
	if !reflect.DeepEqual(got, want) {
		encoded, _ := json.MarshalIndent(got, "", "  ")
		t.Fatalf("related-count envelope changed; review deliberately:\n%s", encoded)
	}
}

func boundaryRelatedCountRequests(t *testing.T) (semantic.Request, semantic.Request) {
	t.Helper()
	request := semantic.Request{Counts: []semantic.CountSpec{{
		ParentRecordID:    strings.Repeat("P", semantic.MaxRecordIdentifierBytes),
		ChildObjectID:     strings.Repeat("O", semantic.MaxObjectIdentifierBytes),
		RelationshipField: strings.Repeat("F", semantic.MaxFieldIdentifierBytes),
		MatchValue:        "x",
	}}}
	size, err := semantic.SemanticRequestSize(request)
	if err != nil {
		t.Fatal(err)
	}
	request.Counts[0].MatchValue += strings.Repeat("x", semantic.MaxSemanticRequestBytes-size)
	size, err = semantic.SemanticRequestSize(request)
	if err != nil || size != semantic.MaxSemanticRequestBytes {
		t.Fatalf("semantic boundary size = %d, %v", size, err)
	}
	oneOver := request
	oneOver.Counts = append([]semantic.CountSpec(nil), request.Counts...)
	oneOver.Counts[0].MatchValue += "x"
	return request, oneOver
}

func wireRelatedCountRequest(request semantic.Request) *runtimev1.CountRelatedRecordsRequest {
	result := &runtimev1.CountRelatedRecordsRequest{}
	for _, count := range request.Counts {
		result.Counts = append(result.Counts, &runtimev1.RelatedRecordCountSpec{
			ParentRecordId: count.ParentRecordID, ChildObjectId: count.ChildObjectID,
			RelationshipField: count.RelationshipField, MatchValue: count.MatchValue,
		})
	}
	return result
}

func maximumRelatedCountResponse() *runtimev1.CountRelatedRecordsResponse {
	response := &runtimev1.CountRelatedRecordsResponse{Receipts: &commonv1.ConsistencyReceipts{
		ProtocolVersion: &commonv1.ProtocolVersion{Major: semantic.ProtocolMajor, Minor: semantic.ProtocolMinor},
		CatalogRevision: math.MaxUint64, SchemaGeneration: math.MaxUint64,
		EngineGeneration: math.MaxUint64, SecurityEpoch: math.MaxUint64,
		CapabilityVersion: &commonv1.CapabilityVersion{Name: semantic.CapabilityName, Major: 1},
	}}
	for index := range semantic.MaxCounts {
		response.Results = append(response.Results, &runtimev1.RelatedRecordCountResult{
			Index: uint32(index), Outcome: runtimev1.RelatedRecordCountOutcome_RELATED_RECORD_COUNT_OUTCOME_COUNTED, Count: math.MaxUint64,
		})
	}
	return response
}
