using Data.SeaHavenIndustries.Enums; namespace SeaHaven.Services.Helpers { public static class WorkOrderBoardMutationRules { private static readonly HashSet ReadOnlyStatuses = LifecycleStatusSets.Terminal.Cast().ToHashSet(); public static bool IsReadOnly(LifecycleStatus? status) => ReadOnlyStatuses.Contains(status); public static bool IsFullyLocked(LifecycleStatus? status) => status == LifecycleStatus.Canceled; public static bool IsCoreLocked(LifecycleStatus? status) => status == LifecycleStatus.Completed; public static bool CanPatchBoardField(LifecycleStatus? status, string field) { if (IsFullyLocked(status)) return false; if (!IsCoreLocked(status)) return true; return field.Equals(WorkOrderBoardFieldNames.FlagColor, StringComparison.OrdinalIgnoreCase); } public static bool CanMutateComments(LifecycleStatus? status) => !IsFullyLocked(status); public static bool CanMutateExtraMedia(LifecycleStatus? status, WorkOrderMediaCategory? category) { if (IsFullyLocked(status)) return false; if (!IsCoreLocked(status)) return true; return IsExtraDocsFamily(category); } public static bool IsExtraDocsFamily(WorkOrderMediaCategory? category) { var resolved = category ?? WorkOrderMediaCategory.Extra; return resolved == WorkOrderMediaCategory.Extra || resolved == WorkOrderMediaCategory.Aveta; } public static bool ShouldBlockStatusChangeWhenPastDue(string field, bool isPastDue) => field.Equals(WorkOrderBoardFieldNames.LifecycleStatus, StringComparison.OrdinalIgnoreCase) && isPastDue; /// /// Concrete Schedule On: a calendar day is set and the row is not week-only. /// public static bool HasConcreteSchedule(DateTime? scheduledDate, bool? scheduleWeekOnly) => scheduledDate.HasValue && scheduleWeekOnly != true; /// /// Incomplete or Pending + concrete scheduled date → Scheduled. Assignee is not required. /// Week-only targets do not auto-schedule (same as ). /// public static bool ShouldAutoSchedule( LifecycleStatus? status, DateTime? scheduledDate, string? assignTo, bool? scheduleWeekOnly = null) { _ = assignTo; return ShouldPromoteToScheduled(status, scheduledDate, scheduleWeekOnly); } public static bool ShouldPromoteToScheduled( LifecycleStatus? status, DateTime? scheduledDate, bool? scheduleWeekOnly = null) => (status == LifecycleStatus.Incomplete || status == LifecycleStatus.Pending) && HasConcreteSchedule(scheduledDate, scheduleWeekOnly); /// /// Scheduled without a concrete date (cleared or week-only) → Pending. Later statuses are ignored. /// public static bool ShouldDemoteFromScheduled( LifecycleStatus? status, DateTime? scheduledDate, bool? scheduleWeekOnly = null) => status == LifecycleStatus.Scheduled && !HasConcreteSchedule(scheduledDate, scheduleWeekOnly); public static bool IsReschedule(DateTime? previousDate, DateTime? newDate) => previousDate.HasValue && newDate.HasValue && previousDate.Value.Date != newDate.Value.Date; public static DateOnly? ComputeOriginalWeek(DateTime? scheduledDate) => scheduledDate.HasValue ? DateOnly.FromDateTime(GetWeekStartMonday(scheduledDate.Value)) : null; public static DateTime GetWeekStartMonday(DateTime date) { var diff = (7 + (date.DayOfWeek - DayOfWeek.Monday)) % 7; return date.Date.AddDays(-diff); } } }