namespace SeaHaven.Services.Implementation
{
///
/// Server-owned uplift state machine (SH-101). Canonical states are
/// Pending, Approved, Rejected, ChangesRequested, Withdrawn, Expired.
/// Legacy stored values "Denied" (-> Rejected) and "Cancelled" (-> Withdrawn)
/// are read as their canonical equivalents but never (re)written.
///
public static class UpliftStatus
{
public const string Pending = "Pending";
public const string Approved = "Approved";
public const string Rejected = "Rejected";
public const string ChangesRequested = "ChangesRequested";
public const string Withdrawn = "Withdrawn";
public const string Expired = "Expired";
public const string NoApprovalRequired = "NoApprovalRequired";
public const string Revoked = "Revoked";
// Legacy aliases retained only for reads; never written by new code.
public const string LegacyDenied = "Denied";
public const string LegacyCancelled = "Cancelled";
private static readonly HashSet TerminalStates =
new(StringComparer.Ordinal) { Approved, Rejected, Withdrawn, Expired, Revoked, NoApprovalRequired };
private static readonly HashSet ActiveStates =
new(StringComparer.Ordinal) { Pending, ChangesRequested };
///
/// Maps a stored status to its canonical DTO value. Legacy "Denied" is
/// read as "Rejected" and "Cancelled" as "Withdrawn".
///
public static string ToCanonical(string? status) => status switch
{
LegacyDenied => Rejected,
LegacyCancelled => Withdrawn,
_ => status ?? Pending
};
/// True for terminal (immutable) states, including legacy aliases.
public static bool IsTerminal(string? status) =>
TerminalStates.Contains(ToCanonical(status));
/// True for actionable states that block a new active request.
public static bool IsActive(string? status) =>
ActiveStates.Contains(ToCanonical(status));
///
/// Validates a server-owned transition. Only legal transitions return true.
/// Terminal states (incl. legacy aliases) never transition out.
///
public static bool CanTransition(string? from, string to)
{
var canonicalFrom = ToCanonical(from);
if (TerminalStates.Contains(canonicalFrom))
return false;
return canonicalFrom switch
{
Pending => to is Approved or Rejected or ChangesRequested or Withdrawn or Expired,
ChangesRequested => to is Pending or Withdrawn or Expired,
_ => false
};
}
/// Snapshot-readable active states, used by data-service filters.
public static IReadOnlyCollection ActiveStatuses => ActiveStates;
}
///
/// Notification pipeline status (separate from the workflow ).
/// A notification failure never destroys an actionable request.
///
public static class UpliftNotificationStatus
{
public const string Pending = "Pending";
public const string Sent = "Sent";
public const string Error = "Error";
}
///
/// Vendor completion document purposes (SH-101). Completion is the legacy/default
/// behavior; UpliftEvidence is immutable supporting evidence for an uplift request.
///
public static class VendorDocumentPurpose
{
public const string Completion = "Completion";
public const string UpliftEvidence = "UpliftEvidence";
public static bool IsValid(string? purpose) =>
purpose is Completion or UpliftEvidence;
}
}