namespace SeaHaven.Services.Helpers
{
public enum SlaState
{
OnTrack,
/// At least half of the response window has elapsed.
AtRisk,
/// The whole response window has elapsed: the response deadline was missed.
Breached
}
/// Where one work order stands against its SEV response window.
public sealed record SlaClock(int Severity, DateTime StartedAt, DateTime DeadlineAt, int PercentElapsed, SlaState State);
///
/// Respond deadlines from the client's SEV table, the same values the severity badge tooltip shows.
/// They apply to Reactive and Emergency work orders with a severity, and the clock starts when the
/// work order is created, never at its scheduled date. The table's "next day if after hours" note
/// is not modelled: the clock always counts the plain hours.
///
public static class SlaResponseWindows
{
public static readonly IReadOnlyDictionary Respond = new Dictionary
{
[1] = TimeSpan.FromHours(2),
[2] = TimeSpan.FromHours(4),
[3] = TimeSpan.FromHours(8),
[4] = TimeSpan.FromHours(24),
[5] = TimeSpan.FromHours(72)
};
/// The latest creation time at which a work order of this level has used half its window.
public static DateTime AtRiskCreatedAtOrBefore(int severity, DateTime now) => now - Respond[severity] / 2;
/// The latest creation time at which a work order of this level has missed its deadline.
public static DateTime BreachedCreatedAtOrBefore(int severity, DateTime now) => now - Respond[severity];
/// Null when the severity is not a SEV 1–5 level.
public static SlaClock? Evaluate(string? severity, DateTime createdAt, DateTime now)
{
var level = WorkOrderSeverityRules.ParseLevel(severity);
if (level is not int sev)
return null;
var window = Respond[sev];
var startedAt = DateTime.SpecifyKind(createdAt, DateTimeKind.Utc);
var elapsed = now - startedAt;
var state = elapsed >= window
? SlaState.Breached
: elapsed >= window / 2 ? SlaState.AtRisk : SlaState.OnTrack;
var percent = (int)Math.Floor(Math.Max(elapsed.Ticks, 0) * 100d / window.Ticks);
return new SlaClock(sev, startedAt, startedAt + window, percent, state);
}
}
}