package runtimeapi

import (
	"context"
	"errors"
	"testing"

	"github.com/nexuscrm/shared/pkg/models"
	approvalnotificationsemantic "github.com/pingcap-inc/nexuscrm-contract/pkg/runtime/approvalnotificationflowv1"
	runtimev1 "github.com/pingcap-inc/nexuscrm-contract/wire-go/gen/runtime/v1"
	"github.com/pingcap-inc/nexuscrm-contract/wire-go/mapping"
)

type approvalNotificationFlowReadTestEngine struct {
	definitions []*models.FlowDefinition
	err         error
	calls       int
}

func (engine *approvalNotificationFlowReadTestEngine) ListApprovalNotificationFlows(
	context.Context,
) ([]*models.FlowDefinition, error) {
	engine.calls++
	return engine.definitions, engine.err
}

func TestApprovalNotificationFlowReadServerProjectsCanonicalDescriptors(t *testing.T) {
	engine := &approvalNotificationFlowReadTestEngine{definitions: []*models.FlowDefinition{
		{DeveloperName: "Z_Notify", MasterLabel: "Notify Z", Description: "Sends Z."},
		{DeveloperName: "A_Notify"},
	}}
	server, err := NewApprovalNotificationFlowReadServer(engine)
	if err != nil {
		t.Fatal(err)
	}
	ctx := approvalNotificationFlowReadTestContext(t)
	request := approvalnotificationsemantic.Request{}
	wireRequest, err := mapping.ToWireReadApprovalNotificationFlowsRequest(ctx, request)
	if err != nil {
		t.Fatal(err)
	}
	wireResponse, err := server.ReadApprovalNotificationFlows(ctx, wireRequest)
	if err != nil {
		t.Fatal(err)
	}
	response, err := mapping.FromWireReadApprovalNotificationFlowsResponse(request, wireResponse)
	if err != nil {
		t.Fatal(err)
	}
	if engine.calls != 1 || len(response.Flows) != 2 ||
		response.Flows[0] != (approvalnotificationsemantic.FlowDescriptor{Name: "A_Notify", Label: "A_Notify"}) ||
		response.Flows[1] != (approvalnotificationsemantic.FlowDescriptor{Name: "Z_Notify", Label: "Notify Z", Description: "Sends Z."}) ||
		response.Receipts.CapabilityVersion != approvalnotificationsemantic.CapabilityName {
		t.Fatalf("response = %#v, calls=%d", response, engine.calls)
	}
}

func TestApprovalNotificationFlowReadServerFailsBeforeRepositoryOnAuthorityMismatch(t *testing.T) {
	engine := &approvalNotificationFlowReadTestEngine{}
	server, err := NewApprovalNotificationFlowReadServer(engine)
	if err != nil {
		t.Fatal(err)
	}
	ctx := metadataAdminReadTestContext(t)
	wireRequest, err := mapping.ToWireReadApprovalNotificationFlowsRequest(
		ctx,
		approvalnotificationsemantic.Request{},
	)
	if err != nil {
		t.Fatal(err)
	}
	if _, err := server.ReadApprovalNotificationFlows(ctx, wireRequest); err == nil {
		t.Fatal("capability confusion was accepted")
	}
	if engine.calls != 0 {
		t.Fatalf("engine calls = %d, want 0", engine.calls)
	}
}

func TestApprovalNotificationFlowReadServerRejectsUnclaimedCatalogRevision(t *testing.T) {
	engine := &approvalNotificationFlowReadTestEngine{}
	server, err := NewApprovalNotificationFlowReadServer(engine)
	if err != nil {
		t.Fatal(err)
	}
	ctx := approvalNotificationFlowReadTestContext(t)
	minimum := uint64(7)
	request := approvalnotificationsemantic.Request{MinCatalogRevision: &minimum}
	wireRequest, err := mapping.ToWireReadApprovalNotificationFlowsRequest(ctx, request)
	if err != nil {
		t.Fatal(err)
	}
	_, err = server.ReadApprovalNotificationFlows(ctx, wireRequest)
	var failure *approvalnotificationsemantic.Failure
	if !errors.As(mapping.FromPlatformStatusError(err, approvalnotificationsemantic.CapabilityName), &failure) ||
		failure.Category != approvalnotificationsemantic.FailureStaleState || failure.RequiredRevision != minimum ||
		failure.ServedRevision != 0 || engine.calls != 0 {
		t.Fatalf("failure = %#v, raw=%v, calls=%d", failure, err, engine.calls)
	}
}

func TestApprovalNotificationFlowReadServerRequiresEngine(t *testing.T) {
	if server, err := NewApprovalNotificationFlowReadServer(nil); err == nil || server != nil {
		t.Fatalf("nil engine = %#v, %v", server, err)
	}
}

func approvalNotificationFlowReadTestContext(t *testing.T) context.Context {
	t.Helper()
	parent := auditLogTestContext(t)
	trusted, ok := trustedCallFromContext(parent)
	if !ok {
		t.Fatal("missing trusted delegated call")
	}
	trusted.capabilities = []string{approvalnotificationsemantic.CapabilityName}
	parent = context.WithValue(parent, trustedCallContextKey{}, trusted)
	return metadataAdminReadHostContext(
		t,
		parent,
		runtimev1.ApprovalNotificationFlowReadService_ReadApprovalNotificationFlows_FullMethodName,
		approvalnotificationsemantic.CapabilityName,
	)
}
