package compatibility_test

import (
	"context"
	"testing"
	"time"

	analyticssemantic "github.com/pingcap-inc/nexuscrm-contract/pkg/runtime/analyticsauthoringv1"
	accesssemantic "github.com/pingcap-inc/nexuscrm-contract/pkg/runtime/resourceaccessv1"
	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 TestGeneratedAnalyticsAuthoringClientUsesOneCoarseRPC(t *testing.T) {
	server := &analyticsAuthoringCompatibilityServer{}
	connection, cleanup := startBufconnServer(t, func(grpcServer *grpc.Server) {
		runtimev1.RegisterAnalyticsAuthoringServiceServer(grpcServer, server)
	})
	defer cleanup()
	ctx, cancel := context.WithTimeout(withCapability(context.Background(), analyticssemantic.CapabilityName), time.Second)
	defer cancel()
	response, err := runtimev1.NewAnalyticsAuthoringServiceClient(connection).MutateAnalyticsDefinition(ctx, &runtimev1.MutateAnalyticsDefinitionRequest{
		CommandId: "dashboard-create-1",
		Operation: runtimev1.AnalyticsAuthoringOperation_ANALYTICS_AUTHORING_OPERATION_CREATE_DASHBOARD,
		Dashboard: &runtimev1.AnalyticsDashboardDefinition{
			Name: stringPointer("Pipeline"), StableName: stringPointer("Pipeline"),
			FolderId: stringPointer("folder-1"), LayoutKind: stringPointer("GRID"),
		},
	})
	if err != nil || response.GetCommandId() != "dashboard-create-1" || response.GetResourceId() != "dashboard-1" || server.calls != 1 {
		t.Fatalf("response=%#v error=%v calls=%d", response, err, server.calls)
	}
}

func TestGeneratedResourceAccessClientPreservesMutationIdentity(t *testing.T) {
	server := &resourceAccessCompatibilityServer{}
	connection, cleanup := startBufconnServer(t, func(grpcServer *grpc.Server) {
		runtimev1.RegisterResourceAccessServiceServer(grpcServer, server)
	})
	defer cleanup()
	ctx, cancel := context.WithTimeout(withCapability(context.Background(), accesssemantic.CapabilityName), time.Second)
	defer cancel()
	response, err := runtimev1.NewResourceAccessServiceClient(connection).AccessResource(ctx, &runtimev1.AccessResourceRequest{
		CommandId:    "replace-1",
		Operation:    runtimev1.ResourceAccessOperation_RESOURCE_ACCESS_OPERATION_REPLACE_GRANTS,
		ResourceType: "folder",
		ResourceId:   "folder-1",
	})
	if err != nil || response.GetCommandId() != "replace-1" || server.calls != 1 {
		t.Fatalf("response=%#v error=%v calls=%d", response, err, server.calls)
	}
}

func TestNewAuthoringClientsFailClosedAgainstPreviousEndpoint(t *testing.T) {
	connection, cleanup := startBufconnServer(t, func(grpcServer *grpc.Server) {
		runtimev1.RegisterCommandAcceptanceServiceServer(grpcServer, &commandOnlyServer{})
	})
	defer cleanup()
	ctx, cancel := context.WithTimeout(context.Background(), time.Second)
	defer cancel()
	if response, err := runtimev1.NewAnalyticsAuthoringServiceClient(connection).MutateAnalyticsDefinition(ctx, &runtimev1.MutateAnalyticsDefinitionRequest{}); response != nil || status.Code(err) != codes.Unimplemented {
		t.Fatalf("analytics N/N-1 response=%#v error=%v", response, err)
	}
	if response, err := runtimev1.NewResourceAccessServiceClient(connection).AccessResource(ctx, &runtimev1.AccessResourceRequest{}); response != nil || status.Code(err) != codes.Unimplemented {
		t.Fatalf("resource access N/N-1 response=%#v error=%v", response, err)
	}
}

type analyticsAuthoringCompatibilityServer struct {
	runtimev1.UnimplementedAnalyticsAuthoringServiceServer
	calls int
}

func (server *analyticsAuthoringCompatibilityServer) MutateAnalyticsDefinition(ctx context.Context, wireRequest *runtimev1.MutateAnalyticsDefinitionRequest) (*runtimev1.MutateAnalyticsDefinitionResponse, error) {
	server.calls++
	request, err := mapping.FromWireMutateAnalyticsDefinitionRequest(ctx, wireRequest)
	if err != nil {
		return nil, mapping.ToPlatformStatusError(err, analyticssemantic.CapabilityName)
	}
	return mapping.ToWireMutateAnalyticsDefinitionResponse(request, analyticssemantic.Response{CommandID: request.CommandID, ResourceID: "dashboard-1", Receipts: analyticsAuthoringCompatibilityReceipts()})
}

type resourceAccessCompatibilityServer struct {
	runtimev1.UnimplementedResourceAccessServiceServer
	calls int
}

func (server *resourceAccessCompatibilityServer) AccessResource(ctx context.Context, wireRequest *runtimev1.AccessResourceRequest) (*runtimev1.AccessResourceResponse, error) {
	server.calls++
	request, err := mapping.FromWireResourceAccessRequest(ctx, wireRequest)
	if err != nil {
		return nil, mapping.ToPlatformStatusError(err, accesssemantic.CapabilityName)
	}
	return mapping.ToWireResourceAccessResponse(request, accesssemantic.Response{CommandID: request.CommandID, Receipts: resourceAccessCompatibilityReceipts()})
}

func analyticsAuthoringCompatibilityReceipts() analyticssemantic.Receipts {
	return analyticssemantic.Receipts{ProtocolVersion: "1.0", CapabilityVersion: analyticssemantic.CapabilityName, CatalogRevision: 1, SchemaGeneration: 2, EngineGeneration: 3, SecurityEpoch: 4}
}

func resourceAccessCompatibilityReceipts() accesssemantic.Receipts {
	return accesssemantic.Receipts{ProtocolVersion: "1.0", CapabilityVersion: accesssemantic.CapabilityName, CatalogRevision: 1, SchemaGeneration: 2, EngineGeneration: 3, SecurityEpoch: 4}
}
