package mapping

import (
	"context"
	"errors"
	"testing"
	"time"

	semantic "github.com/pingcap-inc/nexuscrm-contract/pkg/runtime/recorddefaultsv1"
	runtimev1 "github.com/pingcap-inc/nexuscrm-contract/wire-go/gen/runtime/v1"
)

func TestCreateDefaultPreviewWireRoundTripPreservesTypedDefaultsAndProvenance(t *testing.T) {
	ctx, cancel := context.WithTimeout(context.Background(), time.Second)
	defer cancel()
	parentField := "ParentId"
	parentRecord := "parent-1"
	revision := uint64(41)
	request := semantic.Request{
		ObjectID: "RankedChild", ParentFieldID: &parentField,
		ParentRecordID: &parentRecord, MinCatalogRevision: &revision,
	}
	wireRequest, err := ToWirePreviewCreateDefaultsRequest(ctx, request)
	if err != nil {
		t.Fatal(err)
	}
	roundTripRequest, err := FromWirePreviewCreateDefaultsRequest(ctx, wireRequest)
	if err != nil || roundTripRequest.ParentFieldID == nil ||
		*roundTripRequest.ParentFieldID != parentField ||
		roundTripRequest.ParentRecordID == nil ||
		*roundTripRequest.ParentRecordID != parentRecord {
		t.Fatalf("request round trip=%#v error=%v", roundTripRequest, err)
	}
	next := int64(901)
	ownerID := "005-test"
	ownerName := "Example Principal"
	response := semantic.Response{
		Fields: []semantic.Field{
			{Name: "AssignedPrincipalId", Kind: semantic.ValueReference, Reference: &ownerID, DisplayValue: &ownerName, Provenance: semantic.ProvenanceCurrentPrincipal},
			{Name: "Sequence", Kind: semantic.ValueInteger, Integer: &next, Provenance: semantic.ProvenanceSiblingMaxPlusOne},
		},
		Receipts: semantic.Receipts{ProtocolVersion: "1.0", CapabilityVersion: semantic.CapabilityName, CatalogRevision: revision},
	}
	wireResponse, err := ToWirePreviewCreateDefaultsResponse(request, response)
	if err != nil {
		t.Fatal(err)
	}
	roundTripResponse, err := FromWirePreviewCreateDefaultsResponse(request, wireResponse)
	if err != nil || len(roundTripResponse.Fields) != 2 ||
		roundTripResponse.Fields[1].Integer == nil ||
		*roundTripResponse.Fields[1].Integer != 901 ||
		roundTripResponse.Fields[1].Provenance != semantic.ProvenanceSiblingMaxPlusOne {
		t.Fatalf("response round trip=%#v error=%v", roundTripResponse, err)
	}
}

func TestCreateDefaultPreviewFromWireRejectsFieldCountBeforeMaterialization(t *testing.T) {
	request := semantic.Request{ObjectID: "RankedChild"}
	wireResponse := &runtimev1.PreviewCreateDefaultsResponse{
		Fields: make([]*runtimev1.CreateDefaultField, semantic.MaxDefaultFields+1),
	}
	var failure *semantic.Failure
	if _, err := FromWirePreviewCreateDefaultsResponse(request, wireResponse); !errors.As(err, &failure) || failure.Category != semantic.FailureResourceExhausted ||
		failure.LimitKind != semantic.LimitFieldCount {
		t.Fatalf("FromWirePreviewCreateDefaultsResponse(too many fields) = %#v", err)
	}
}

func TestCreateDefaultPreviewStatusUsesItsOwnCapability(t *testing.T) {
	err := ToCreateDefaultPreviewStatusError(&semantic.Failure{Category: semantic.FailureForbidden})
	var failure *semantic.Failure
	if !errors.As(FromCreateDefaultPreviewStatusError(err), &failure) ||
		failure.Category != semantic.FailureForbidden {
		t.Fatalf("status round trip=%#v", failure)
	}
}

func TestCreateDefaultPreviewStatusPreservesInvalidOperation(t *testing.T) {
	err := ToCreateDefaultPreviewStatusError(&semantic.Failure{
		Category:         semantic.FailureInvalidRequest,
		InvalidComponent: semantic.InvalidComponentOperation,
	})
	var failure *semantic.Failure
	if !errors.As(FromCreateDefaultPreviewStatusError(err), &failure) ||
		failure.Category != semantic.FailureInvalidRequest ||
		failure.InvalidComponent != semantic.InvalidComponentOperation {
		t.Fatalf("status round trip=%#v", failure)
	}
}
