package runtimeapi

import (
	"context"
	"errors"
	"slices"

	"github.com/nexuscrm/metadata"
	"github.com/nexuscrm/metadata/catalogsnapshot"
	"github.com/pingcap-inc/nexuscrm-api-host/runtimegrpc"
	pagelayoutsemantic "github.com/pingcap-inc/nexuscrm-contract/pkg/runtime/pagelayoutresolutionv1"
	recordsemantic "github.com/pingcap-inc/nexuscrm-contract/pkg/runtime/recordsv1"
	"google.golang.org/grpc"
)

// PageLayoutConsumerSelection binds the shared L1 handler workload to the
// consumer-only PageLayout view published for one immutable Catalog snapshot.
// It deliberately does not manufacture an L1 HandlerBinding: several L1
// handlers consume this same neutral Runtime dependency.
type PageLayoutConsumerSelection struct {
	OrgID                string
	AppInstallationID    string
	WorkloadID           string
	RuntimeMetadataView  *catalogsnapshot.RuntimeMetadataView
	CurrentSecurityEpoch func() (uint64, bool)
}

// PageLayoutConsumerAdmission joins the two authorities PageLayout requests
// need: SecurityAdmission's authenticated delegated user and Catalog's exact
// consumer-only metadata view. The request cannot choose either authority.
type preparedPageLayoutConsumer struct {
	selection PageLayoutConsumerSelection
}

type PageLayoutConsumerAdmission struct {
	prepared func() *preparedPageLayoutConsumer
}

func NewPageLayoutConsumerAdmission(
	selection PageLayoutConsumerSelection,
) (*PageLayoutConsumerAdmission, error) {
	view := selection.RuntimeMetadataView
	if selection.OrgID == "" || selection.AppInstallationID == "" ||
		selection.WorkloadID == "" || selection.CurrentSecurityEpoch == nil ||
		view == nil || view.OrgID() != selection.OrgID ||
		view.AppInstallationID() != selection.AppInstallationID ||
		view.CapabilityID() != pagelayoutsemantic.CapabilityName ||
		view.Profile() != catalogsnapshot.ProjectionPageLayoutResolutionV1 {
		return nil, errors.New(
			"runtime page-layout consumer admission: selection is incomplete",
		)
	}
	snapshot := &preparedPageLayoutConsumer{selection: selection}
	return &PageLayoutConsumerAdmission{prepared: func() *preparedPageLayoutConsumer { return snapshot }}, nil
}

func (a *PageLayoutConsumerAdmission) 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 != pagelayoutsemantic.CapabilityName {
			return handler(ctx, request)
		}
		admitted, err := a.bind(ctx)
		if err != nil {
			return nil, runtimegrpc.StatusError(ctx, err)
		}
		return handler(admitted, request)
	}
}

func (a *PageLayoutConsumerAdmission) bind(ctx context.Context) (context.Context, error) {
	if ctx == nil || ctx.Err() != nil {
		if ctx != nil {
			return nil, ctx.Err()
		}
		return nil, &pagelayoutsemantic.Failure{
			Category: pagelayoutsemantic.FailureUnavailable,
		}
	}
	if a == nil || a.prepared == nil {
		return nil, &pagelayoutsemantic.Failure{
			Category: pagelayoutsemantic.FailureUnavailable,
		}
	}
	snapshot := a.prepared()
	if snapshot == nil {
		return nil, &pagelayoutsemantic.Failure{Category: pagelayoutsemantic.FailureUnavailable}
	}
	trusted, ok := trustedCallFromContext(ctx)
	if !ok {
		return nil, &pagelayoutsemantic.Failure{
			Category: pagelayoutsemantic.FailureUnauthenticated,
		}
	}
	if trusted.orgID != snapshot.selection.OrgID ||
		trusted.appInstallationID != snapshot.selection.AppInstallationID ||
		trusted.callerWorkloadID != snapshot.selection.WorkloadID ||
		!slices.Contains(trusted.capabilities, pagelayoutsemantic.CapabilityName) {
		return nil, &pagelayoutsemantic.Failure{
			Category: pagelayoutsemantic.FailureForbidden,
		}
	}
	securityEpoch, ready := snapshot.selection.CurrentSecurityEpoch()
	if !ready || securityEpoch == 0 {
		return nil, &pagelayoutsemantic.Failure{
			Category: pagelayoutsemantic.FailureUnavailable,
		}
	}
	if trusted.securityEpoch != securityEpoch {
		return nil, &pagelayoutsemantic.Failure{
			Category:         pagelayoutsemantic.FailureStaleState,
			StaleAxis:        pagelayoutsemantic.StaleAxisSecurity,
			RequiredRevision: securityEpoch,
			ServedRevision:   trusted.securityEpoch,
		}
	}
	lineage := snapshot.selection.RuntimeMetadataView.Lineage()
	scope := RuntimeExecutionScope{
		view:  snapshot.selection.RuntimeMetadataView,
		orgID: snapshot.selection.OrgID, appInstallationID: snapshot.selection.AppInstallationID,
		workloadID: snapshot.selection.WorkloadID,
		capability: pagelayoutsemantic.CapabilityName,
		receipts: pagelayoutsemantic.Receipts{
			ProtocolVersion:   "1.0",
			CapabilityVersion: pagelayoutsemantic.CapabilityName,
			CatalogRevision:   uint64(lineage.CatalogRevision),
			SchemaGeneration:  uint64(lineage.SchemaGeneration),
			EngineGeneration:  uint64(lineage.EngineGeneration),
			SecurityEpoch:     securityEpoch,
		},
	}
	ctx = metadata.WithRuntimeMetadataView(ctx, scope.view)
	return context.WithValue(ctx, runtimeExecutionScopeContextKey{}, scope), nil
}
