using System; using Svrnty.CQRS.Events.Abstractions.Streaming; using Svrnty.CQRS.Events.Subscriptions; using Svrnty.CQRS.Events.Configuration; using Svrnty.CQRS.Events.Abstractions.Subscriptions; using Svrnty.CQRS.Events.Abstractions.Models; using System.Collections.Generic; using Microsoft.Extensions.DependencyInjection; using Svrnty.CQRS.Events.Abstractions; namespace Svrnty.CQRS.Events.Core; /// /// Builder for configuring event streaming services using a fluent API. /// /// /// /// The provides a fluent interface for configuring /// event streams, subscriptions, and delivery options. This builder is returned by /// /// and allows for progressive configuration as features are added in each phase. /// /// /// Phase 1 Focus: /// Basic stream configuration with workflow-based event emission. /// Additional configuration options will be added in later phases. /// /// public class EventStreamingBuilder { private readonly IServiceCollection _services; private readonly Dictionary _streamConfigurations = new(); /// /// Initializes a new instance of the class. /// /// The service collection to configure. internal EventStreamingBuilder(IServiceCollection services) { _services = services ?? throw new ArgumentNullException(nameof(services)); } /// /// Gets the service collection being configured. /// public IServiceCollection Services => _services; /// /// Adds a stream configuration for a specific workflow. /// /// The workflow type that emits events to this stream. /// Optional action to configure stream settings. /// The builder for method chaining. /// /// /// Phase 1 Behavior: /// Creates an ephemeral stream with at-least-once delivery by default. /// Stream name is derived from the workflow type name. /// /// /// Example Usage: /// /// streaming.AddStream<UserWorkflow>(stream => /// { /// stream.Type = StreamType.Persistent; /// stream.DeliverySemantics = DeliverySemantics.ExactlyOnce; /// }); /// /// /// public EventStreamingBuilder AddStream(Action? configure = null) where TWorkflow : Workflow { var streamName = GetStreamName(); var config = new StreamConfiguration(streamName); configure?.Invoke(config); config.Validate(); _streamConfigurations[streamName] = config; // Register the configuration as a singleton _services.AddSingleton(config); return this; } /// /// Adds a stream configuration with an explicit stream name. /// /// The name of the stream. /// Optional action to configure stream settings. /// The builder for method chaining. /// /// Use this overload when you need explicit control over stream naming, /// or when configuring streams that aren't associated with a specific workflow type. /// public EventStreamingBuilder AddStream(string streamName, Action? configure = null) { if (string.IsNullOrWhiteSpace(streamName)) throw new ArgumentException("Stream name cannot be null or whitespace.", nameof(streamName)); var config = new StreamConfiguration(streamName); configure?.Invoke(config); config.Validate(); _streamConfigurations[streamName] = config; // Register the configuration as a singleton _services.AddSingleton(config); return this; } /// /// Gets the stream name for a workflow type. /// /// The workflow type. /// The stream name derived from the workflow type. /// /// Naming Convention: /// - Removes "Workflow" suffix if present /// - Converts to kebab-case /// - Appends "-events" /// /// Examples: /// - UserWorkflow → "user-events" /// - InvitationWorkflow → "invitation-events" /// - OrderProcessing → "order-processing-events" /// /// private static string GetStreamName() where TWorkflow : Workflow { var typeName = typeof(TWorkflow).Name; // Remove "Workflow" suffix if present if (typeName.EndsWith("Workflow", StringComparison.OrdinalIgnoreCase)) { typeName = typeName.Substring(0, typeName.Length - "Workflow".Length); } // Convert to kebab-case (simplified version for now) var kebabCase = System.Text.RegularExpressions.Regex.Replace( typeName, "(? /// Gets all registered stream configurations. /// Used internally by the framework. /// internal IReadOnlyDictionary GetStreamConfigurations() { return _streamConfigurations; } // ======================================================================== // SUBSCRIPTION CONFIGURATION (Phase 1.4) // ======================================================================== /// /// Adds a subscription configuration for consuming events from a stream. /// /// Unique subscription identifier. /// Name of the stream to subscribe to. /// Optional action to configure subscription settings. /// The builder for method chaining. /// /// /// Phase 1 Behavior: /// Creates a subscription with Broadcast mode by default. /// Subscription is registered with the EventSubscriptionClient for consumption. /// /// /// Example Usage: /// /// streaming.AddSubscription("analytics", "user-events", sub => /// { /// sub.Mode = SubscriptionMode.Exclusive; /// sub.VisibilityTimeout = TimeSpan.FromSeconds(60); /// sub.EventTypeFilter = new HashSet<string> { "UserAddedEvent", "UserRemovedEvent" }; /// }); /// /// /// public EventStreamingBuilder AddSubscription( string subscriptionId, string streamName, Action? configure = null) { if (string.IsNullOrWhiteSpace(subscriptionId)) throw new ArgumentException("Subscription ID cannot be null or whitespace.", nameof(subscriptionId)); if (string.IsNullOrWhiteSpace(streamName)) throw new ArgumentException("Stream name cannot be null or whitespace.", nameof(streamName)); var subscription = new Subscription(subscriptionId, streamName); configure?.Invoke(subscription); subscription.Validate(); // Register the subscription with the client // (We'll get the client from DI when the builder is executed) _services.AddSingleton(subscription); // Configure the subscription when the service provider is built _services.AddSingleton(subscription); return this; } /// /// Adds a subscription for a specific workflow stream. /// /// The workflow type whose events to subscribe to. /// Unique subscription identifier. /// Optional action to configure subscription settings. /// The builder for method chaining. /// /// Convenience method that automatically derives the stream name from the workflow type. /// public EventStreamingBuilder AddSubscription( string subscriptionId, Action? configure = null) where TWorkflow : Workflow { var streamName = GetStreamName(); return AddSubscription(subscriptionId, streamName, configure); } }