using System; using Svrnty.CQRS.Events.Abstractions.Notifications; using Svrnty.CQRS.Events.Abstractions.Delivery; using Svrnty.CQRS.Events.Delivery; using Svrnty.CQRS.Events.Abstractions.EventStore; using System.Threading; using System.Threading.Tasks; using Microsoft.Extensions.Logging; using Svrnty.CQRS.Events.Abstractions; using Svrnty.CQRS.Events.Abstractions.Subscriptions; namespace Svrnty.CQRS.Events.Subscriptions; /// /// Decorator that integrates Phase 8 persistent subscription delivery with the existing event delivery pipeline. /// This wraps the default IEventDeliveryService and adds SignalR/gRPC-based delivery to active persistent subscriptions. /// /// /// /// Integration Point: /// This decorator is registered in the DI container when Phase 8 is enabled, wrapping the default EventDeliveryService. /// /// /// Responsibilities: /// - Delegates to the wrapped IEventDeliveryService for standard subscription management /// - Delivers events to active persistent subscriptions via IPersistentSubscriptionDeliveryService /// - Pushes events to connected SignalR and gRPC clients in real-time /// - Tracks sequence numbers for catch-up on reconnect /// /// public sealed class PersistentSubscriptionDeliveryDecorator : IEventDeliveryService { private readonly IEventDeliveryService _inner; private readonly IPersistentSubscriptionDeliveryService? _persistentDeliveryService; private readonly IPersistentSubscriptionStore? _subscriptionStore; private readonly ILogger _logger; public PersistentSubscriptionDeliveryDecorator( IEventDeliveryService inner, IPersistentSubscriptionDeliveryService? persistentDeliveryService, IPersistentSubscriptionStore? subscriptionStore, ILogger logger) { _inner = inner ?? throw new ArgumentNullException(nameof(inner)); _persistentDeliveryService = persistentDeliveryService; _subscriptionStore = subscriptionStore; _logger = logger ?? throw new ArgumentNullException(nameof(logger)); } public async Task DeliverEventAsync(ICorrelatedEvent @event, long sequence, CancellationToken cancellationToken = default) { // First, delegate to the wrapped service for standard processing await _inner.DeliverEventAsync(@event, sequence, cancellationToken); // Then, deliver to Phase 8 persistent subscriptions if enabled if (_persistentDeliveryService != null) { try { var deliveredCount = await _persistentDeliveryService.DeliverEventAsync( @event.CorrelationId, @event, sequence, cancellationToken); if (deliveredCount > 0) { _logger.LogDebug( "Delivered event {EventType} (sequence {Sequence}) to {Count} persistent subscription(s)", @event.GetType().Name, sequence, deliveredCount); } // Push events to connected gRPC/SignalR clients via event notifiers // This happens after state updates so clients receive events immediately if (_subscriptionStore != null) { // Note: Event notifiers (gRPC, SignalR) are called by EventEmitter via IEventNotifier // This is handled separately in the event emission pipeline // We could optionally call notifiers here as well for immediate push } } catch (Exception ex) { // Log but don't fail the entire delivery if Phase 8 delivery fails _logger.LogError( ex, "Error delivering event {EventType} (sequence {Sequence}) to persistent subscriptions", @event.GetType().Name, sequence); } } } }