package mapping

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

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

func TestApprovalNotificationFlowWireRoundTripPreservesCatalog(t *testing.T) {
	ctx, cancel := context.WithTimeout(context.Background(), time.Second)
	defer cancel()
	minimum := uint64(7)
	request := semantic.Request{MinCatalogRevision: &minimum}
	wireRequest, err := ToWireReadApprovalNotificationFlowsRequest(ctx, request)
	if err != nil {
		t.Fatalf("ToWireReadApprovalNotificationFlowsRequest() = %v", err)
	}
	roundRequest, err := FromWireReadApprovalNotificationFlowsRequest(ctx, wireRequest)
	if err != nil || !reflect.DeepEqual(roundRequest, request) {
		t.Fatalf("request round trip = %#v, %v", roundRequest, err)
	}

	response := semantic.Response{
		Flows: []semantic.FlowDescriptor{
			{Name: "Account_Notify_Approver", Label: "Notify Account Approver", Description: "Sends the notification."},
			{Name: "Credits_Notify_Approver", Label: "Notify Credits Approver"},
		},
		Receipts: approvalNotificationFlowReceipts(minimum),
	}
	wireResponse, err := ToWireReadApprovalNotificationFlowsResponse(request, response)
	if err != nil {
		t.Fatalf("ToWireReadApprovalNotificationFlowsResponse() = %v", err)
	}
	roundResponse, err := FromWireReadApprovalNotificationFlowsResponse(request, wireResponse)
	if err != nil || !reflect.DeepEqual(roundResponse, response) {
		t.Fatalf("response round trip = %#v, %v", roundResponse, err)
	}
}

func TestApprovalNotificationFlowWireFailsClosedBeforeAllocation(t *testing.T) {
	request := semantic.Request{}
	if _, err := FromWireReadApprovalNotificationFlowsResponse(request, nil); err == nil {
		t.Fatal("nil response was accepted")
	}
	if _, err := FromWireReadApprovalNotificationFlowsResponse(request, &runtimev1.ReadApprovalNotificationFlowsResponse{
		Flows: []*runtimev1.ApprovalNotificationFlowDescriptor{nil},
		Receipts: toWireReceipts(
			approvalNotificationFlowReceipts(1),
			platformCapability(semantic.CapabilityName),
		),
	}); err == nil {
		t.Fatal("nil descriptor was accepted")
	}

	oversized := &runtimev1.ReadApprovalNotificationFlowsResponse{
		Flows: make([]*runtimev1.ApprovalNotificationFlowDescriptor, semantic.MaxFlows+1),
		Receipts: toWireReceipts(
			approvalNotificationFlowReceipts(1),
			platformCapability(semantic.CapabilityName),
		),
	}
	var failure *semantic.Failure
	if _, err := FromWireReadApprovalNotificationFlowsResponse(request, oversized); !errors.As(err, &failure) ||
		failure.Category != semantic.FailureResourceExhausted ||
		failure.LimitKind != semantic.LimitRecordCount || failure.Maximum != semantic.MaxFlows {
		t.Fatalf("oversized response = %#v", err)
	}
}

func TestApprovalNotificationFlowStatusMappingAcceptsOnlyOwnedLimits(t *testing.T) {
	valid := &semantic.Failure{
		Category: semantic.FailureResourceExhausted, LimitKind: semantic.LimitRecordCount,
		Maximum: semantic.MaxFlows,
	}
	var failure *semantic.Failure
	if decoded := FromPlatformStatusError(
		ToPlatformStatusError(valid, semantic.CapabilityName), semantic.CapabilityName,
	); !errors.As(decoded, &failure) || failure.Category != valid.Category ||
		failure.LimitKind != valid.LimitKind || failure.Maximum != valid.Maximum {
		t.Fatalf("owned limit round trip = %#v", decoded)
	}

	foreign := *valid
	foreign.Maximum--
	if decoded := FromPlatformStatusError(
		ToPlatformStatusError(&foreign, semantic.CapabilityName), semantic.CapabilityName,
	); !errors.As(decoded, &failure) || failure.Category != semantic.FailureInternal {
		t.Fatalf("foreign limit = %#v", decoded)
	}
}

func approvalNotificationFlowReceipts(revision uint64) semantic.Receipts {
	return semantic.Receipts{
		ProtocolVersion: "1.0", CapabilityVersion: semantic.CapabilityName,
		CatalogRevision: revision, SchemaGeneration: 2, EngineGeneration: 3, SecurityEpoch: 4,
	}
}
