package runtimeapi

import (
	"context"
	"errors"
	"fmt"
	"slices"
	"sync/atomic"

	"github.com/pingcap-inc/nexuscrm-api-host/runtimegrpc"
	catalogsemantic "github.com/pingcap-inc/nexuscrm-contract/pkg/catalog/consumerv1"
	commonsemantic "github.com/pingcap-inc/nexuscrm-contract/pkg/runtime/commonv1"
	mailboxauthorizationsemantic "github.com/pingcap-inc/nexuscrm-contract/pkg/runtime/mailboxauthorizationv1"
	"google.golang.org/grpc"
)

// MailboxAuthorizationSelection identifies the one Integration workload that
// may authorize connected-mailbox operations. The operation is metadata-free,
// but its receipts remain pinned to the stopped Catalog view.
type MailboxAuthorizationSelection struct {
	OrgID             string
	AppInstallationID string
	WorkloadID        string
}

type MailboxAuthorizationAdmission struct {
	selection     MailboxAuthorizationSelection
	current       catalogsemantic.IntegrationBindingView
	securityEpoch atomic.Uint64
}

func NewMailboxAuthorizationAdmission(
	ctx context.Context,
	selection MailboxAuthorizationSelection,
	catalogView CatalogConsumerViewEngine,
) (*MailboxAuthorizationAdmission, error) {
	if ctx == nil || selection.OrgID == "" || selection.AppInstallationID == "" ||
		selection.WorkloadID == "" || catalogView == nil {
		return nil, errors.New("runtime mailbox authorization admission: selection is incomplete")
	}
	scope := CatalogConsumerScope{
		OrgID: selection.OrgID, AppInstallationID: selection.AppInstallationID,
		WorkloadID:   selection.WorkloadID,
		Capabilities: []string{catalogsemantic.IntegrationBindingCapabilityName},
	}
	readCtx, cancel := context.WithTimeout(ctx, integrationConsumerPrepareAttemptCap)
	defer cancel()
	current, err := catalogView.GetIntegrationBindingView(
		readCtx,
		scope,
		catalogsemantic.GetRequest{},
	)
	if err != nil {
		return nil, fmt.Errorf("runtime mailbox authorization admission: read Catalog authority: %w", err)
	}
	if err := catalogsemantic.ValidateIntegrationBindingView(current); err != nil {
		return nil, fmt.Errorf("runtime mailbox authorization admission: validate Catalog authority: %w", err)
	}
	if err := validateIntegrationBindingViewScope(scope, nil, current); err != nil {
		return nil, err
	}
	admission := &MailboxAuthorizationAdmission{
		selection: selection,
		current:   cloneRuntimeIntegrationBindingView(current),
	}
	admission.securityEpoch.Store(current.Receipts.SecurityEpoch)
	return admission, nil
}

// ReconcileSecurityEpoch advances only the Runtime security axis. The stopped
// Catalog lineage and Integration workload binding remain immutable.
func (admission *MailboxAuthorizationAdmission) ReconcileSecurityEpoch(
	securityEpoch uint64,
) error {
	if admission == nil || securityEpoch == 0 {
		return errors.New("runtime mailbox authorization admission: security epoch is unavailable")
	}
	for {
		current := admission.securityEpoch.Load()
		if current == 0 || securityEpoch < current {
			return errors.New("runtime mailbox authorization admission: security epoch cannot move backward")
		}
		if securityEpoch == current || admission.securityEpoch.CompareAndSwap(current, securityEpoch) {
			return nil
		}
	}
}

func (admission *MailboxAuthorizationAdmission) 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,
				mailboxAuthorizationAdmissionFailure(commonsemantic.FailureInternal),
			)
		}
		if capability != mailboxauthorizationsemantic.CapabilityName {
			return handler(ctx, request)
		}
		admitted, err := admission.bind(ctx)
		if err != nil {
			return nil, runtimegrpc.StatusError(ctx, err)
		}
		return handler(admitted, request)
	}
}

func (admission *MailboxAuthorizationAdmission) bind(ctx context.Context) (context.Context, error) {
	if admission == nil || ctx == nil || ctx.Err() != nil ||
		catalogsemantic.ValidateIntegrationBindingView(admission.current) != nil {
		if ctx != nil && ctx.Err() != nil {
			return nil, ctx.Err()
		}
		return nil, mailboxAuthorizationAdmissionFailure(commonsemantic.FailureUnavailable)
	}
	var orgID, workloadID, appInstallationID string
	var securityEpoch uint64
	if trusted, ok := trustedCallFromContext(ctx); ok {
		orgID, workloadID = trusted.orgID, trusted.callerWorkloadID
		appInstallationID, securityEpoch = trusted.appInstallationID, trusted.securityEpoch
		if !slices.Contains(trusted.capabilities, mailboxauthorizationsemantic.CapabilityName) {
			return nil, mailboxAuthorizationAdmissionFailure(commonsemantic.FailureForbidden)
		}
	} else if trusted, ok := trustedWorkloadCallFromContext(ctx); ok {
		orgID, workloadID = trusted.orgID, trusted.callerWorkloadID
		appInstallationID, securityEpoch = admission.selection.AppInstallationID, trusted.securityEpoch
		if trusted.capability != mailboxauthorizationsemantic.CapabilityName {
			return nil, mailboxAuthorizationAdmissionFailure(commonsemantic.FailureForbidden)
		}
	} else {
		return nil, mailboxAuthorizationAdmissionFailure(commonsemantic.FailureUnauthenticated)
	}
	if orgID != admission.selection.OrgID || workloadID != admission.selection.WorkloadID ||
		appInstallationID != admission.selection.AppInstallationID {
		return nil, mailboxAuthorizationAdmissionFailure(commonsemantic.FailureForbidden)
	}
	currentSecurityEpoch := admission.securityEpoch.Load()
	if currentSecurityEpoch == 0 {
		return nil, mailboxAuthorizationAdmissionFailure(commonsemantic.FailureUnavailable)
	}
	if securityEpoch != currentSecurityEpoch {
		return nil, &mailboxauthorizationsemantic.Failure{
			Category: commonsemantic.FailureStaleState, StaleAxis: commonsemantic.StaleAxisSecurity,
			RequiredRevision: currentSecurityEpoch,
			ServedRevision:   securityEpoch,
		}
	}
	receipts := admission.current.Receipts
	receipts.SecurityEpoch = currentSecurityEpoch
	scope := RuntimeExecutionScope{
		orgID: admission.selection.OrgID, appInstallationID: admission.selection.AppInstallationID,
		workloadID: admission.selection.WorkloadID, capability: mailboxauthorizationsemantic.CapabilityName,
		receipts: receiptsForCapability(
			receipts,
			mailboxauthorizationsemantic.CapabilityName,
		),
	}
	return context.WithValue(ctx, runtimeExecutionScopeContextKey{}, scope), nil
}

func mailboxAuthorizationAdmissionFailure(category commonsemantic.FailureCategory) error {
	return &mailboxauthorizationsemantic.Failure{Category: category}
}
