package compatibility_test

import (
	"context"
	"sync/atomic"
	"testing"
	"time"

	approvalsemantic "github.com/pingcap-inc/nexuscrm-contract/pkg/runtime/approvalv1"
	commonsemantic "github.com/pingcap-inc/nexuscrm-contract/pkg/runtime/commonv1"
	runtimev1 "github.com/pingcap-inc/nexuscrm-contract/wire-go/gen/runtime/v1"
	"github.com/pingcap-inc/nexuscrm-contract/wire-go/mapping"
	"google.golang.org/grpc"
	"google.golang.org/grpc/codes"
	"google.golang.org/grpc/status"
)

func TestGeneratedApprovalClientUsesTwoCoarseRPCsOnOneEndpoint(t *testing.T) {
	client, service, cleanup := startApprovalTestServer(t)
	defer cleanup()

	executeContext, executeCancel := context.WithTimeout(
		withCapability(context.Background(), approvalsemantic.ExecuteCapabilityName),
		time.Second,
	)
	defer executeCancel()
	executed, err := client.ExecuteApproval(executeContext, &runtimev1.ExecuteApprovalRequest{
		Operation:      runtimev1.ApprovalOperation_APPROVAL_OPERATION_SUBMIT,
		IdempotencyKey: "request-1", ObjectId: "Account", TargetRecordId: "record-1",
	})
	if err != nil || executed.GetTargetRecordId() != "record-1" ||
		executed.GetActingPrincipalId() != "user-1" || service.executeCalls.Load() != 1 {
		t.Fatalf("ExecuteApproval() response=%#v error=%v calls=%d", executed, err, service.executeCalls.Load())
	}

	readContext, readCancel := context.WithTimeout(
		withCapability(context.Background(), approvalsemantic.ReadCapabilityName),
		time.Second,
	)
	defer readCancel()
	read, err := client.ReadApproval(readContext, &runtimev1.ReadApprovalRequest{
		Kind:           runtimev1.ApprovalReadKind_APPROVAL_READ_KIND_LATEST_FOR_RECORD,
		TargetRecordId: "record-1",
	})
	if err != nil || read.GetLatest() != nil || service.readCalls.Load() != 1 {
		t.Fatalf("ReadApproval() response=%#v error=%v calls=%d", read, err, service.readCalls.Load())
	}
}

func TestGeneratedApprovalClientFailsClosedAcrossCapabilityBoundary(t *testing.T) {
	client, service, cleanup := startApprovalTestServer(t)
	defer cleanup()
	ctx, cancel := context.WithTimeout(
		withCapability(context.Background(), approvalsemantic.ReadCapabilityName),
		time.Second,
	)
	defer cancel()

	response, err := client.ExecuteApproval(ctx, &runtimev1.ExecuteApprovalRequest{})
	if response != nil || status.Code(err) != codes.FailedPrecondition || service.executeCalls.Load() != 0 {
		t.Fatalf("cross-capability response=%#v error=%v calls=%d", response, err, service.executeCalls.Load())
	}
}

type testApprovalService struct {
	runtimev1.UnimplementedApprovalServiceServer
	executeCalls atomic.Int64
	readCalls    atomic.Int64
}

func (s *testApprovalService) ExecuteApproval(ctx context.Context, request *runtimev1.ExecuteApprovalRequest) (*runtimev1.ExecuteApprovalResponse, error) {
	s.executeCalls.Add(1)
	semanticRequest, err := mapping.FromWireExecuteApprovalRequest(ctx, request)
	if err != nil {
		return nil, mapping.ToPlatformStatusError(err, approvalsemantic.ExecuteCapabilityName)
	}
	response := approvalsemantic.ExecuteResponse{
		ExecutionID: "execution-1", InstanceID: "instance-1", TargetRecordID: semanticRequest.TargetRecordID,
		ActingPrincipalID: "user-1",
		State:             approvalsemantic.InstanceStatePending,
		PendingWorkItems:  []approvalsemantic.WorkItemAssignment{{WorkItemID: "work-1", ActorID: "user-2"}},
		ActionFailures:    []approvalsemantic.ActionFailure{},
		Receipts: approvalsemantic.Receipts{
			ProtocolVersion: "1.0",
			CatalogRevision: 1, SchemaGeneration: 1, EngineGeneration: 1, SecurityEpoch: 1,
			CapabilityVersion: approvalsemantic.ExecuteCapabilityName,
		},
	}
	wireResponse, err := mapping.ToWireExecuteApprovalResponse(semanticRequest, response)
	if err != nil {
		return nil, mapping.ToPlatformStatusError(err, approvalsemantic.ExecuteCapabilityName)
	}
	return wireResponse, nil
}

func (s *testApprovalService) ReadApproval(ctx context.Context, request *runtimev1.ReadApprovalRequest) (*runtimev1.ReadApprovalResponse, error) {
	s.readCalls.Add(1)
	semanticRequest, err := mapping.FromWireReadApprovalRequest(ctx, request)
	if err != nil {
		return nil, mapping.ToPlatformStatusError(err, approvalsemantic.ReadCapabilityName)
	}
	response := approvalsemantic.ReadResponse{
		Pending: []approvalsemantic.PendingItem{}, Steps: []approvalsemantic.StepDisplay{},
		Receipts: commonsemantic.Receipts{
			ProtocolVersion: "1.0",
			CatalogRevision: 1, SchemaGeneration: 1, EngineGeneration: 1, SecurityEpoch: 1,
			CapabilityVersion: approvalsemantic.ReadCapabilityName,
		},
	}
	wireResponse, err := mapping.ToWireReadApprovalResponse(semanticRequest, response)
	if err != nil {
		return nil, mapping.ToPlatformStatusError(err, approvalsemantic.ReadCapabilityName)
	}
	return wireResponse, nil
}

func startApprovalTestServer(t *testing.T) (runtimev1.ApprovalServiceClient, *testApprovalService, func()) {
	t.Helper()
	service := &testApprovalService{}
	connection, cleanup := startBufconnServer(t, func(server *grpc.Server) {
		runtimev1.RegisterApprovalServiceServer(server, service)
	})
	return runtimev1.NewApprovalServiceClient(connection), service, cleanup
}
