using ClosedXML.Excel; using line_gestao_api.Data; using line_gestao_api.Dtos; using line_gestao_api.Models; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; using Microsoft.EntityFrameworkCore; namespace line_gestao_api.Controllers; [ApiController] [Route("api/notifications")] [Authorize] public class NotificationsController : ControllerBase { private readonly AppDbContext _db; public NotificationsController(AppDbContext db) { _db = db; } [HttpGet] [HttpGet("/notifications")] public async Task>> GetNotifications() { var items = await ( from notification in _db.Notifications.AsNoTracking() join vigencia in _db.VigenciaLines.AsNoTracking() on notification.VigenciaLineId equals vigencia.Id into vigencias from vigencia in vigencias.DefaultIfEmpty() orderby notification.Data descending select new NotificationDto { Id = notification.Id, Tipo = notification.Tipo, Titulo = notification.Titulo, Mensagem = notification.Mensagem, Data = notification.Data, ReferenciaData = notification.ReferenciaData, DiasParaVencer = notification.DiasParaVencer, Lida = notification.Lida, LidaEm = notification.LidaEm, VigenciaLineId = notification.VigenciaLineId, Cliente = notification.Cliente ?? vigencia.Cliente, Linha = notification.Linha ?? vigencia.Linha, Conta = vigencia.Conta, Usuario = notification.Usuario ?? vigencia.Usuario, PlanoContrato = vigencia.PlanoContrato, DtEfetivacaoServico = vigencia.DtEfetivacaoServico, DtTerminoFidelizacao = vigencia.DtTerminoFidelizacao }) .ToListAsync(); return Ok(items); } [HttpPatch("{id:guid}/read")] [HttpPatch("/notifications/{id:guid}/read")] public async Task MarkAsRead(Guid id) { var notification = await _db.Notifications .FirstOrDefaultAsync(n => n.Id == id); if (notification is null) { return NotFound(); } if (!notification.Lida) { notification.Lida = true; notification.LidaEm = DateTime.UtcNow; await _db.SaveChangesAsync(); } return NoContent(); } [HttpPatch("read-all")] [HttpPatch("/notifications/read-all")] public async Task MarkAllAsRead( [FromQuery] string? filter, [FromBody] NotificationSelectionRequest? request) { var utcNow = DateTime.UtcNow; var query = ApplySelectionAndFilter(_db.Notifications, filter, request?.NotificationIds) .Where(n => !n.Lida); await query.ExecuteUpdateAsync(updates => updates .SetProperty(n => n.Lida, true) .SetProperty(n => n.LidaEm, utcNow)); return NoContent(); } [HttpGet("export")] [HttpGet("/notifications/export")] public async Task ExportNotifications([FromQuery] string? filter) { var query = ApplySelectionAndFilter(_db.Notifications.AsNoTracking(), filter, null); return await ExportNotificationsAsync(query, filter); } [HttpPost("export")] [HttpPost("/notifications/export")] public async Task ExportNotifications( [FromQuery] string? filter, [FromBody] NotificationSelectionRequest? request) { var query = ApplySelectionAndFilter(_db.Notifications.AsNoTracking(), filter, request?.NotificationIds); return await ExportNotificationsAsync(query, filter); } private async Task ExportNotificationsAsync(IQueryable query, string? filter) { var rows = await ( from notification in query join vigencia in _db.VigenciaLines.AsNoTracking() on notification.VigenciaLineId equals vigencia.Id into vigencias from vigencia in vigencias.DefaultIfEmpty() orderby notification.ReferenciaData descending, notification.Data descending select new NotificationExportRow( vigencia.Conta, notification.Linha ?? vigencia.Linha, notification.Cliente ?? vigencia.Cliente, notification.Usuario ?? vigencia.Usuario, vigencia.PlanoContrato, vigencia.DtEfetivacaoServico, notification.ReferenciaData ?? vigencia.DtTerminoFidelizacao, notification.Tipo)) .ToListAsync(); using var workbook = new XLWorkbook(); var worksheet = workbook.Worksheets.Add("Notificacoes"); var normalizedFilter = NormalizeFilter(filter); var headers = new[] { "CONTA", "LINHA", "Cliente", "Usuário", "PLANO CONTRATO", "DATA INICIO", normalizedFilter is "vencidas" or "vencido" ? "DATA VENCIMENTO" : "DATA A VENCER", "Status" }; for (var i = 0; i < headers.Length; i++) { worksheet.Cell(1, i + 1).Value = headers[i]; } var headerRange = worksheet.Range(1, 1, 1, headers.Length); headerRange.Style.Font.Bold = true; headerRange.Style.Fill.BackgroundColor = XLColor.LightGray; headerRange.Style.Alignment.Horizontal = XLAlignmentHorizontalValues.Center; for (var i = 0; i < rows.Count; i++) { var row = rows[i]; var rowIndex = i + 2; worksheet.Cell(rowIndex, 1).Value = row.Conta ?? string.Empty; worksheet.Cell(rowIndex, 2).Value = row.Linha ?? string.Empty; worksheet.Cell(rowIndex, 3).Value = (row.Cliente ?? string.Empty).ToUpperInvariant(); worksheet.Cell(rowIndex, 4).Value = (row.Usuario ?? string.Empty).ToUpperInvariant(); worksheet.Cell(rowIndex, 5).Value = row.PlanoContrato ?? string.Empty; worksheet.Cell(rowIndex, 6).Value = row.DataInicio; worksheet.Cell(rowIndex, 7).Value = row.DataReferencia; worksheet.Cell(rowIndex, 8).Value = row.Tipo.ToUpperInvariant(); } worksheet.Column(1).Width = 18; worksheet.Column(2).Width = 18; worksheet.Column(3).Width = 26; worksheet.Column(4).Width = 24; worksheet.Column(5).Width = 22; worksheet.Column(6).Width = 16; worksheet.Column(7).Width = 18; worksheet.Column(8).Width = 14; worksheet.Column(6).Style.DateFormat.Format = "dd/MM/yyyy"; worksheet.Column(7).Style.DateFormat.Format = "dd/MM/yyyy"; worksheet.Columns().AdjustToContents(); using var stream = new MemoryStream(); workbook.SaveAs(stream); stream.Position = 0; var fileName = $"notificacoes-{DateTime.UtcNow:yyyyMMddHHmmss}.xlsx"; return File( stream.ToArray(), "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", fileName); } private static IQueryable ApplySelectionAndFilter( IQueryable query, string? filter, IReadOnlyCollection? notificationIds) { query = ApplyFilter(query, filter); if (notificationIds is { Count: > 0 }) { query = query.Where(n => notificationIds.Contains(n.Id)); } return query; } private static IQueryable ApplyFilter(IQueryable query, string? filter) { var normalized = NormalizeFilter(filter); return normalized switch { "a-vencer" or "avencer" => query.Where(n => n.Tipo == "AVencer"), "vencidas" or "vencido" => query.Where(n => n.Tipo == "Vencido"), _ => query }; } private static string? NormalizeFilter(string? filter) { return filter?.Trim().ToLowerInvariant(); } private sealed record NotificationExportRow( string? Conta, string? Linha, string? Cliente, string? Usuario, string? PlanoContrato, DateTime? DataInicio, DateTime? DataReferencia, string Tipo); public sealed class NotificationSelectionRequest { public List? NotificationIds { get; set; } } }