using System.IdentityModel.Tokens.Jwt; using System.Security.Claims; using System.Text.Json; using System.Text.Json.Serialization; using line_gestao_api.Data; using line_gestao_api.Models; namespace line_gestao_api.Services; public class SystemAuditService : ISystemAuditService { private const int ActionMaxLength = 20; private static readonly JsonSerializerOptions JsonOptions = new() { DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull }; private readonly AppDbContext _db; private readonly IHttpContextAccessor _httpContextAccessor; private readonly ITenantProvider _tenantProvider; public SystemAuditService( AppDbContext db, IHttpContextAccessor httpContextAccessor, ITenantProvider tenantProvider) { _db = db; _httpContextAccessor = httpContextAccessor; _tenantProvider = tenantProvider; } public async Task LogAsync(string action, Guid targetTenantId, object? metadata = null, CancellationToken cancellationToken = default) { var actorTenantId = _tenantProvider.ActorTenantId; if (!actorTenantId.HasValue || actorTenantId.Value == Guid.Empty) { return; } var user = _httpContextAccessor.HttpContext?.User; var userId = ResolveUserId(user); var userName = ResolveUserName(user); var userEmail = ResolveUserEmail(user); var request = _httpContextAccessor.HttpContext?.Request; var requestPath = request?.Path.Value; var requestMethod = request?.Method; var ipAddress = _httpContextAccessor.HttpContext?.Connection?.RemoteIpAddress?.ToString(); var safeMetadataJson = JsonSerializer.Serialize(metadata ?? new { }, JsonOptions); var normalizedAction = NormalizeAction(action); _db.AuditLogs.Add(new AuditLog { TenantId = actorTenantId.Value, ActorUserId = userId, ActorTenantId = actorTenantId.Value, TargetTenantId = targetTenantId, OccurredAtUtc = DateTime.UtcNow, Action = normalizedAction, Page = "System", EntityName = "System", EntityId = targetTenantId.ToString(), EntityLabel = null, ChangesJson = "[]", MetadataJson = safeMetadataJson, UserId = userId, UserName = userName, UserEmail = userEmail, RequestPath = requestPath, RequestMethod = requestMethod, IpAddress = ipAddress }); await _db.SaveChangesAsync(cancellationToken); } private static string NormalizeAction(string? action) { var normalized = (action ?? string.Empty).Trim().ToUpperInvariant(); if (string.IsNullOrEmpty(normalized)) { return "UNKNOWN"; } if (normalized.Length <= ActionMaxLength) { return normalized; } return normalized[..ActionMaxLength]; } private static Guid? ResolveUserId(ClaimsPrincipal? user) { var raw = user?.FindFirstValue(ClaimTypes.NameIdentifier) ?? user?.FindFirstValue(JwtRegisteredClaimNames.Sub) ?? user?.FindFirstValue("sub"); return Guid.TryParse(raw, out var parsed) ? parsed : null; } private static string? ResolveUserName(ClaimsPrincipal? user) { return user?.FindFirstValue("name") ?? user?.FindFirstValue(ClaimTypes.Name) ?? user?.Identity?.Name; } private static string? ResolveUserEmail(ClaimsPrincipal? user) { return user?.FindFirstValue(ClaimTypes.Email) ?? user?.FindFirstValue(JwtRegisteredClaimNames.Email) ?? user?.FindFirstValue("email"); } }