using Nuuru.Server.DTOs.Forum; using Nuuru.Server.DTOs.Reaction; using Nuuru.Server.Models.Forum; using Nuuru.Server.Services; namespace Nuuru.Server.Extensions { /// /// Extension methods for mapping between Forum entities and DTOs /// public static class ForumMappingExtensions { /// /// Maps a ForumCategory entity to ForumCategoryDto /// public static ForumCategoryDto ToDto(this ForumCategory category, int threadCount = 0, int postCount = 0, ForumThread? latestThread = null, Dictionary? displayInfoMap = null) { if (category == null) return null!; return new ForumCategoryDto { Id = category.Id, Slug = category.Slug.StartsWith("clan-") ? "clan" : category.Slug, Name = category.Name, Description = category.Description, DisplayOrder = category.DisplayOrder, Color = category.Color, ThreadCount = threadCount, PostCount = postCount, LatestThread = latestThread?.ToSummaryDto(displayInfoMap) }; } /// /// Maps a ForumCategory entity to ForumCategorySummaryDto /// public static ForumCategorySummaryDto ToSummaryDto(this ForumCategory category) { if (category == null) return null!; return new ForumCategorySummaryDto { Id = category.Id, Slug = category.Slug.StartsWith("clan-") ? "clan" : category.Slug, Name = category.Name, Color = category.Color }; } /// /// Maps a ForumThread entity to ForumThreadDto /// public static ForumThreadDto ToDto(this ForumThread thread, bool includeFirstPost = false, Guid? requestingUserId = null, Dictionary? displayInfoMap = null) { if (thread == null) return null!; UserDisplayInfo? authorDisplayInfo = null; if (thread.Author != null && displayInfoMap != null) { displayInfoMap.TryGetValue(thread.Author.Id, out authorDisplayInfo); } UserDisplayInfo? lastPostAuthorDisplayInfo = null; if (thread.LastPost?.Author != null && displayInfoMap != null) { displayInfoMap.TryGetValue(thread.LastPost.Author.Id, out lastPostAuthorDisplayInfo); } var threadBansEnabled = !string.Equals(thread.Category?.Slug, "gen", StringComparison.OrdinalIgnoreCase); var canManageBans = threadBansEnabled && requestingUserId.HasValue && thread.AuthorId == requestingUserId.Value; var currentUserIsBanned = requestingUserId.HasValue && threadBansEnabled && thread.UserBans.Any(b => b.UserId == requestingUserId.Value); return new ForumThreadDto { Id = thread.Id, Title = thread.Title, IsPinned = thread.IsPinned, IsLocked = thread.IsLocked, CurrentUserCanManageBans = canManageBans, CurrentUserIsBanned = currentUserIsBanned, CreatedAt = thread.CreatedAt, LastPostAt = thread.LastPostAt, ReplyCount = thread.ReplyCount, ViewCount = thread.ViewCount, Author = thread.Author?.ToUploaderDto(authorDisplayInfo)!, LastPostId = thread.LastPostId, LastPostAuthor = thread.LastPost?.Author?.ToUploaderDto(lastPostAuthorDisplayInfo), Category = thread.Category?.ToSummaryDto()!, FirstPost = includeFirstPost ? thread.FirstPost?.ToDto(requestingUserId, displayInfoMap) : null, BannedUsers = canManageBans ? thread.UserBans .Where(b => b.User != null) .OrderBy(b => b.User.UserName) .Select(b => { UserDisplayInfo? bannedUserDisplayInfo = null; displayInfoMap?.TryGetValue(b.UserId, out bannedUserDisplayInfo); return new ForumThreadBannedUserDto { User = b.User.ToUploaderDto(bannedUserDisplayInfo), BannedAt = b.CreatedAt }; }) .ToList() : [] }; } /// /// Maps a ForumThread entity to ForumThreadSummaryDto /// public static ForumThreadSummaryDto ToSummaryDto(this ForumThread thread, Dictionary? displayInfoMap = null) { if (thread == null) return null!; UserDisplayInfo? authorDisplayInfo = null; if (thread.Author != null && displayInfoMap != null) { displayInfoMap.TryGetValue(thread.Author.Id, out authorDisplayInfo); } UserDisplayInfo? lastPostAuthorDisplayInfo = null; if (thread.LastPost?.Author != null && displayInfoMap != null) { displayInfoMap.TryGetValue(thread.LastPost.Author.Id, out lastPostAuthorDisplayInfo); } return new ForumThreadSummaryDto { Id = thread.Id, Title = thread.Title, LastPostAt = thread.LastPostAt, ReplyCount = thread.ReplyCount, Author = thread.Author?.ToUploaderDto(authorDisplayInfo)!, LastPostId = thread.LastPostId, LastPostAuthor = thread.LastPost?.Author?.ToUploaderDto(lastPostAuthorDisplayInfo) }; } /// /// Maps a collection of ForumThreads to ForumThreadDtos /// public static List ToDto(this IEnumerable threads, bool includeFirstPost = false, Guid? requestingUserId = null, Dictionary? displayInfoMap = null) { return threads?.Select(t => t.ToDto(includeFirstPost, requestingUserId, displayInfoMap)).ToList() ?? new List(); } /// /// Maps a ForumPost entity to ForumPostDto /// /// The post 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 ForumPostDto ToDto( this ForumPost post, Guid? requestingUserId = null, Dictionary? displayInfoMap = null, Dictionary? reactionsMap = null) { if (post == null) return null!; UserDisplayInfo? authorDisplayInfo = null; if (post.Author != null && displayInfoMap != null) { displayInfoMap.TryGetValue(post.Author.Id, out authorDisplayInfo); } ReactionDTO? reactions = null; reactionsMap?.TryGetValue(post.Id, out reactions); return new ForumPostDto { Id = post.Id, ContentHtml = post.ContentHtml, // Only include raw content for the author (so they can edit) ContentRaw = requestingUserId.HasValue && post.AuthorId == requestingUserId.Value ? post.ContentRaw : null, CreatedAt = post.CreatedAt, EditedAt = post.EditedAt, Author = post.Author?.ToUploaderDto(authorDisplayInfo)!, ThreadId = post.ThreadId, Thread = post.Thread != null ? new ForumPostThreadDto { Id = post.Thread.Id, Title = post.Thread.Title, Category = post.Thread.Category?.ToSummaryDto() } : null, Reactions = reactions }; } /// /// Maps a collection of ForumPosts to ForumPostDtos /// public static List ToDto( this IEnumerable posts, Guid? requestingUserId = null, Dictionary? displayInfoMap = null, Dictionary? reactionsMap = null) { return posts?.Select(p => p.ToDto(requestingUserId, displayInfoMap, reactionsMap)).ToList() ?? new List(); } } }