using Data.SeaHavenIndustries; using Data.SeaHavenIndustries.Enums; using SeaHaven.Services.Exceptions; namespace SeaHaven.Services.Helpers { public static class WorkOrderBoardFieldMutations { public static List ApplyAssignTo(WorkOrder workOrder, string? value, string auditField = "AssignTo") { var normalized = string.IsNullOrWhiteSpace(value) ? null : value.Trim(); var old = workOrder.AssignTo; var changes = new List(); if (old != normalized) { workOrder.AssignTo = normalized; changes.Add(BoardFieldChange.AssignmentChanged(auditField, old, normalized)); } changes.AddRange(ApplyAutoScheduleSideEffects(workOrder)); return changes; } public static List ApplyScheduledDate( WorkOrder workOrder, DateTime? parsed, string auditField = "ScheduledDate", bool incrementRescheduleOnChange = true) { var previousDate = workOrder.ScheduledDate; var changes = new List(); var oldFormatted = FormatDate(previousDate); var newFormatted = FormatDate(parsed); if (oldFormatted == newFormatted) return changes; workOrder.ScheduledDate = parsed; changes.Add(BoardFieldChange.Changed(auditField, oldFormatted, newFormatted)); if (!workOrder.OriginalDate.HasValue && parsed.HasValue) { workOrder.OriginalDate = DateOnly.FromDateTime(parsed.Value); var originalWeek = WorkOrderBoardMutationRules.ComputeOriginalWeek(parsed); if (originalWeek.HasValue) workOrder.OriginalWeek = originalWeek; } if (incrementRescheduleOnChange && WorkOrderBoardMutationRules.IsReschedule(previousDate, parsed)) { var oldCount = workOrder.RescheduleCount.ToString(); workOrder.RescheduleCount++; changes.Add(BoardFieldChange.Changed( WorkOrderBoardFieldNames.RescheduleCount, oldCount, workOrder.RescheduleCount.ToString())); } changes.AddRange(ApplyAutoScheduleSideEffects(workOrder)); return changes; } public static List ApplyAutoScheduleSideEffects(WorkOrder workOrder) { var changes = new List(); if (!WorkOrderBoardMutationRules.ShouldAutoSchedule( workOrder.LifecycleStatus, workOrder.ScheduledDate, workOrder.AssignTo, workOrder.ScheduleWeekOnly)) return changes; var old = workOrder.LifecycleStatus?.ToString(); workOrder.LifecycleStatus = LifecycleStatus.Scheduled; workOrder.Status = WorkOrderDerivedFields.GetLifecycleStatusLabel(LifecycleStatus.Scheduled); changes.Add(BoardFieldChange.StatusChanged("LifecycleStatus", old, LifecycleStatus.Scheduled.ToString())); return changes; } public static BoardFieldChange ApplyApptTime(WorkOrder workOrder, Dispatch dispatch, string? value) { if (!WorkOrderBoardApptTimeParser.TryParse(value, out var start, out var end, out var error)) throw new WorkOrderBoardValidationException("InvalidValue", error ?? "Invalid apptTime."); var apptDate = dispatch.ScheduledDate ?? workOrder.ScheduledDate; var oldStart = FormatDateTime(workOrder.ScheduledStart); var oldEnd = FormatDateTime(workOrder.ScheduledEnd); workOrder.ScheduledStart = WorkOrderBoardApptTimeParser.ToScheduledInstant(apptDate, start); workOrder.ScheduledEnd = WorkOrderBoardApptTimeParser.ToScheduledInstant(apptDate, end); var newStart = FormatDateTime(workOrder.ScheduledStart); var newEnd = FormatDateTime(workOrder.ScheduledEnd); var oldCombined = FormatApptRange(oldStart, oldEnd); var newCombined = FormatApptRange(newStart, newEnd); if (oldCombined == newCombined) return BoardFieldChange.Unchanged("ApptTime", dispatch.Id); return BoardFieldChange.Changed("ApptTime", oldCombined, newCombined, dispatch.Id); } public static void SetInitialLifecycleStatus(WorkOrder workOrder) { workOrder.LifecycleStatus = LifecycleStatus.Incomplete; workOrder.Status = WorkOrderDerivedFields.GetLifecycleStatusLabel(LifecycleStatus.Incomplete); } private static string? FormatDate(DateTime? value) => value?.ToString("yyyy-MM-dd"); private static string? FormatDateTime(DateTime? value) => value?.ToString("O"); private static string? FormatApptRange(string? start, string? end) => start == null && end == null ? null : $"{start}|{end}"; public sealed record BoardFieldChange( string FieldName, string? OldValue, string? NewValue, AuditActionType Action, int? DispatchId, bool HasChanged) { public static BoardFieldChange Unchanged(string fieldName, int? dispatchId = null) => new(fieldName, null, null, AuditActionType.FieldChanged, dispatchId, false); public static BoardFieldChange Changed(string fieldName, string? oldValue, string? newValue, int? dispatchId = null) => new(fieldName, oldValue, newValue, AuditActionType.FieldChanged, dispatchId, true); public static BoardFieldChange StatusChanged(string fieldName, string? oldValue, string? newValue) => new(fieldName, oldValue, newValue, AuditActionType.StatusChanged, null, true); public static BoardFieldChange AssignmentChanged(string fieldName, string? oldValue, string? newValue) => new(fieldName, oldValue, newValue, AuditActionType.AssignmentChanged, null, true); } } }