package compatibility_test

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

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

type recycleBinEnvelopeGolden struct {
	Schema            string `json:"schema"`
	EvidenceRevisions struct {
		NexusCRM string `json:"nexuscrm"`
		AppPacks string `json:"nexuscrm_app_packs"`
		Contract string `json:"contract"`
	} `json:"evidence_revisions"`
	CurrentEngine struct {
		VisibleItemCap       int    `json:"visible_item_cap"`
		CandidateScanCap     int    `json:"candidate_scan_cap"`
		FailedObjectUpperCap int    `json:"failed_object_upper_cap"`
		LimitClass           string `json:"limit_class"`
	} `json:"current_engine"`
	BusinessEnvelope struct {
		SemanticResponseCapBytes int `json:"semantic_response_cap_bytes"`
		RequestWireBytes         int `json:"request_wire_bytes"`
		MaxResponseWireBytes     int `json:"max_response_wire_bytes"`
		OneOverSemanticBytes     int `json:"one_over_semantic_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"`
	} `json:"transport"`
}

func TestRecycleBinBusinessAndTransportEnvelope(t *testing.T) {
	response, oneOver := boundaryRecycleBinResponses(t)
	wireResponse, err := mapping.ToWireListRecycleBinResponse(response)
	if err != nil {
		t.Fatal(err)
	}
	request := &runtimev1.ListRecycleBinRequest{Scope: runtimev1.RecycleBinScope_RECYCLE_BIN_SCOPE_ALL}

	var got recycleBinEnvelopeGolden
	got.Schema = "runtime-recycle-bin-wire-envelope.v1"
	got.EvidenceRevisions.NexusCRM = "f11d63e55f37936f358ec0166fb9b92c20cd9b68"
	got.EvidenceRevisions.AppPacks = "a3e3ad964bfd38622c8aca240e65c5aadbfcd242"
	got.EvidenceRevisions.Contract = "b745131a565388021d59f3df8da02a772c1d8b55"
	got.CurrentEngine.VisibleItemCap = semantic.MaxItems
	got.CurrentEngine.CandidateScanCap = 2000
	got.CurrentEngine.FailedObjectUpperCap = semantic.MaxFailedObjects
	got.CurrentEngine.LimitClass = "existing records-engine post-visibility and candidate-scan bounds"
	got.BusinessEnvelope.SemanticResponseCapBytes = semantic.MaxSemanticResponseBytes
	got.BusinessEnvelope.RequestWireBytes = proto.Size(request)
	got.BusinessEnvelope.MaxResponseWireBytes = proto.Size(wireResponse)
	oneOverSize, err := semantic.SemanticResponseSize(oneOver)
	if err != nil {
		t.Fatal(err)
	}
	got.BusinessEnvelope.OneOverSemanticBytes = oneOverSize
	got.Transport.ReceiveCapBytes = envelope.MaxRequestWireBytes
	got.Transport.ReceiveHeadroomBytes = envelope.MaxRequestWireBytes - got.BusinessEnvelope.RequestWireBytes
	got.Transport.SendCapBytes = envelope.MaxResponseWireBytes
	got.Transport.SendHeadroomBytes = envelope.MaxResponseWireBytes - got.BusinessEnvelope.MaxResponseWireBytes
	got.Transport.Compression = "disabled"
	if got.Transport.ReceiveHeadroomBytes <= 0 || got.Transport.SendHeadroomBytes <= 0 {
		t.Fatalf("recycle-bin wire shape exceeds transport envelope: %#v", got.Transport)
	}
	if err := semantic.ValidateResponse(oneOver); err == nil {
		t.Fatal("one-over semantic response was accepted")
	}

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

func boundaryRecycleBinResponses(t *testing.T) (semantic.Response, semantic.Response) {
	t.Helper()
	response := semantic.Response{
		Items:    []semantic.Item{{RecordID: "row-1", ObjectID: "WorkItem", DeletedDate: "2026-07-22T04:00:00Z"}},
		Receipts: semantic.Receipts{ProtocolVersion: "1.0", CapabilityVersion: semantic.CapabilityName},
	}
	size, err := semantic.SemanticResponseSize(response)
	if err != nil {
		t.Fatal(err)
	}
	response.Items[0].RecordName = strings.Repeat("x", semantic.MaxSemanticResponseBytes-size)
	size, err = semantic.SemanticResponseSize(response)
	if err != nil || size != semantic.MaxSemanticResponseBytes {
		t.Fatalf("semantic boundary size = %d, %v", size, err)
	}
	oneOver := response
	oneOver.Items = append([]semantic.Item(nil), response.Items...)
	oneOver.Items[0].RecordName += "x"
	return response, oneOver
}
