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);
}
}