namespace KBX.Shared.Workflow; public sealed record WorkflowTransition(string Id, IReadOnlySet From, string To, string? Permission = null, bool ReasonRequired = false); public static class WorkflowGuard { public static WorkflowTransition RequireTransition(IEnumerable transitions, string transitionId, string currentState, string? reason = null) { var transition = transitions.FirstOrDefault(x => string.Equals(x.Id, transitionId, StringComparison.OrdinalIgnoreCase)) ?? throw new InvalidOperationException($"Unknown transition: {transitionId}"); if (!transition.From.Contains(currentState)) throw new InvalidOperationException($"Transition {transitionId} is not allowed from {currentState}."); if (transition.ReasonRequired && string.IsNullOrWhiteSpace(reason)) throw new InvalidOperationException("A reason is required for this transition."); return transition; } }