using Data.SeaHavenIndustries; using Data.SeaHavenIndustries.Enums; using SeaHaven.Services.Constants; using SeaHaven.Services.Interfaces; namespace SeaHaven.Services.Implementation { public class SyncFieldMergePolicy : ISyncFieldMergePolicy { private static readonly HashSet ShocOwnedFieldSet = new(StringComparer.OrdinalIgnoreCase) { WorkOrderFieldNames.LifecycleStatus, WorkOrderFieldNames.AssignTo, WorkOrderFieldNames.ScheduledDate, WorkOrderFieldNames.ScheduledStart, WorkOrderFieldNames.ScheduledEnd, WorkOrderFieldNames.TargetWeek, WorkOrderFieldNames.ScheduleWeekOnly, WorkOrderFieldNames.DueDate, WorkOrderFieldNames.Description, WorkOrderFieldNames.WorkerOrderTitle, WorkOrderFieldNames.SiteCode, WorkOrderFieldNames.Building, WorkOrderFieldNames.LocationId, WorkOrderFieldNames.WorkOrderType, WorkOrderFieldNames.Service, WorkOrderFieldNames.DocStatus, WorkOrderFieldNames.Trade, WorkOrderFieldNames.Problem, WorkOrderFieldNames.Priority, WorkOrderFieldNames.InternalWONumber, WorkOrderFieldNames.Status, }; private readonly IWorkOrderFieldLockService _fieldLocks; public SyncFieldMergePolicy(IWorkOrderFieldLockService fieldLocks) { _fieldLocks = fieldLocks; } public IReadOnlySet ShocOwnedFields => ShocOwnedFieldSet; public async Task CanSyncOverwriteAsync(int workOrderId, string fieldName) => !await _fieldLocks.IsLockedAsync(workOrderId, fieldName); public async Task TryApplyAsync(WorkOrderSyncContext context, string fieldName, object? incomingValue) { if (incomingValue == null) return false; if (await context.FieldLocks.IsLockedAsync(context.WorkOrder.Id, fieldName)) { var current = GetFieldValue(context.WorkOrder, fieldName); await context.Audit.LogSyncRejectedAsync( context.WorkOrder.Id, fieldName, current, incomingValue.ToString()); return false; } return TryApplyField(context.WorkOrder, fieldName, incomingValue); } private static string? GetFieldValue(WorkOrder wo, string fieldName) => fieldName switch { WorkOrderFieldNames.Description => wo.Description, WorkOrderFieldNames.WorkerOrderTitle => wo.WorkerOrderTitle, WorkOrderFieldNames.Status => wo.Status, WorkOrderFieldNames.DueDate => wo.DueDate?.ToString("O"), WorkOrderFieldNames.ScheduledDate => wo.ScheduledDate?.ToString("O"), WorkOrderFieldNames.ScheduledStart => wo.ScheduledStart?.ToString("O"), WorkOrderFieldNames.ScheduledEnd => wo.ScheduledEnd?.ToString("O"), WorkOrderFieldNames.TargetWeek => wo.TargetWeek?.ToString("O"), WorkOrderFieldNames.ScheduleWeekOnly => wo.ScheduleWeekOnly?.ToString(), WorkOrderFieldNames.SiteCode => wo.SiteCode, WorkOrderFieldNames.Building => wo.Building, WorkOrderFieldNames.Priority => wo.Priority, WorkOrderFieldNames.AssignTo => wo.AssignTo, WorkOrderFieldNames.LocationId => wo.LocationId?.ToString(), WorkOrderFieldNames.LifecycleStatus => wo.LifecycleStatus?.ToString(), WorkOrderFieldNames.WorkOrderType => wo.WorkOrderType?.ToString(), WorkOrderFieldNames.Service => wo.Service, WorkOrderFieldNames.DocStatus => wo.DocStatus?.ToString(), WorkOrderFieldNames.Trade => wo.Trade, WorkOrderFieldNames.Problem => wo.Problem, WorkOrderFieldNames.InternalWONumber => wo.InternalWONumber, _ => null }; private static bool TryApplyField(WorkOrder wo, string fieldName, object incomingValue) { switch (fieldName) { case WorkOrderFieldNames.Description: wo.Description = incomingValue.ToString(); return true; case WorkOrderFieldNames.WorkerOrderTitle: wo.WorkerOrderTitle = incomingValue.ToString(); return true; case WorkOrderFieldNames.Status: wo.Status = incomingValue.ToString(); return true; case WorkOrderFieldNames.DueDate when TryParseDateTime(incomingValue, out var dueDate): wo.DueDate = dueDate; return true; case WorkOrderFieldNames.ScheduledDate when TryParseDateTime(incomingValue, out var scheduledDate): wo.ScheduledDate = scheduledDate; return true; case WorkOrderFieldNames.ScheduledStart when TryParseDateTime(incomingValue, out var scheduledStart): wo.ScheduledStart = scheduledStart; return true; case WorkOrderFieldNames.ScheduledEnd when TryParseDateTime(incomingValue, out var scheduledEnd): wo.ScheduledEnd = scheduledEnd; return true; case WorkOrderFieldNames.TargetWeek when TryParseDateOnly(incomingValue, out var targetWeek): wo.TargetWeek = targetWeek; return true; case WorkOrderFieldNames.ScheduleWeekOnly when TryParseBool(incomingValue, out var scheduleWeekOnly): wo.ScheduleWeekOnly = scheduleWeekOnly; return true; case WorkOrderFieldNames.SiteCode: wo.SiteCode = incomingValue.ToString(); return true; case WorkOrderFieldNames.Building: wo.Building = incomingValue.ToString(); return true; case WorkOrderFieldNames.Priority: wo.Priority = incomingValue.ToString(); return true; case WorkOrderFieldNames.AssignTo: wo.AssignTo = incomingValue.ToString(); return true; case WorkOrderFieldNames.LocationId when TryParseInt(incomingValue, out var locationId): wo.LocationId = locationId; return true; case WorkOrderFieldNames.LifecycleStatus when TryParseEnum(incomingValue, out LifecycleStatus lifecycleStatus): wo.LifecycleStatus = lifecycleStatus; return true; case WorkOrderFieldNames.WorkOrderType when TryParseEnum(incomingValue, out WorkOrderType workOrderType): wo.WorkOrderType = workOrderType; return true; case WorkOrderFieldNames.Service: wo.Service = incomingValue.ToString(); return true; case WorkOrderFieldNames.DocStatus when TryParseEnum(incomingValue, out DocStatus docStatus): wo.DocStatus = docStatus; return true; case WorkOrderFieldNames.Trade: wo.Trade = incomingValue.ToString(); return true; case WorkOrderFieldNames.Problem: wo.Problem = incomingValue.ToString(); return true; case WorkOrderFieldNames.InternalWONumber: wo.InternalWONumber = incomingValue.ToString(); return true; default: return false; } } private static bool TryParseDateTime(object value, out DateTime result) { if (value is DateTime dt) { result = dt; return true; } return DateTime.TryParse(value.ToString(), out result); } private static bool TryParseDateOnly(object value, out DateOnly result) { if (value is DateOnly dateOnly) { result = dateOnly; return true; } if (value is DateTime dt) { result = DateOnly.FromDateTime(dt); return true; } return DateOnly.TryParse(value.ToString(), out result); } private static bool TryParseBool(object value, out bool result) { if (value is bool b) { result = b; return true; } return bool.TryParse(value.ToString(), out result); } private static bool TryParseInt(object value, out int result) { if (value is int i) { result = i; return true; } return int.TryParse(value.ToString(), out result); } private static bool TryParseEnum(object value, out TEnum result) where TEnum : struct, Enum { if (value is TEnum enumValue) { result = enumValue; return true; } return Enum.TryParse(value.ToString(), true, out result); } } }