package runtimegrpc

import (
	"context"
	"errors"
	"strconv"
	"strings"

	identitysemantic "github.com/pingcap-inc/nexuscrm-contract/pkg/runtime/identityv1"
	querysemantic "github.com/pingcap-inc/nexuscrm-contract/pkg/runtime/queryv1"
	accesssemantic "github.com/pingcap-inc/nexuscrm-contract/pkg/runtime/recordaccessv1"
	attachmentsemantic "github.com/pingcap-inc/nexuscrm-contract/pkg/runtime/recordattachmentsv1"
	defaultsemantic "github.com/pingcap-inc/nexuscrm-contract/pkg/runtime/recorddefaultsv1"
	semantic "github.com/pingcap-inc/nexuscrm-contract/pkg/runtime/recordsv1"
	recordviewsemantic "github.com/pingcap-inc/nexuscrm-contract/pkg/runtime/recordviewv1"
	recyclemutationsemantic "github.com/pingcap-inc/nexuscrm-contract/pkg/runtime/recyclebinmutationv1"
	recyclesemantic "github.com/pingcap-inc/nexuscrm-contract/pkg/runtime/recyclebinv1"
	relatedsemantic "github.com/pingcap-inc/nexuscrm-contract/pkg/runtime/relatedcountsv1"
	"github.com/pingcap-inc/nexuscrm-contract/wire-go/envelope"
	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/metadata"
)

// SelectedRootInvoker is the generated RecordsService method shape. A function
// value keeps construction directly nil-checkable while allowing the caller to
// own connection, credentials, resolver, and lifecycle policy.
type SelectedRootInvoker func(context.Context, *runtimev1.ReadSelectedRootRequest, ...grpc.CallOption) (*runtimev1.ReadSelectedRootResponse, error)

type RecordAccessInvoker func(context.Context, *runtimev1.EvaluateRecordAccessRequest, ...grpc.CallOption) (*runtimev1.EvaluateRecordAccessResponse, error)

type CreateDefaultPreviewInvoker func(context.Context, *runtimev1.PreviewCreateDefaultsRequest, ...grpc.CallOption) (*runtimev1.PreviewCreateDefaultsResponse, error)

type QueryInvoker func(context.Context, *runtimev1.QueryRecordsRequest, ...grpc.CallOption) (*runtimev1.QueryRecordsResponse, error)
type QueryHistoryInvoker func(context.Context, *runtimev1.QueryHistoryRequest, ...grpc.CallOption) (*runtimev1.QueryHistoryResponse, error)

type RelatedCountInvoker func(context.Context, *runtimev1.CountRelatedRecordsRequest, ...grpc.CallOption) (*runtimev1.CountRelatedRecordsResponse, error)

type RecycleBinInvoker func(context.Context, *runtimev1.ListRecycleBinRequest, ...grpc.CallOption) (*runtimev1.ListRecycleBinResponse, error)
type RecycleBinRestoreInvoker func(context.Context, *runtimev1.RestoreRecycleBinRequest, ...grpc.CallOption) (*runtimev1.RestoreRecycleBinResponse, error)
type RecycleBinPurgeInvoker func(context.Context, *runtimev1.PurgeRecycleBinRequest, ...grpc.CallOption) (*runtimev1.PurgeRecycleBinResponse, error)

type RecordViewReadInvoker func(context.Context, *runtimev1.ReadRecordViewRequest, ...grpc.CallOption) (*runtimev1.ReadRecordViewResponse, error)

// RecordViewResolveInvoker is retained only for clients interoperating with
// the historic RecordViewReadService attachment method. New callers must use
// AttachmentSourceResolveInvoker and RecordAttachmentSourceResolutionService.
type RecordViewResolveInvoker func(context.Context, *runtimev1.ResolveRecordAttachmentSourcesRequest, ...grpc.CallOption) (*runtimev1.ResolveRecordAttachmentSourcesResponse, error)
type AttachmentSourceResolveInvoker func(context.Context, *runtimev1.ResolveAttachmentSourcesRequest, ...grpc.CallOption) (*runtimev1.ResolveAttachmentSourcesResponse, error)

type IdentityExchangeInvoker func(context.Context, *runtimev1.ExchangeRequest, ...grpc.CallOption) (*runtimev1.ExchangeResponse, error)

// SelectedRootClient owns the app-blind per-call Runtime transport policy. L1
// clients own product projection; callers own connection security and lifetime.
type SelectedRootClient struct {
	invoke SelectedRootInvoker
}

// RecordAccessClient owns the app-blind per-call Runtime transport policy for
// the coarse access-evaluation capability. Product clients own their public
// request/response grammar and projection.
type RecordAccessClient struct {
	invoke RecordAccessInvoker
}

type CreateDefaultPreviewClient struct {
	invoke CreateDefaultPreviewInvoker
}

type QueryClient struct {
	invokeRecords QueryInvoker
	invokeHistory QueryHistoryInvoker
}

type RelatedCountClient struct {
	invoke RelatedCountInvoker
}

type RecycleBinClient struct {
	invoke RecycleBinInvoker
}

type RecycleBinMutationClient struct {
	restore RecycleBinRestoreInvoker
	purge   RecycleBinPurgeInvoker
}

// RecordViewReadClient owns only the bounded record-view read RPC. It is
// separate from the historic RecordViewClient so new clients cannot
// accidentally retain the legacy, unproven attachment-source RPC.
type RecordViewReadClient struct {
	read RecordViewReadInvoker
}

// RecordViewClient is retained for N compatibility with the historic
// RecordViewReadService attachment method. It deliberately retains its
// original concrete methods and request types: callers already built against
// the released API must continue to compile and get the same fail-closed
// unavailable result from a nil client. New callers that only need the
// record-view read construct RecordViewReadClient; new attachment-source
// callers construct AttachmentSourceClient.
type RecordViewClient struct {
	read    RecordViewReadInvoker
	resolve RecordViewResolveInvoker
}

// AttachmentSourceClient owns the public, proof-bearing attachment-source
// transport. The caller supplies only product-parsed values and byte proofs;
// the Runtime owns validation, metadata authority, record/FLS checks, and
// file authorization.
type AttachmentSourceClient struct {
	resolve AttachmentSourceResolveInvoker
}

type IdentityExchangeClient struct {
	invoke IdentityExchangeInvoker
}

func NewSelectedRootClient(invoke SelectedRootInvoker) (*SelectedRootClient, error) {
	if invoke == nil {
		return nil, errors.New("runtimegrpc: selected-root invoker is required")
	}
	return &SelectedRootClient{invoke: invoke}, nil
}

func NewRecordAccessClient(invoke RecordAccessInvoker) (*RecordAccessClient, error) {
	if invoke == nil {
		return nil, errors.New("runtimegrpc: record-access invoker is required")
	}
	return &RecordAccessClient{invoke: invoke}, nil
}

func NewCreateDefaultPreviewClient(invoke CreateDefaultPreviewInvoker) (*CreateDefaultPreviewClient, error) {
	if invoke == nil {
		return nil, errors.New("runtimegrpc: create-default preview invoker is required")
	}
	return &CreateDefaultPreviewClient{invoke: invoke}, nil
}

func NewQueryClient(records QueryInvoker, history QueryHistoryInvoker) (*QueryClient, error) {
	if records == nil || history == nil {
		return nil, errors.New("runtimegrpc: query record and history invokers are required")
	}
	return &QueryClient{invokeRecords: records, invokeHistory: history}, nil
}

func NewRelatedCountClient(invoke RelatedCountInvoker) (*RelatedCountClient, error) {
	if invoke == nil {
		return nil, errors.New("runtimegrpc: related-count invoker is required")
	}
	return &RelatedCountClient{invoke: invoke}, nil
}

func NewRecycleBinClient(invoke RecycleBinInvoker) (*RecycleBinClient, error) {
	if invoke == nil {
		return nil, errors.New("runtimegrpc: recycle-bin invoker is required")
	}
	return &RecycleBinClient{invoke: invoke}, nil
}

func NewRecycleBinMutationClient(restore RecycleBinRestoreInvoker, purge RecycleBinPurgeInvoker) (*RecycleBinMutationClient, error) {
	if restore == nil || purge == nil {
		return nil, errors.New("runtimegrpc: recycle-bin restore and purge invokers are required")
	}
	return &RecycleBinMutationClient{restore: restore, purge: purge}, nil
}

func NewRecordViewReadClient(read RecordViewReadInvoker) (*RecordViewReadClient, error) {
	if read == nil {
		return nil, errors.New("runtimegrpc: record-view read invoker is required")
	}
	return &RecordViewReadClient{read: read}, nil
}

func NewRecordViewClient(read RecordViewReadInvoker, resolve RecordViewResolveInvoker) (*RecordViewClient, error) {
	if read == nil || resolve == nil {
		return nil, errors.New("runtimegrpc: record-view read and attachment resolver invokers are required")
	}
	return &RecordViewClient{read: read, resolve: resolve}, nil
}

func NewAttachmentSourceClient(resolve AttachmentSourceResolveInvoker) (*AttachmentSourceClient, error) {
	if resolve == nil {
		return nil, errors.New("runtimegrpc: attachment-source resolver invoker is required")
	}
	return &AttachmentSourceClient{resolve: resolve}, nil
}

func NewIdentityExchangeClient(invoke IdentityExchangeInvoker) (*IdentityExchangeClient, error) {
	if invoke == nil {
		return nil, errors.New("runtimegrpc: identity exchange invoker is required")
	}
	return &IdentityExchangeClient{invoke: invoke}, nil
}

// ReadSelectedRoot validates the semantic request before transport work,
// installs the exact protocol/capability declaration, enforces the approved
// client message envelope, and returns only validated semantic results.
func (c *SelectedRootClient) ReadSelectedRoot(ctx context.Context, request semantic.Request) (semantic.Response, error) {
	if c == nil || c.invoke == nil {
		return semantic.Response{}, &semantic.Failure{Category: semantic.FailureUnavailable}
	}
	wireRequest, err := mapping.ToWireRequest(ctx, request)
	if err != nil {
		return semantic.Response{}, err
	}
	wireResponse, err := c.invoke(withClientProtocol(ctx, semantic.ProtocolMajor, semantic.ProtocolMinor, semantic.CapabilityName), wireRequest,
		grpc.MaxCallSendMsgSize(envelope.MaxRequestWireBytes),
		grpc.MaxCallRecvMsgSize(envelope.MaxResponseWireBytes),
		grpc.WaitForReady(false),
	)
	if err != nil {
		return semantic.Response{}, mapping.FromStatusError(err)
	}
	return mapping.FromWireResponse(wireResponse)
}

// EvaluateRecordAccess validates the complete semantic batch before transport
// work, installs the exact capability declaration, and accepts only a response
// correlated to the submitted identifiers.
func (c *RecordAccessClient) EvaluateRecordAccess(ctx context.Context, request accesssemantic.Request) (accesssemantic.Response, error) {
	if c == nil || c.invoke == nil {
		return accesssemantic.Response{}, &accesssemantic.Failure{Category: accesssemantic.FailureUnavailable}
	}
	wireRequest, err := mapping.ToWireEvaluateRecordAccessRequest(ctx, request)
	if err != nil {
		return accesssemantic.Response{}, err
	}
	wireResponse, err := c.invoke(withClientProtocol(ctx, accesssemantic.ProtocolMajor, accesssemantic.ProtocolMinor, accesssemantic.CapabilityName), wireRequest,
		grpc.MaxCallSendMsgSize(envelope.MaxRequestWireBytes),
		grpc.MaxCallRecvMsgSize(envelope.MaxResponseWireBytes),
		grpc.WaitForReady(false),
	)
	if err != nil {
		return accesssemantic.Response{}, mapping.FromRecordAccessStatusError(err)
	}
	return mapping.FromWireEvaluateRecordAccessResponse(request, wireResponse)
}

func (c *CreateDefaultPreviewClient) PreviewCreateDefaults(
	ctx context.Context,
	request defaultsemantic.Request,
) (defaultsemantic.Response, error) {
	if c == nil || c.invoke == nil {
		return defaultsemantic.Response{}, &defaultsemantic.Failure{Category: defaultsemantic.FailureUnavailable}
	}
	wireRequest, err := mapping.ToWirePreviewCreateDefaultsRequest(ctx, request)
	if err != nil {
		return defaultsemantic.Response{}, err
	}
	wireResponse, err := c.invoke(
		withClientProtocol(ctx, defaultsemantic.ProtocolMajor, defaultsemantic.ProtocolMinor, defaultsemantic.CapabilityName),
		wireRequest,
		grpc.MaxCallSendMsgSize(envelope.MaxRequestWireBytes),
		grpc.MaxCallRecvMsgSize(envelope.MaxResponseWireBytes),
		grpc.WaitForReady(false),
	)
	if err != nil {
		return defaultsemantic.Response{}, mapping.FromCreateDefaultPreviewStatusError(err)
	}
	return mapping.FromWirePreviewCreateDefaultsResponse(request, wireResponse)
}

func (c *QueryClient) QueryRecords(ctx context.Context, request querysemantic.Request) (querysemantic.Response, error) {
	if c == nil || c.invokeRecords == nil {
		return querysemantic.Response{}, &querysemantic.Failure{Category: querysemantic.FailureUnavailable}
	}
	wireRequest, err := mapping.ToWireQueryRecordsRequest(ctx, request)
	if err != nil {
		return querysemantic.Response{}, err
	}
	wireResponse, err := c.invokeRecords(withClientProtocol(ctx, querysemantic.ProtocolMajor, querysemantic.ProtocolMinor, querysemantic.CapabilityName), wireRequest,
		grpc.MaxCallSendMsgSize(envelope.MaxRequestWireBytes), grpc.MaxCallRecvMsgSize(envelope.MaxResponseWireBytes), grpc.WaitForReady(false))
	if err != nil {
		return querysemantic.Response{}, mapping.FromQueryStatusError(err)
	}
	return mapping.FromWireQueryRecordsResponse(wireResponse)
}

func (c *QueryClient) QueryHistory(ctx context.Context, request querysemantic.HistoryRequest) (querysemantic.HistoryResponse, error) {
	if c == nil || c.invokeHistory == nil {
		return querysemantic.HistoryResponse{}, &querysemantic.Failure{Category: querysemantic.FailureUnavailable}
	}
	wireRequest, err := mapping.ToWireQueryHistoryRequest(ctx, request)
	if err != nil {
		return querysemantic.HistoryResponse{}, err
	}
	wireResponse, err := c.invokeHistory(withClientProtocol(ctx, querysemantic.ProtocolMajor, querysemantic.ProtocolMinor, querysemantic.CapabilityName), wireRequest,
		grpc.MaxCallSendMsgSize(envelope.MaxRequestWireBytes), grpc.MaxCallRecvMsgSize(envelope.MaxResponseWireBytes), grpc.WaitForReady(false))
	if err != nil {
		return querysemantic.HistoryResponse{}, mapping.FromQueryStatusError(err)
	}
	return mapping.FromWireQueryHistoryResponse(wireResponse)
}

func (c *RelatedCountClient) CountRelatedRecords(ctx context.Context, request relatedsemantic.Request) (relatedsemantic.Response, error) {
	if c == nil || c.invoke == nil {
		return relatedsemantic.Response{}, &relatedsemantic.Failure{Category: relatedsemantic.FailureUnavailable}
	}
	wireRequest, err := mapping.ToWireCountRelatedRecordsRequest(ctx, request)
	if err != nil {
		return relatedsemantic.Response{}, err
	}
	wireResponse, err := c.invoke(withClientProtocol(ctx, relatedsemantic.ProtocolMajor, relatedsemantic.ProtocolMinor, relatedsemantic.CapabilityName), wireRequest,
		grpc.MaxCallSendMsgSize(envelope.MaxRequestWireBytes), grpc.MaxCallRecvMsgSize(envelope.MaxResponseWireBytes), grpc.WaitForReady(false))
	if err != nil {
		return relatedsemantic.Response{}, mapping.FromRelatedCountStatusError(err)
	}
	return mapping.FromWireCountRelatedRecordsResponse(request, wireResponse)
}

func (c *RecycleBinClient) ListRecycleBin(ctx context.Context, request recyclesemantic.Request) (recyclesemantic.Response, error) {
	if c == nil || c.invoke == nil {
		return recyclesemantic.Response{}, &recyclesemantic.Failure{Category: recyclesemantic.FailureUnavailable}
	}
	wireRequest, err := mapping.ToWireListRecycleBinRequest(ctx, request)
	if err != nil {
		return recyclesemantic.Response{}, err
	}
	wireResponse, err := c.invoke(withClientProtocol(ctx, recyclesemantic.ProtocolMajor, recyclesemantic.ProtocolMinor, recyclesemantic.CapabilityName), wireRequest,
		grpc.MaxCallSendMsgSize(envelope.MaxRequestWireBytes), grpc.MaxCallRecvMsgSize(envelope.MaxResponseWireBytes), grpc.WaitForReady(false))
	if err != nil {
		return recyclesemantic.Response{}, mapping.FromRecycleBinStatusError(err)
	}
	return mapping.FromWireListRecycleBinResponse(wireResponse)
}

func (c *RecycleBinMutationClient) RestoreRecycleBin(ctx context.Context, request recyclemutationsemantic.Request) (recyclemutationsemantic.Response, error) {
	if c == nil || c.restore == nil {
		return recyclemutationsemantic.Response{}, &recyclemutationsemantic.Failure{Category: recyclemutationsemantic.FailureUnavailable}
	}
	wireRequest, err := mapping.ToWireRestoreRecycleBinRequest(ctx, request)
	if err != nil {
		return recyclemutationsemantic.Response{}, err
	}
	wireResponse, err := c.restore(withClientProtocol(ctx, recyclemutationsemantic.ProtocolMajor, recyclemutationsemantic.ProtocolMinor, recyclemutationsemantic.CapabilityName), wireRequest,
		grpc.MaxCallSendMsgSize(envelope.MaxRequestWireBytes), grpc.MaxCallRecvMsgSize(envelope.MaxResponseWireBytes), grpc.WaitForReady(false))
	if err != nil {
		return recyclemutationsemantic.Response{}, mapping.FromRecycleBinMutationStatusError(err)
	}
	return mapping.FromWireRestoreRecycleBinResponse(request, wireResponse)
}

func (c *RecycleBinMutationClient) PurgeRecycleBin(ctx context.Context, request recyclemutationsemantic.Request) (recyclemutationsemantic.Response, error) {
	if c == nil || c.purge == nil {
		return recyclemutationsemantic.Response{}, &recyclemutationsemantic.Failure{Category: recyclemutationsemantic.FailureUnavailable}
	}
	wireRequest, err := mapping.ToWirePurgeRecycleBinRequest(ctx, request)
	if err != nil {
		return recyclemutationsemantic.Response{}, err
	}
	wireResponse, err := c.purge(withClientProtocol(ctx, recyclemutationsemantic.ProtocolMajor, recyclemutationsemantic.ProtocolMinor, recyclemutationsemantic.CapabilityName), wireRequest,
		grpc.MaxCallSendMsgSize(envelope.MaxRequestWireBytes), grpc.MaxCallRecvMsgSize(envelope.MaxResponseWireBytes), grpc.WaitForReady(false))
	if err != nil {
		return recyclemutationsemantic.Response{}, mapping.FromRecycleBinMutationStatusError(err)
	}
	return mapping.FromWirePurgeRecycleBinResponse(request, wireResponse)
}

func (c *RecordViewReadClient) ReadRecordView(ctx context.Context, request recordviewsemantic.ReadRequest) (recordviewsemantic.ReadResponse, error) {
	if c == nil || c.read == nil {
		return recordviewsemantic.ReadResponse{}, &recordviewsemantic.Failure{Category: recordviewsemantic.FailureUnavailable}
	}
	wireRequest, err := mapping.ToWireReadRecordViewRequest(ctx, request)
	if err != nil {
		return recordviewsemantic.ReadResponse{}, err
	}
	wireResponse, err := c.read(withClientProtocol(ctx, recordviewsemantic.ProtocolMajor, recordviewsemantic.ProtocolMinor, recordviewsemantic.CapabilityName), wireRequest,
		grpc.MaxCallSendMsgSize(envelope.MaxRequestWireBytes), grpc.MaxCallRecvMsgSize(envelope.MaxResponseWireBytes), grpc.WaitForReady(false))
	if err != nil {
		return recordviewsemantic.ReadResponse{}, mapping.FromRecordViewStatusError(err)
	}
	return mapping.FromWireReadRecordViewResponse(request, wireResponse)
}

// ReadRecordView retains the released RecordViewClient method. Do not promote
// RecordViewReadClient through embedding: promoted methods panic when the
// outer *RecordViewClient is nil before the inner nil guard can fail closed.
func (c *RecordViewClient) ReadRecordView(ctx context.Context, request recordviewsemantic.ReadRequest) (recordviewsemantic.ReadResponse, error) {
	if c == nil || c.read == nil {
		return recordviewsemantic.ReadResponse{}, &recordviewsemantic.Failure{Category: recordviewsemantic.FailureUnavailable}
	}
	wireRequest, err := mapping.ToWireReadRecordViewRequest(ctx, request)
	if err != nil {
		return recordviewsemantic.ReadResponse{}, err
	}
	wireResponse, err := c.read(withClientProtocol(ctx, recordviewsemantic.ProtocolMajor, recordviewsemantic.ProtocolMinor, recordviewsemantic.CapabilityName), wireRequest,
		grpc.MaxCallSendMsgSize(envelope.MaxRequestWireBytes), grpc.MaxCallRecvMsgSize(envelope.MaxResponseWireBytes), grpc.WaitForReady(false))
	if err != nil {
		return recordviewsemantic.ReadResponse{}, mapping.FromRecordViewStatusError(err)
	}
	return mapping.FromWireReadRecordViewResponse(request, wireResponse)
}

func (c *RecordViewClient) ResolveRecordAttachmentSources(ctx context.Context, request recordviewsemantic.ResolveAttachmentSourcesRequest) (recordviewsemantic.ResolveAttachmentSourcesResponse, error) {
	if c == nil || c.resolve == nil {
		return recordviewsemantic.ResolveAttachmentSourcesResponse{}, &recordviewsemantic.Failure{Category: recordviewsemantic.FailureUnavailable}
	}
	wireRequest, err := mapping.ToWireResolveRecordAttachmentSourcesRequest(ctx, request)
	if err != nil {
		return recordviewsemantic.ResolveAttachmentSourcesResponse{}, err
	}
	wireResponse, err := c.resolve(withClientProtocol(ctx, recordviewsemantic.ProtocolMajor, recordviewsemantic.ProtocolMinor, recordviewsemantic.CapabilityName), wireRequest,
		grpc.MaxCallSendMsgSize(envelope.MaxRequestWireBytes), grpc.MaxCallRecvMsgSize(envelope.MaxResponseWireBytes), grpc.WaitForReady(false))
	if err != nil {
		return recordviewsemantic.ResolveAttachmentSourcesResponse{}, mapping.FromRecordViewStatusError(err)
	}
	return mapping.FromWireResolveRecordAttachmentSourcesResponse(request, wireResponse)
}

func (c *AttachmentSourceClient) ResolveAttachmentSources(ctx context.Context, request attachmentsemantic.ResolveRequest) (attachmentsemantic.ResolveResponse, error) {
	if c == nil || c.resolve == nil {
		return attachmentsemantic.ResolveResponse{}, &attachmentsemantic.Failure{Category: attachmentsemantic.FailureUnavailable}
	}
	wireRequest, err := mapping.ToWireResolveAttachmentSourcesRequest(ctx, request)
	if err != nil {
		return attachmentsemantic.ResolveResponse{}, err
	}
	wireResponse, err := c.resolve(withClientProtocol(ctx, attachmentsemantic.ProtocolMajor, attachmentsemantic.ProtocolMinor, attachmentsemantic.CapabilityName), wireRequest,
		grpc.MaxCallSendMsgSize(envelope.MaxRequestWireBytes), grpc.MaxCallRecvMsgSize(envelope.MaxResponseWireBytes), grpc.WaitForReady(false))
	if err != nil {
		return attachmentsemantic.ResolveResponse{}, mapping.FromRecordAttachmentSourceStatusError(err)
	}
	return mapping.FromWireResolveAttachmentSourcesResponse(request, wireResponse)
}

// Exchange installs the user bearer only in api-host's dedicated sensitive
// metadata channel. The standard authorization key remains untouched for
// workload authentication. The bearer is not added to the semantic request,
// and this client does not decode or inspect the returned delegation.
func (c *IdentityExchangeClient) Exchange(ctx context.Context, endUserBearer string, request identitysemantic.Request) (identitysemantic.Response, error) {
	if c == nil || c.invoke == nil {
		return identitysemantic.Response{}, &identitysemantic.Failure{Category: identitysemantic.FailureUnavailable}
	}
	if endUserBearer == "" || strings.TrimSpace(endUserBearer) != endUserBearer || len(endUserBearer) > envelope.MaxRequestWireBytes {
		return identitysemantic.Response{}, &identitysemantic.Failure{Category: identitysemantic.FailureUnauthenticated}
	}
	wireRequest, err := mapping.ToWireIdentityExchangeRequest(ctx, request)
	if err != nil {
		return identitysemantic.Response{}, err
	}
	callCtx := withClientProtocol(ctx, identitysemantic.ProtocolMajor, identitysemantic.ProtocolMinor, identitysemantic.CapabilityName)
	values, _ := metadata.FromOutgoingContext(callCtx)
	values = values.Copy()
	values.Delete(EndUserAuthorizationMetadataKey)
	values.Set(EndUserAuthorizationMetadataKey, "Bearer "+endUserBearer)
	callCtx = metadata.NewOutgoingContext(callCtx, values)
	wireResponse, err := c.invoke(callCtx, wireRequest,
		grpc.MaxCallSendMsgSize(envelope.MaxRequestWireBytes),
		grpc.MaxCallRecvMsgSize(envelope.MaxResponseWireBytes),
		grpc.WaitForReady(false),
	)
	if err != nil {
		return identitysemantic.Response{}, mapping.FromIdentityStatusError(err)
	}
	return mapping.FromWireIdentityExchangeResponse(wireResponse)
}

func withClientProtocol(ctx context.Context, protocolMajor, protocolMinor uint32, capability string) context.Context {
	values, _ := metadata.FromOutgoingContext(ctx)
	values = values.Copy()
	values.Delete(envelope.ProtocolMajorMetadataKey)
	values.Delete(envelope.ProtocolMinorMetadataKey)
	values.Delete(envelope.CapabilityMetadataKey)
	values.Set(envelope.ProtocolMajorMetadataKey, strconv.FormatUint(uint64(protocolMajor), 10))
	values.Set(envelope.ProtocolMinorMetadataKey, strconv.FormatUint(uint64(protocolMinor), 10))
	values.Set(envelope.CapabilityMetadataKey, capability)
	return metadata.NewOutgoingContext(ctx, values)
}
