using Microsoft.EntityFrameworkCore; using Nuuru.Server.Extensions; using Nuuru.Server.Models; using Nuuru.Server.Services; using System.Collections.Concurrent; namespace Nuuru.Server.Middleware { public class DailyBointsMiddleware { private readonly RequestDelegate _next; private readonly IServiceScopeFactory _scopeFactory; private static readonly ConcurrentDictionary _credited = new(); private static DateTime _lastReset = DateTime.UtcNow.Date; public DailyBointsMiddleware(RequestDelegate next, IServiceScopeFactory scopeFactory) { _next = next; _scopeFactory = scopeFactory; } public async Task InvokeAsync(HttpContext context) { if (context.User.Identity?.IsAuthenticated == true) { var userId = context.User.GetUserId(); if (userId.HasValue) { var today = DateTime.UtcNow.Date; if (today > _lastReset) { _credited.Clear(); _lastReset = today; } if (_credited.TryAdd(userId.Value, 0)) { var uid = userId.Value; _ = Task.Run(async () => { try { using var scope = _scopeFactory.CreateScope(); var bointsService = scope.ServiceProvider.GetRequiredService(); var db = scope.ServiceProvider.GetRequiredService(); await bointsService.CreditAsync(uid, BointsReason.DailyLogin, 5); // Calculate login streak: count consecutive days with DailyLogin entries var loginDates = await db.BointsLedger .Where(l => l.UserId == uid && l.Reason == BointsReason.DailyLogin) .OrderByDescending(l => l.CreatedAt) .Select(l => l.CreatedAt.Date) .Distinct() .Take(8) .ToListAsync(); var streak = 0; var checkDate = DateTime.UtcNow.Date; foreach (var date in loginDates) { if (date == checkDate) { streak++; checkDate = checkDate.AddDays(-1); } else break; } var streakBonus = Math.Min(streak, 7); if (streakBonus > 0) await bointsService.CreditAsync(uid, BointsReason.LoginStreak, streakBonus); } catch { _credited.TryRemove(uid, out _); } }); } } } await _next(context); } } }