using MongoDB.Bson; using MongoDB.Bson.Serialization.Attributes; namespace QuestionsApi.Models; public static class CreditEconomy { public const int RollingEarningLimit = 20; public static readonly TimeSpan EarningWindow = TimeSpan.FromHours(24); public const string ExamTokenSku = "exam_token_1"; public const string QBankResetTokenSku = "qbank_reset_1"; public const int ExamTokenPrice = 100; public const int QBankResetTokenPrice = 300; public static readonly IReadOnlyList Products = [ new() { Sku = ExamTokenSku, Name = "Exam Simulation Token", Description = "Starts one new full exam simulation.", CoinPrice = ExamTokenPrice, Grant = RzStoreGrant.ExamToken }, new() { Sku = QBankResetTokenSku, Name = "QBank Reset Token", Description = "Makes every QBank question eligible again without erasing progress.", CoinPrice = QBankResetTokenPrice, Grant = RzStoreGrant.QBankResetToken } ]; public static RzStoreProduct? FindProduct(string sku) => Products.FirstOrDefault(value => value.Sku.Equals(sku, StringComparison.Ordinal)); } public enum RzStoreGrant { ExamToken, QBankResetToken } public sealed class RzStoreProduct { public string Sku { get; set; } = ""; public string Name { get; set; } = ""; public string Description { get; set; } = ""; public int CoinPrice { get; set; } public RzStoreGrant Grant { get; set; } } public sealed class CreditEvent { public string OperationId { get; set; } = ""; public DateTime CreatedUtc { get; set; } } public sealed class CreditPurchaseEvent { public string OperationId { get; set; } = ""; public string Sku { get; set; } = ""; public int CoinCost { get; set; } public DateTime CreatedUtc { get; set; } } public sealed class UserCreditAccount { [BsonId] [BsonRepresentation(BsonType.ObjectId)] public string? Id { get; set; } public string UserId { get; set; } = ""; public int Credits { get; set; } public int ExamTokens { get; set; } public int QBankResetTokens { get; set; } public int BankCycle { get; set; } public bool InitialExamGrantApplied { get; set; } public List RecentEarnings { get; set; } = []; public List Purchases { get; set; } = []; public List AppliedOperations { get; set; } = []; public DateTime UpdatedUtc { get; set; } = DateTime.UtcNow; } public sealed class CreditStatusResponse { public int Credits { get; set; } public int EarnedInRollingWindow { get; set; } public int RemainingEarningCapacity { get; set; } public int RollingWindowHours { get; set; } = 24; public int ExamTokens { get; set; } public int QBankResetTokens { get; set; } public int BankCycle { get; set; } public IReadOnlyList Products { get; set; } = CreditEconomy.Products; } public sealed class CreditPurchaseRequest { public string Sku { get; set; } = ""; public string OperationId { get; set; } = ""; } public sealed class QBankResetRequest { public string Confirmation { get; set; } = ""; public string OperationId { get; set; } = ""; } public enum CreditOperationResult { Succeeded, InsufficientCredits, InsufficientTokens, OperationConflict } public sealed class CreditOperationResponse { public CreditOperationResult Result { get; set; } public CreditStatusResponse Status { get; set; } = new(); }