using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.Logging; using UniVerse.Application.DTOs.Achievements; using UniVerse.Application.DTOs.Common; using UniVerse.Application.DTOs.Gamification; using UniVerse.Application.Interfaces; using UniVerse.Application.Mappings; using UniVerse.Domain.Entities; using UniVerse.Domain.Enums; using UniVerse.Infrastructure.Data; namespace UniVerse.Infrastructure.Services; public class GamificationService : IGamificationService { private readonly AppDbContext _db; private readonly IConfiguration _config; private readonly ILogger _logger; public GamificationService(AppDbContext db, IConfiguration config, ILogger logger) { _db = db; _config = config; _logger = logger; } public async Task AwardCoinsAsync(int userId, int amount, CoinTransactionType type, int? reviewId = null, int? achievementId = null, string? description = null) { var user = await _db.Users.FindAsync(userId); if (user == null) return; user.Coins += amount; user.Xp += amount; _db.CoinTransactions.Add(new CoinTransaction { UserId = userId, Amount = amount, Type = type, ReviewId = reviewId, AchievementId = achievementId, Description = description }); await _db.SaveChangesAsync(); _logger.LogInformation("Awarded {Amount} coins to user {UserId} ({Type})", amount, userId, type); } public async Task CheckAndAwardAchievementsAsync(int userId) { var achievements = await _db.Achievements.ToListAsync(); var existing = await _db.UserAchievements.Where(ua => ua.UserId == userId) .Select(ua => ua.AchievementId).ToListAsync(); var reviews = await _db.Reviews.CountAsync(r => r.UserId == userId); var attended = await _db.LectureEnrollments.CountAsync(e => e.UserId == userId && e.Attended); foreach (var achievement in achievements.Where(a => !existing.Contains(a.Id))) { var earned = achievement.Condition switch { "reviews_1" => reviews >= 1, "reviews_5" => reviews >= 5, "reviews_10" => reviews >= 10, "attended_5" => attended >= 5, "attended_10" => attended >= 10, _ => false }; if (!earned) continue; _db.UserAchievements.Add(new UserAchievement { UserId = userId, AchievementId = achievement.Id }); if (achievement.CoinReward > 0) await AwardCoinsAsync(userId, achievement.CoinReward, CoinTransactionType.AchievementReward, achievementId: achievement.Id, description: $"Achievement: {achievement.Name}"); } await _db.SaveChangesAsync(); } public int CalculateLevel(int xp) { var thresholds = _config.GetSection("Gamification:XpThresholds").Get() ?? [0, 100, 300, 600, 1000, 1500, 2500, 4000]; for (int i = thresholds.Length - 1; i >= 0; i--) if (xp >= thresholds[i]) return i + 1; return 1; } public async Task> GetUserAchievementsAsync(int userId) => await _db.UserAchievements.Include(ua => ua.Achievement) .Where(ua => ua.UserId == userId).OrderByDescending(ua => ua.AwardedAt) .Select(ua => ua.ToDto()).ToListAsync(); public async Task> GetTransactionsAsync(int userId, PaginationRequest pagination) { var query = _db.CoinTransactions.Where(ct => ct.UserId == userId); var total = await query.CountAsync(); var items = await query.OrderByDescending(ct => ct.CreatedAt) .Skip((pagination.Page - 1) * pagination.PageSize).Take(pagination.PageSize) .Select(ct => ct.ToDto()).ToListAsync(); return PagedResult.Create(items, total, pagination.Page, pagination.PageSize); } }