using System;
using Svrnty.CQRS.Events.Abstractions.Models;
using System.Collections.Generic;
using System.Linq;
using Svrnty.CQRS.Events.Abstractions.EventStore;
namespace Svrnty.CQRS.Events.Abstractions.Models;
///
/// Base class for workflows that emit correlated events.
/// A workflow represents a logical business process that may span multiple commands.
/// Each workflow instance has a unique ID that serves as the correlation ID for all events emitted within it.
///
///
///
/// Design Philosophy:
/// - Workflows are the primary abstraction for event emission (events are implementation details)
/// - Each workflow instance represents a single logical process (e.g., one invitation, one order)
/// - Workflow ID becomes the correlation ID for all events
///
///
/// Developer Usage:
/// Create a workflow class by inheriting from this base class:
///
/// public class InvitationWorkflow : Workflow
/// {
/// public void EmitInvited(UserInvitedEvent e) => Emit(e);
/// public void EmitAccepted(UserInviteAcceptedEvent e) => Emit(e);
/// }
///
///
///
/// Framework Usage:
/// The framework manages workflow lifecycle:
/// - Sets when workflow starts or continues
/// - Sets based on whether this is a new workflow or continuation
/// - Reads after command execution
/// - Calls to set correlation IDs on all events
///
///
public abstract class Workflow
{
///
/// Unique identifier for this workflow instance.
/// Set by the framework when the workflow is started or continued.
/// This ID becomes the correlation ID for all events emitted by this workflow.
///
///
/// Framework Use: This property is set by the framework and should not be modified by user code.
///
public string Id { get; set; } = string.Empty;
///
/// Indicates whether this is a new workflow instance (true) or a continuation of an existing workflow (false).
/// Set by the framework based on whether the workflow was started or continued.
///
///
/// This can be useful for workflow logic that should only run once (e.g., validation on start).
/// Framework Use: This property is set by the framework and should not be modified by user code.
///
public bool IsNew { get; set; }
///
/// Internal collection of events that have been emitted but not yet persisted.
/// The framework reads this after command execution to emit events.
///
private readonly List _pendingEvents = new();
///
/// Gets the pending events that have been emitted within this workflow.
/// Used by the framework to retrieve events after command execution.
///
///
/// Framework Use Only: This property is for framework use and should not be accessed by user code.
///
public IReadOnlyList PendingEvents => _pendingEvents.AsReadOnly();
///
/// Emits an event within this workflow.
/// The event will be assigned this workflow's ID as its correlation ID by the framework.
///
/// The type of event to emit. Must implement .
/// The event to emit.
/// Thrown if is null.
///
///
/// This method is protected so only derived workflow classes can emit events.
/// Events are collected and will be persisted by the framework after the command handler completes.
///
///
/// Usage Example:
///
/// protected void EmitInvited(UserInvitedEvent e) => Emit(e);
///
///
///
protected void Emit(TEvent @event) where TEvent : ICorrelatedEvent
{
if (@event == null)
throw new ArgumentNullException(nameof(@event));
_pendingEvents.Add(@event);
}
///
/// Assigns this workflow's ID as the correlation ID to all pending events.
/// Called by the framework before events are persisted.
///
/// Thrown if workflow ID is not set.
///
/// Framework Use Only: This method is for framework use and should not be called by user code.
///
public void AssignCorrelationIds()
{
if (string.IsNullOrWhiteSpace(Id))
throw new InvalidOperationException("Workflow ID must be set before assigning correlation IDs.");
foreach (var @event in _pendingEvents)
{
@event.CorrelationId = Id;
}
}
///
/// Clears all pending events.
/// Called by the framework after events have been persisted.
///
///
/// Framework Use Only: This method is for framework use and should not be called by user code.
///
public void ClearPendingEvents()
{
_pendingEvents.Clear();
}
///
/// Gets the number of events that have been emitted within this workflow.
/// Useful for testing and diagnostics.
///
///
/// Framework Use Only: This property is for framework use and diagnostics.
///
public int PendingEventCount => _pendingEvents.Count;
}