using Nuuru.Server.DTOs.Booru; using Nuuru.Server.DTOs.Reaction; using Nuuru.Server.Models; using Nuuru.Server.Models.Booru; using Nuuru.Server.Services; namespace Nuuru.Server.Extensions { /// /// Extension methods for mapping between Booru entities and DTOs /// public static class BooruMappingExtensions { /// /// Maps a Tag entity to TagDto /// public static TagDto ToDto(this Tag tag) { if (tag == null) return null; return new TagDto { Id = tag.Id, Name = tag.Name, Category = tag.Category?.Slug, CategoryName = tag.Category?.Name, CategoryColor = tag.Category?.ColorHex, PostCount = tag.PostCount, CreatedAt = tag.CreatedAt }; } /// /// Maps an ApplicationUser to UploaderDto /// public static UploaderDto ToUploaderDto(this Models.ApplicationUser user, UserDisplayInfo? displayInfo = null) { if (user == null) return null!; return new UploaderDto { Id = user.Id, UserName = user.UserName ?? string.Empty, AvatarUrl = ApplicationUser.GetAvatarUrl(user.UserName, user.AvatarStorageIdentifier), BackgroundImageUrl = ApplicationUser.GetBackgroundImageUrl(user.UserName, user.BackgroundImageStorageIdentifier), RoleColor = displayInfo?.RoleColor, Status = user?.Status, Badges = displayInfo?.Badges ?? [], ActiveBanZones = displayInfo?.ActiveBanZones?.Select(z => z.ToString()).ToList() ?? [], ActiveBans = displayInfo?.ActiveBans ?? [], ForcedDisplayName = (displayInfo?.BointsEnabled ?? false) ? user.ActiveForcedDisplayName : null, ClanId = displayInfo?.ClanId, ClanTag = displayInfo?.ClanTag, ClanColor = displayInfo?.ClanColor, ClanBadgeUrl = displayInfo?.ClanBadgeUrl }; } /// /// Maps a Post entity to PostDto /// public static PostDto ToDto( this Post post, Dictionary? displayInfoMap = null, ReactionDTO? reactions = null) { if (post == null) return null!; UserDisplayInfo? uploaderDisplayInfo = null; if (post.Uploader != null && displayInfoMap != null) { displayInfoMap.TryGetValue(post.Uploader.Id, out uploaderDisplayInfo); } UserDisplayInfo? approverDisplayInfo = null; if (post.ApprovedBy != null && displayInfoMap != null) { displayInfoMap.TryGetValue(post.ApprovedBy.Id, out approverDisplayInfo); } return new PostDto { Id = post.Id, StorageIdentifier = post.StorageIdentifier, ImageHash = post.ImageHash, MimeType = post.MimeType, FileSize = post.FileSize, OriginalFileName = post.OriginalFileName, Source = post.Source, Description = post.Description, Width = post.Width, Height = post.Height, DurationSeconds = post.DurationSeconds, ThumbnailPath = post.ThumbnailPath, UploadedAt = post.UploadedAt, Rating = post.Rating.ToString().ToLowerInvariant(), Category = post.Category.ToString().ToLowerInvariant(), IsApproved = post.IsApproved, ApprovedBy = post.ApprovedBy?.ToUploaderDto(approverDisplayInfo), ApprovedAt = post.ApprovedAt, CommentsLocked = post.CommentsLocked, IsTrashed = post.IsTrashed, TrashedAt = post.TrashedAt, TrashReason = post.TrashReason, IsFeatured = post.IsFeatured, FeaturedAt = post.FeaturedAt, HasGoldenFrame = (uploaderDisplayInfo?.BointsEnabled ?? false) && post.HasGoldenFrame, Uploader = post.Uploader?.ToUploaderDto(uploaderDisplayInfo), Tags = post.PostTags?.Select(pt => pt.Tag.ToDto()).ToList() ?? new List(), Reactions = reactions }; } /// /// Maps a collection of Tags to TagDtos /// public static List ToDto(this IEnumerable tags) { return tags?.Select(t => t.ToDto()).ToList() ?? new List(); } /// /// Maps a collection of Posts to PostDtos /// public static List ToDto( this IEnumerable posts, Dictionary? displayInfoMap = null, Dictionary? reactionsMap = null) { return posts?.Select(p => { ReactionDTO? reactions = null; reactionsMap?.TryGetValue(p.Id, out reactions); return p.ToDto(displayInfoMap, reactions); }).ToList() ?? new List(); } /// /// Maps a Comment entity to CommentDto /// /// The comment to map /// If provided, includes raw content only if this user is the author /// Optional display info map for badge/color lookup /// Optional reactions map for reaction data public static CommentDto ToDto( this Comment comment, Guid? requestingUserId = null, Dictionary? displayInfoMap = null, Dictionary? reactionsMap = null) { if (comment == null) return null!; UserDisplayInfo? authorDisplayInfo = null; if (comment.User != null && displayInfoMap != null) { displayInfoMap.TryGetValue(comment.User.Id, out authorDisplayInfo); } ReactionDTO? reactions = null; reactionsMap?.TryGetValue(comment.Id, out reactions); return new CommentDto { Id = comment.Id, ContentHtml = comment.ContentHtml, // Only include raw content for the author (so they can edit) ContentRaw = requestingUserId.HasValue && comment.UserId == requestingUserId.Value ? comment.ContentRaw : null, CreatedAt = comment.CreatedAt, EditedAt = comment.EditedAt, Author = comment.User?.ToUploaderDto(authorDisplayInfo), PostId = comment.PostId, Reactions = reactions }; } /// /// Maps a collection of Comments to CommentDtos /// public static List ToDto( this IEnumerable comments, Guid? requestingUserId = null, Dictionary? displayInfoMap = null, Dictionary? reactionsMap = null) { return comments?.Select(c => c.ToDto(requestingUserId, displayInfoMap, reactionsMap)).ToList() ?? new List(); } /// /// Maps a TagHistory entity to TagHistoryDto /// public static TagHistoryDto ToDto( this TagHistory history, Dictionary? displayInfoMap = null, bool includeSuppressedInfo = false) { if (history == null) return null!; UserDisplayInfo? userDisplayInfo = null; if (history.User != null && displayInfoMap != null) { displayInfoMap.TryGetValue(history.User.Id, out userDisplayInfo); } UserDisplayInfo? suppressedByDisplayInfo = null; if (history.SuppressedBy != null && displayInfoMap != null) { displayInfoMap.TryGetValue(history.SuppressedBy.Id, out suppressedByDisplayInfo); } return new TagHistoryDto { Id = history.Id, PostId = history.PostId, User = history.User?.ToUploaderDto(userDisplayInfo)!, Tags = history.Tags.Split(' ', StringSplitOptions.RemoveEmptyEntries), DateSet = history.DateSet, IsSuppressed = history.SuppressedAt.HasValue, SuppressedAt = includeSuppressedInfo ? history.SuppressedAt : null, SuppressedBy = includeSuppressedInfo && history.SuppressedBy != null ? history.SuppressedBy.ToUploaderDto(suppressedByDisplayInfo) : null, SuppressionReason = includeSuppressedInfo ? history.SuppressionReason : null }; } /// /// Maps a SourceHistory entity to SourceHistoryDto /// public static SourceHistoryDto ToDto( this SourceHistory history, Dictionary? displayInfoMap = null, bool includeSuppressedInfo = false) { if (history == null) return null!; UserDisplayInfo? userDisplayInfo = null; if (history.User != null && displayInfoMap != null) { displayInfoMap.TryGetValue(history.User.Id, out userDisplayInfo); } UserDisplayInfo? suppressedByDisplayInfo = null; if (history.SuppressedBy != null && displayInfoMap != null) { displayInfoMap.TryGetValue(history.SuppressedBy.Id, out suppressedByDisplayInfo); } return new SourceHistoryDto { Id = history.Id, PostId = history.PostId, User = history.User?.ToUploaderDto(userDisplayInfo)!, Source = history.Source, DateSet = history.DateSet, IsSuppressed = history.SuppressedAt.HasValue, SuppressedAt = includeSuppressedInfo ? history.SuppressedAt : null, SuppressedBy = includeSuppressedInfo && history.SuppressedBy != null ? history.SuppressedBy.ToUploaderDto(suppressedByDisplayInfo) : null, SuppressionReason = includeSuppressedInfo ? history.SuppressionReason : null }; } } }