using System.Security.Claims; using Microsoft.EntityFrameworkCore; using ProposalSystem.Application.Interfaces; using ProposalSystem.Domain.Entities; using ProposalSystem.Infrastructure.Data; namespace ProposalSystem.Api.Services; public class CurrentUserService : ICurrentUserService { private readonly IHttpContextAccessor _httpContext; private readonly ProposalDbContext _db; private User? _cachedUser; public CurrentUserService(IHttpContextAccessor httpContext, ProposalDbContext db) { _httpContext = httpContext; _db = db; } public Guid UserId => GetOrThrow().Id; public string Email => GetOrThrow().Email; public UserRole Role => GetOrThrow().Role; public string? IpAddress => _httpContext.HttpContext?.Connection.RemoteIpAddress?.ToString(); public async Task ResolveAsync() { if (_cachedUser != null) return; var principal = _httpContext.HttpContext?.User ?? throw new UnauthorizedAccessException("No authenticated user"); var sub = principal.FindFirstValue("sub"); var email = principal.FindFirstValue(ClaimTypes.Email) ?? principal.FindFirstValue("email") ?? "unknown@seahaven.com"; var name = principal.FindFirstValue("name") ?? email.Split('@')[0]; var groups = principal.FindAll("cognito:groups") .Select(c => c.Value).ToList(); var role = groups.Contains("sysadmins") ? UserRole.SysAdmin : groups.Contains("admins") ? UserRole.Admin : UserRole.Dispatcher; var userId = principal.FindFirstValue(ClaimTypes.NameIdentifier); if (userId != null && Guid.TryParse(userId, out var parsedId)) _cachedUser = await _db.Users.FirstOrDefaultAsync(u => u.Id == parsedId); if (_cachedUser == null && sub != null) _cachedUser = await _db.Users.FirstOrDefaultAsync(u => u.CognitoSub == sub); if (_cachedUser == null) _cachedUser = await _db.Users.FirstOrDefaultAsync(u => u.Email == email); if (_cachedUser != null) { var changed = false; if (_cachedUser.Email != email) { _cachedUser.Email = email; changed = true; } if (_cachedUser.DisplayName != name) { _cachedUser.DisplayName = name; changed = true; } if (changed) { _cachedUser.UpdatedAt = DateTime.UtcNow; await _db.SaveChangesAsync(); } return; } _cachedUser = new User { Id = Guid.NewGuid(), CognitoSub = sub ?? $"auto-{Guid.NewGuid():N}", Email = email, DisplayName = name, Role = role, IsActive = true, CreatedAt = DateTime.UtcNow, UpdatedAt = DateTime.UtcNow, }; _db.Users.Add(_cachedUser); try { await _db.SaveChangesAsync(); } catch (DbUpdateException) { _db.Entry(_cachedUser).State = EntityState.Detached; _cachedUser = await _db.Users.FirstOrDefaultAsync(u => u.Email == email) ?? throw new UnauthorizedAccessException("Could not resolve current user"); } } private User GetOrThrow() { if (_cachedUser != null) return _cachedUser; ResolveAsync().GetAwaiter().GetResult(); return _cachedUser ?? throw new UnauthorizedAccessException("Could not resolve current user"); } }