package runtimegrpc

import (
	"context"
	semantic "github.com/pingcap-inc/nexuscrm-contract/pkg/runtime/applicationnavigationv1"
	common "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"
	"reflect"
	"sync"
	"testing"
	"time"
)

type navigationTestServer struct {
	runtimev1.UnimplementedApplicationNavigationServiceServer
	parent   *platformRuntimeTestServer
	mu       sync.Mutex
	document semantic.Document
}

func (s *navigationTestServer) Read(ctx context.Context, r *runtimev1.ApplicationNavigationServiceReadRequest) (*runtimev1.ApplicationNavigationServiceReadResponse, error) {
	s.parent.requireDelegation(ctx)
	request, err := mapping.FromWireNavigationReadRequest(ctx, r)
	if err != nil {
		return nil, mapping.ToPlatformStatusError(err, semantic.CapabilityName)
	}
	s.mu.Lock()
	defer s.mu.Unlock()
	if s.document.Revision == 0 {
		s.document = semantic.Document{ApplicationID: request.ApplicationID, Revision: 1, Items: []semantic.Item{}}
	}
	return mapping.ToWireNavigationReadResponse(request, semantic.ReadResponse{Document: s.document, Receipts: navigationTestReceipts()})
}
func (s *navigationTestServer) Resolve(ctx context.Context, r *runtimev1.ApplicationNavigationServiceResolveRequest) (*runtimev1.ApplicationNavigationServiceResolveResponse, error) {
	s.parent.requireDelegation(ctx)
	request, err := mapping.FromWireNavigationResolveRequest(ctx, r)
	if err != nil {
		return nil, mapping.ToPlatformStatusError(err, semantic.CapabilityName)
	}
	s.mu.Lock()
	defer s.mu.Unlock()
	return mapping.ToWireNavigationResolveResponse(request, semantic.ResolveResponse{Documents: []semantic.Document{s.document}, Receipts: navigationTestReceipts()})
}
func (s *navigationTestServer) Save(ctx context.Context, r *runtimev1.ApplicationNavigationServiceSaveRequest) (*runtimev1.ApplicationNavigationServiceSaveResponse, error) {
	s.parent.requireDelegation(ctx)
	request, err := mapping.FromWireNavigationSaveRequest(ctx, r)
	if err != nil {
		return nil, mapping.ToPlatformStatusError(err, semantic.CapabilityName)
	}
	s.mu.Lock()
	defer s.mu.Unlock()
	s.document = semantic.Document{ApplicationID: request.ApplicationID, Revision: request.ExpectedRevision + 1, Items: request.Items}
	return mapping.ToWireNavigationSaveResponse(request, semantic.SaveResponse{Document: s.document, Changed: true, Receipts: navigationTestReceipts()})
}
func TestNavigationClientDelegatesAndReadsCommittedDocument(t *testing.T) {
	target, server, transport, cleanup := startPlatformRuntimeTestServer(t)
	defer cleanup()
	ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second)
	defer cancel()
	client, err := dialPlatformRuntimeWithCredentials(ctx, RuntimeClientConfig{Target: target, AppInstallationID: "installation-1", WorkloadToken: "workload-secret", AttemptTimeout: 500 * time.Millisecond}, transport)
	if err != nil {
		t.Fatal(err)
	}
	defer client.Close()
	before, err := client.ReadApplicationNavigation(ctx, "user-secret", semantic.ReadRequest{ApplicationID: "app"})
	if err != nil {
		t.Fatal(err)
	}
	url := "https://example.org/help"
	items := []semantic.Item{{ID: "help", Label: "Help", Icon: "Link", Target: semantic.Target{URL: &url}}, {ID: "start", Label: "Start", Target: semantic.Target{Page: &semantic.PageTarget{ID: "start"}}}, {ID: "report", Label: "Overview", Target: semantic.Target{Resource: &semantic.ResourceTarget{ResourceType: "Dashboard", ResourceID: "dashboard-1"}}}}
	saved, err := client.SaveApplicationNavigation(ctx, "user-secret", semantic.SaveRequest{ApplicationID: "app", ExpectedRevision: before.Document.Revision, IdempotencyKey: "save-1", Items: items})
	if err != nil {
		t.Fatal(err)
	}
	resolved, err := client.ResolveApplicationNavigation(ctx, "user-secret", semantic.ResolveRequest{ApplicationIDs: []string{"app"}})
	if err != nil || len(resolved.Documents) != 1 || !reflect.DeepEqual(resolved.Documents[0], saved.Document) {
		t.Fatalf("saved=%+v resolved=%+v err=%v", saved, resolved, err)
	}
	if server.catalogCalls.Load() != 0 || server.commandCalls.Load() != 0 {
		t.Fatal("navigation consulted catalog or asynchronous commands")
	}
	if server.identityCalls.Load() != 1 {
		t.Fatalf("identity exchanges=%d", server.identityCalls.Load())
	}
}

func navigationTestReceipts() common.Receipts {
	return common.Receipts{ProtocolVersion: "1.0", SecurityEpoch: 1, CapabilityVersion: semantic.CapabilityName}
}
