package runtimeapi

import (
	"context"
	"errors"

	"github.com/nexuscrm/metadata/catalogsnapshot"
	"github.com/pingcap-inc/nexuscrm-api-host/runtimegrpc"
	catalogsemantic "github.com/pingcap-inc/nexuscrm-contract/pkg/catalog/consumerv1"
	outboundintentsemantic "github.com/pingcap-inc/nexuscrm-contract/pkg/runtime/outboundintentv1"
	recordsemantic "github.com/pingcap-inc/nexuscrm-contract/pkg/runtime/recordsv1"
	"google.golang.org/grpc"
)

// OutboundIntentSelection binds one independently deployed Integration
// workload to the exact immutable record-mutation projection used to produce
// and complete provider-neutral outbound intents. It cannot author metadata or
// widen the Integration consumer's current Catalog authority.
type OutboundIntentSelection struct {
	OrgID               string
	AppInstallationID   string
	WorkloadID          string
	RuntimeMetadataView *catalogsnapshot.RuntimeMetadataView
}

// OutboundIntentAdmission closes the consumer-only admission boundary without
// manufacturing an L1 HandlerBinding. Security admission authenticates the
// delegated workload first; this owner then binds it to the prepared
// immutable IntegrationBindingView and exact Runtime projection revision.
type preparedOutboundConsumer struct {
	selection OutboundIntentSelection
	current   catalogsemantic.IntegrationBindingView
}

// prepared captures one immutable consumer from its owning serving set.
type OutboundIntentAdmission struct {
	prepared func() *preparedOutboundConsumer
}

func NewOutboundIntentAdmission(
	ctx context.Context,
	selection OutboundIntentSelection,
	catalogView CatalogConsumerViewEngine,
) (*OutboundIntentAdmission, error) {
	view := selection.RuntimeMetadataView
	if selection.OrgID == "" || selection.AppInstallationID == "" ||
		selection.WorkloadID == "" || catalogView == nil || view == nil ||
		view.OrgID() != selection.OrgID ||
		view.AppInstallationID() != selection.AppInstallationID ||
		view.CapabilityID() != outboundintentsemantic.CapabilityName ||
		view.Profile() != catalogsnapshot.ProjectionRecordMutationV1 {
		return nil, errors.New("runtime outbound intent admission: selection is incomplete")
	}
	current, err := prepareIntegrationConsumerAuthority(
		ctx,
		integrationConsumerSelection{
			orgID: selection.OrgID, appInstallationID: selection.AppInstallationID,
			workloadID: selection.WorkloadID, capability: outboundintentsemantic.CapabilityName,
			runtimeView: selection.RuntimeMetadataView,
		},
		catalogView,
	)
	if err != nil {
		return nil, err
	}
	snapshot := &preparedOutboundConsumer{selection: selection, current: current}
	return &OutboundIntentAdmission{prepared: func() *preparedOutboundConsumer { return snapshot }}, nil
}

func (a *OutboundIntentAdmission) UnaryServerInterceptor() grpc.UnaryServerInterceptor {
	return func(
		ctx context.Context,
		request any,
		_ *grpc.UnaryServerInfo,
		handler grpc.UnaryHandler,
	) (any, error) {
		capability, ok := runtimegrpc.Capability(ctx)
		if !ok {
			return nil, runtimegrpc.StatusError(ctx, &recordsemantic.Failure{
				Category: recordsemantic.FailureInternal,
			})
		}
		if capability != outboundintentsemantic.CapabilityName {
			return handler(ctx, request)
		}
		if a == nil {
			return nil, runtimegrpc.StatusError(ctx, &recordsemantic.Failure{
				Category: recordsemantic.FailureUnavailable,
			})
		}
		admitted, err := a.bind(ctx)
		if err != nil {
			return nil, runtimegrpc.StatusError(ctx, err)
		}
		return handler(admitted, request)
	}
}

// StreamServerInterceptor establishes the same immutable Integration consumer
// authority as unary outbound-intent RPCs. Attachment streaming is additive to
// the existing capability and must not become an unscoped second read path.
func (a *OutboundIntentAdmission) StreamServerInterceptor() grpc.StreamServerInterceptor {
	return func(
		server any,
		stream grpc.ServerStream,
		_ *grpc.StreamServerInfo,
		handler grpc.StreamHandler,
	) error {
		if stream == nil {
			return runtimegrpc.StatusError(context.Background(), &recordsemantic.Failure{
				Category: recordsemantic.FailureInternal,
			})
		}
		ctx := stream.Context()
		capability, ok := runtimegrpc.Capability(ctx)
		if !ok {
			return runtimegrpc.StatusError(ctx, &recordsemantic.Failure{
				Category: recordsemantic.FailureInternal,
			})
		}
		if capability != outboundintentsemantic.CapabilityName {
			return handler(server, stream)
		}
		if a == nil {
			return runtimegrpc.StatusError(ctx, &recordsemantic.Failure{
				Category: recordsemantic.FailureUnavailable,
			})
		}
		admitted, err := a.bind(ctx)
		if err != nil {
			return runtimegrpc.StatusError(ctx, err)
		}
		return handler(server, &runtimeContextServerStream{ServerStream: stream, ctx: admitted})
	}
}

func (a *OutboundIntentAdmission) bind(ctx context.Context) (context.Context, error) {
	if a == nil || a.prepared == nil {
		return nil, &recordsemantic.Failure{Category: recordsemantic.FailureUnavailable}
	}
	snapshot := a.prepared()
	if snapshot == nil {
		return nil, &recordsemantic.Failure{Category: recordsemantic.FailureUnavailable}
	}
	admitted, _, err := bindIntegrationConsumerAuthority(
		ctx,
		integrationConsumerSelection{
			orgID: snapshot.selection.OrgID, appInstallationID: snapshot.selection.AppInstallationID,
			workloadID: snapshot.selection.WorkloadID, capability: outboundintentsemantic.CapabilityName,
			runtimeView: snapshot.selection.RuntimeMetadataView,
		},
		snapshot.current,
	)
	return admitted, err
}
