package automation

import (
	"context"

	"github.com/nexuscrm/shared/pkg/models"
)

// Option configures a FlowExecutor at construction time. Options are the
// public surface for substituting in-memory implementations of internal
// lookups during tests, without exposing FlowExecutor's unexported fields
// to external packages.
//
// Production callers pass zero opts; the executor falls through to its
// default MetadataService / QueryService lookups. Options are additive:
// each one nudges a single seam, and a missing option leaves the
// production behavior unchanged.
//
// External test modules MUST NOT import this package directly (Go's
// internal/ rule blocks that anyway). The L0 bridge package
// backend/pkg/automationtesting wraps these options for use by scenario
// modules under tests/conformance/.
type Option func(*FlowExecutor)

// WithFlowActionCall adds one registered actionCall handler to the executor's
// built-in registry. Duplicate action types panic during construction so app
// packs cannot silently override L0 or another pack's handler.
func WithFlowActionCall(registration FlowActionCallRegistration) Option {
	return WithFlowActionCalls(registration)
}

// WithFlowActionCalls adds registered actionCall handlers to the executor's
// built-in registry. Registrations are additive; a caller cannot replace the
// whole registry through this public seam.
func WithFlowActionCalls(registrations ...FlowActionCallRegistration) Option {
	return func(e *FlowExecutor) {
		e.RegisterFlowActionCalls(registrations...)
	}
}

// WithFlowVersionResolver replaces the FlowDefinition + Flow + FlowVersion
// query path used during root, subflow, and protected-flow dispatch with the
// supplied resolver. Independent Runtime composition must bind this to its
// request-scoped immutable metadata view so Flow execution cannot cross a
// Catalog revision. Tests may inject an in-memory pack of FlowVersions.
//
// fn must be safe for concurrent use.
func WithFlowVersionResolver(
	fn func(ctx context.Context, apiName string, user *models.UserSession) (*models.FlowVersion, error),
) Option {
	return func(e *FlowExecutor) {
		e.flowVersionFn = fn
	}
}

// WithBeforeCreatePreviewSafetyResolver replaces the metadata-backed proof
// that active before-create flows on an object are safe to run during a no-DML
// standard-action create preview. Tests use this to model org-level trigger
// state without standing up MetadataService and _System_TriggerSubscription.
func WithBeforeCreatePreviewSafetyResolver(
	fn func(ctx context.Context, objectAPIName string, user *models.UserSession) (bool, error),
) Option {
	return func(e *FlowExecutor) {
		e.beforeCreatePreviewSafeFn = fn
	}
}

// WithConnectorMappingResolver wires the production direction-aware connector
// field-mapping resolver used by the applyConnectorFieldMappings invocable
// action. Production passes the bootstrap-backed resolver at service-manager
// wiring time; tests may instead set the connectorMappingResolverFn seam.
func WithConnectorMappingResolver(r ConnectorMappingResolver) Option {
	return func(e *FlowExecutor) {
		e.connectorMappingResolver = r
	}
}

// WithConnectorPushDurableStore wires inbound identity evidence, recovery and
// historical attempt diagnostics.
func WithConnectorPushDurableStore(store ConnectorPushDurableStore) Option {
	return func(e *FlowExecutor) {
		e.SetConnectorPushDurableStore(store)
	}
}

// WithRuntimeOutboundIntentSink installs the standalone Runtime composition's
// provider-neutral PushToConnector handoff to Integration.
func WithRuntimeOutboundIntentSink(sink RuntimeOutboundIntentSink) Option {
	return func(e *FlowExecutor) {
		if err := e.EnableRuntimeOutboundIntents(sink); err != nil {
			panic(err)
		}
	}
}

// WithFieldReadAuthorizer wires the invoking-user FLS authorizer used by
// PushToConnector outbound payload construction.
func WithFieldReadAuthorizer(authorizer FieldReadAuthorizer) Option {
	return func(e *FlowExecutor) {
		e.fieldReadAuthorizer = authorizer
	}
}

// WithRecordReadAuthorizer wires the invoking-user object + record/RLS read
// authorizer used by PushToConnector outbound payload construction.
func WithRecordReadAuthorizer(authorizer RecordReadAuthorizer) Option {
	return func(e *FlowExecutor) {
		e.recordReadAuthorizer = authorizer
	}
}
