using MongoDB.Bson; using MongoDB.Bson.Serialization.Attributes; namespace QuestionsApi.Models; public static class AppFeedbackCategory { public const string Bug = "bug"; public const string Usability = "usability"; public const string FeatureRequest = "feature_request"; public const string Account = "account"; public const string Other = "other"; public static readonly HashSet All = [Bug, Usability, FeatureRequest, Account, Other]; } public sealed class AppFeedbackContext { public string Screen { get; set; } = ""; public string SessionType { get; set; } = ""; public string Platform { get; set; } = ""; public int ViewportWidth { get; set; } public int ViewportHeight { get; set; } public string AppVersion { get; set; } = ""; public string BuildRevision { get; set; } = ""; public DateTime? ClientUtc { get; set; } } public sealed class AppFeedback { [BsonId, BsonRepresentation(BsonType.ObjectId)] public string? Id { get; set; } public string UserId { get; set; } = ""; public string Category { get; set; } = ""; public string Details { get; set; } = ""; public AppFeedbackContext Context { get; set; } = new(); public string Status { get; set; } = QuestionReportStatus.Open; public string ReviewedByUserId { get; set; } = ""; public string ReviewerNote { get; set; } = ""; public DateTime? ReviewedUtc { get; set; } public DateTime CreatedUtc { get; set; } = DateTime.UtcNow; public DateTime UpdatedUtc { get; set; } = DateTime.UtcNow; } public sealed class CreateAppFeedbackRequest { public string Category { get; set; } = ""; public string Details { get; set; } = ""; public AppFeedbackContext Context { get; set; } = new(); } public sealed class AppFeedbackListResponse { public List Items { get; set; } = []; public long Total { get; set; } public int Skip { get; set; } public int Limit { get; set; } } public static class AppFeedbackValidator { public const int MinDetailsLength = 10; public const int MaxDetailsLength = 4000; public const int MaxContextLength = 120; public static string? Validate(CreateAppFeedbackRequest request) { var category = (request.Category ?? "").Trim().ToLowerInvariant(); var details = (request.Details ?? "").Trim(); if (!AppFeedbackCategory.All.Contains(category)) return "category is invalid"; if (details.Length is < MinDetailsLength or > MaxDetailsLength) return $"details must be between {MinDetailsLength} and {MaxDetailsLength} characters"; var context = request.Context ?? new AppFeedbackContext(); if (context.ViewportWidth < 0 || context.ViewportHeight < 0) return "viewport dimensions cannot be negative"; if (new[] { context.Screen, context.SessionType, context.Platform, context.AppVersion, context.BuildRevision } .Any(value => (value ?? "").Length > MaxContextLength)) return $"context values must be {MaxContextLength} characters or fewer"; return null; } }