using Data.SeaHavenIndustries; using Data.SeaHavenIndustries.Enums; using SeaHaven.DataServices.Interfaces; using SeaHaven.Services.DTOs; using SeaHaven.Services.Exceptions; using SeaHaven.Services.Helpers; using SeaHaven.Services.Interfaces; namespace SeaHaven.Services.Implementation { public class WorkOrderMediaService : IWorkOrderMediaService { private readonly IWorkOrderMediaDataService _mediaData; private readonly IWorkOrderDetailDataService _detailData; public WorkOrderMediaService(IWorkOrderMediaDataService mediaData, IWorkOrderDetailDataService detailData) { _mediaData = mediaData; _detailData = detailData; } public async Task?> GetMediaAsync(int workOrderId) { if (!await _detailData.ExistsAsync(workOrderId)) return null; var workOrder = await _detailData.GetWorkOrderForMediaAsync(workOrderId); if (workOrder == null) return null; var attachments = await _detailData.GetAttachmentsAsync(workOrderId); return WorkOrderMediaProjection.ProjectAll(workOrder, attachments); } public async Task AddMediaAsync( int workOrderId, WorkOrderMediaCategory category, string fileUrl, string? actorId) { if (!await _detailData.ExistsAsync(workOrderId)) throw new WorkOrderBoardValidationException("NotFound", "Work order not found."); var workOrder = await _mediaData.GetTrackedWorkOrderAsync(workOrderId, CancellationToken.None); if (workOrder == null) throw new WorkOrderBoardValidationException("NotFound", "Work order not found."); if (WorkOrderBoardMutationRules.IsReadOnly(workOrder.LifecycleStatus)) throw new WorkOrderBoardValidationException("ReadOnly", "Work order is read-only in its current status."); if (category == WorkOrderMediaCategory.Completion) { throw new WorkOrderBoardValidationException( "UseCompletionDocEndpoint", "Completion documents must be uploaded via POST /api/workorders/{id}/completion-doc."); } if (category == WorkOrderMediaCategory.Before) { workOrder.BeforPhotoAttachment = fileUrl; await _mediaData.SaveAsync(CancellationToken.None); return new WorkOrderMediaFileDto { Id = -1, Category = category, Url = fileUrl, IsLegacy = true }; } if (category == WorkOrderMediaCategory.After) { workOrder.AfterPhotoAttachment = fileUrl; await _mediaData.SaveAsync(CancellationToken.None); return new WorkOrderMediaFileDto { Id = -2, Category = category, Url = fileUrl, IsLegacy = true }; } var attachment = new WorkOrderAttachments { WorkorderId = workOrderId, Attachments = fileUrl, Category = category, CreatedDate = DateTime.UtcNow, createdby = actorId }; _mediaData.TrackAttachment(attachment); await _mediaData.SaveAsync(CancellationToken.None); return new WorkOrderMediaFileDto { Id = attachment.Id, Category = category, Url = fileUrl, UploadedAt = attachment.CreatedDate?.ToUniversalTime().ToString("o"), IsLegacy = false }; } public async Task DeleteMediaAsync(int workOrderId, int mediaId, string? actorId) { if (mediaId <= 0) throw new WorkOrderBoardValidationException("InvalidMedia", "Legacy media cannot be deleted via this endpoint."); if (!await _detailData.ExistsAsync(workOrderId)) throw new WorkOrderBoardValidationException("NotFound", "Work order not found."); var workOrder = await _mediaData.GetTrackedWorkOrderAsync(workOrderId, CancellationToken.None); if (workOrder == null) throw new WorkOrderBoardValidationException("NotFound", "Work order not found."); if (WorkOrderBoardMutationRules.IsReadOnly(workOrder.LifecycleStatus)) throw new WorkOrderBoardValidationException("ReadOnly", "Work order is read-only in its current status."); var attachment = await _mediaData.GetTrackedAttachmentAsync(mediaId, workOrderId, CancellationToken.None); if (attachment == null) throw new WorkOrderBoardValidationException("NotFound", "Media not found."); attachment.IsDeleted = true; attachment.DeletionTime = DateTime.UtcNow; attachment.DeleterUserId = actorId; await _mediaData.SaveAsync(CancellationToken.None); } } }