namespace StellaOps.Notifier.Worker.Escalation; /// /// Service for managing on-call schedules. /// public interface IOnCallScheduleService { /// /// Lists all on-call schedules for a tenant. /// Task> ListSchedulesAsync( string tenantId, CancellationToken cancellationToken = default); /// /// Gets a specific on-call schedule. /// Task GetScheduleAsync( string tenantId, string scheduleId, CancellationToken cancellationToken = default); /// /// Creates or updates an on-call schedule. /// Task UpsertScheduleAsync( OnCallSchedule schedule, string? actor, CancellationToken cancellationToken = default); /// /// Deletes an on-call schedule. /// Task DeleteScheduleAsync( string tenantId, string scheduleId, string? actor, CancellationToken cancellationToken = default); /// /// Gets the current on-call user(s) for a schedule. /// Task> GetCurrentOnCallAsync( string tenantId, string scheduleId, DateTimeOffset? atTime = null, CancellationToken cancellationToken = default); /// /// Gets on-call coverage for a time range. /// Task> GetCoverageAsync( string tenantId, string scheduleId, DateTimeOffset from, DateTimeOffset to, CancellationToken cancellationToken = default); /// /// Creates an override for a schedule. /// Task CreateOverrideAsync( string tenantId, string scheduleId, OnCallOverride @override, string? actor, CancellationToken cancellationToken = default); /// /// Deletes an override. /// Task DeleteOverrideAsync( string tenantId, string scheduleId, string overrideId, string? actor, CancellationToken cancellationToken = default); } /// /// An on-call schedule defining rotation coverage. /// public sealed record OnCallSchedule { /// /// Unique schedule ID. /// public required string ScheduleId { get; init; } /// /// Tenant ID. /// public required string TenantId { get; init; } /// /// Display name. /// public required string Name { get; init; } /// /// Description. /// public string? Description { get; init; } /// /// Timezone for the schedule (IANA format). /// public required string Timezone { get; init; } /// /// Whether the schedule is enabled. /// public bool Enabled { get; init; } = true; /// /// Rotation layers (combined to determine on-call). /// public required IReadOnlyList Layers { get; init; } /// /// Active overrides. /// public IReadOnlyList? Overrides { get; init; } /// /// When created. /// public DateTimeOffset CreatedAt { get; init; } /// /// Who created. /// public string? CreatedBy { get; init; } /// /// When last updated. /// public DateTimeOffset UpdatedAt { get; init; } /// /// Who last updated. /// public string? UpdatedBy { get; init; } } /// /// A rotation layer in an on-call schedule. /// public sealed record RotationLayer { /// /// Layer name. /// public required string Name { get; init; } /// /// Layer priority (lower = higher priority for conflicts). /// public int Priority { get; init; } = 100; /// /// Users in this rotation. /// public required IReadOnlyList Users { get; init; } /// /// Rotation type. /// public required RotationType Type { get; init; } /// /// Handoff time (when rotations change). /// public required TimeOnly HandoffTime { get; init; } /// /// Rotation interval. /// public required TimeSpan RotationInterval { get; init; } /// /// When this layer's rotation starts. /// public required DateTimeOffset RotationStart { get; init; } /// /// Days/times this layer is active (null = always). /// public IReadOnlyList? Restrictions { get; init; } /// /// Whether this layer is enabled. /// public bool Enabled { get; init; } = true; } /// /// Type of rotation. /// public enum RotationType { /// Daily rotation. Daily, /// Weekly rotation. Weekly, /// Custom interval rotation. Custom } /// /// A restriction on when a rotation layer is active. /// public sealed record ScheduleRestriction { /// /// Type of restriction. /// public required RestrictionType Type { get; init; } /// /// Days of week (0=Sunday, 6=Saturday) for weekly restrictions. /// public IReadOnlyList? DaysOfWeek { get; init; } /// /// Start time for the restriction. /// public TimeOnly? StartTime { get; init; } /// /// End time for the restriction. /// public TimeOnly? EndTime { get; init; } } /// /// Type of schedule restriction. /// public enum RestrictionType { /// Restrict to specific days of week. DaysOfWeek, /// Restrict to specific time range. TimeOfDay, /// Restrict to specific days and times. DaysAndTime } /// /// A user in an on-call schedule. /// public sealed record OnCallUser { /// /// User ID. /// public required string UserId { get; init; } /// /// Display name. /// public required string Name { get; init; } /// /// Email address. /// public string? Email { get; init; } /// /// Phone number. /// public string? Phone { get; init; } /// /// Preferred notification channel. /// public string? PreferredChannelId { get; init; } /// /// Position in rotation order. /// public int Order { get; init; } } /// /// An override for an on-call schedule. /// public sealed record OnCallOverride { /// /// Override ID. /// public required string OverrideId { get; init; } /// /// User taking over on-call. /// public required OnCallUser User { get; init; } /// /// When the override starts. /// public required DateTimeOffset StartsAt { get; init; } /// /// When the override ends. /// public required DateTimeOffset EndsAt { get; init; } /// /// Reason for the override. /// public string? Reason { get; init; } /// /// Who created the override. /// public string? CreatedBy { get; init; } /// /// When created. /// public DateTimeOffset CreatedAt { get; init; } } /// /// A computed on-call shift. /// public sealed record OnCallShift { /// /// User on call. /// public required OnCallUser User { get; init; } /// /// When the shift starts. /// public required DateTimeOffset StartsAt { get; init; } /// /// When the shift ends. /// public required DateTimeOffset EndsAt { get; init; } /// /// Layer this shift comes from. /// public required string LayerName { get; init; } /// /// Whether this is from an override. /// public bool IsOverride { get; init; } }