using Microsoft.AspNetCore.Mvc; using Microsoft.EntityFrameworkCore; using RR3CommunityServer.Data; using RR3CommunityServer.Models; namespace RR3CommunityServer.Controllers; [ApiController] [Route("synergy/notifications")] public class NotificationsController : ControllerBase { private readonly RR3DbContext _context; private readonly ILogger _logger; public NotificationsController(RR3DbContext context, ILogger logger) { _context = context; _logger = logger; } // GET /synergy/notifications?synergyId=xxx&unreadOnly=false&limit=50 [HttpGet] public async Task GetNotifications( [FromQuery] string synergyId, [FromQuery] bool unreadOnly = false, [FromQuery] int limit = 50) { try { if (string.IsNullOrEmpty(synergyId)) return BadRequest(new SynergyResponse { resultCode = -1, message = "synergyId required" }); var user = await _context.Users.FirstOrDefaultAsync(u => u.SynergyId == synergyId); if (user == null) return NotFound(new SynergyResponse { resultCode = -404, message = "Player not found" }); var now = DateTime.UtcNow; var query = _context.Notifications .Where(n => n.UserId == user.Id && (n.ExpiresAt == null || n.ExpiresAt > now)); if (unreadOnly) query = query.Where(n => !n.IsRead); var notifications = await query .OrderByDescending(n => n.CreatedAt) .Take(limit) .ToListAsync(); var unreadCount = await _context.Notifications .CountAsync(n => n.UserId == user.Id && !n.IsRead && (n.ExpiresAt == null || n.ExpiresAt > now)); var dtos = notifications.Select(n => new NotificationDto { Id = n.Id, Type = n.Type, Title = n.Title, Message = n.Message, IsRead = n.IsRead, CreatedAt = new DateTimeOffset(n.CreatedAt).ToUnixTimeSeconds(), ExpiresAt = n.ExpiresAt.HasValue ? new DateTimeOffset(n.ExpiresAt.Value).ToUnixTimeSeconds() : null }).ToList(); _logger.LogInformation("Retrieved {Count} notifications for {SynergyId}", dtos.Count, synergyId); return Ok(new SynergyResponse { resultCode = 0, data = new NotificationsResponse { Notifications = dtos, TotalCount = dtos.Count, UnreadCount = unreadCount } }); } catch (Exception ex) { _logger.LogError(ex, "Error getting notifications for {SynergyId}", synergyId); return StatusCode(500, new SynergyResponse { resultCode = -500, message = "Internal server error" }); } } // GET /synergy/notifications/unread-count?synergyId=xxx [HttpGet("unread-count")] public async Task GetUnreadCount([FromQuery] string synergyId) { try { if (string.IsNullOrEmpty(synergyId)) return BadRequest(new SynergyResponse { resultCode = -1, message = "synergyId required" }); var user = await _context.Users.FirstOrDefaultAsync(u => u.SynergyId == synergyId); if (user == null) return Ok(new SynergyResponse { resultCode = 0, data = new UnreadCountResponse { UnreadCount = 0 } }); var now = DateTime.UtcNow; var count = await _context.Notifications .CountAsync(n => n.UserId == user.Id && !n.IsRead && (n.ExpiresAt == null || n.ExpiresAt > now)); return Ok(new SynergyResponse { resultCode = 0, data = new UnreadCountResponse { UnreadCount = count } }); } catch (Exception ex) { _logger.LogError(ex, "Error getting unread count for {SynergyId}", synergyId); return StatusCode(500, new SynergyResponse { resultCode = -500, message = "Internal server error" }); } } // POST /synergy/notifications/mark-read [HttpPost("mark-read")] public async Task MarkRead([FromBody] MarkReadRequest request) { try { if (string.IsNullOrEmpty(request.SynergyId)) return BadRequest(new SynergyResponse { resultCode = -1, message = "synergyId required" }); var user = await _context.Users.FirstOrDefaultAsync(u => u.SynergyId == request.SynergyId); if (user == null) return NotFound(new SynergyResponse { resultCode = -404, message = "Player not found" }); IQueryable query = _context.Notifications.Where(n => n.UserId == user.Id && !n.IsRead); // If specific IDs provided, only mark those; otherwise mark all if (request.NotificationIds != null && request.NotificationIds.Count > 0) query = query.Where(n => request.NotificationIds.Contains(n.Id)); var notifications = await query.ToListAsync(); foreach (var n in notifications) n.IsRead = true; await _context.SaveChangesAsync(); _logger.LogInformation("Marked {Count} notifications read for {SynergyId}", notifications.Count, request.SynergyId); return Ok(new SynergyResponse { resultCode = 0, message = $"Marked {notifications.Count} notification(s) as read" }); } catch (Exception ex) { _logger.LogError(ex, "Error marking notifications read for {SynergyId}", request.SynergyId); return StatusCode(500, new SynergyResponse { resultCode = -500, message = "Internal server error" }); } } // POST /synergy/notifications/send (admin/system use) [HttpPost("send")] public async Task SendNotification([FromBody] SendNotificationRequest request) { try { if (string.IsNullOrEmpty(request.Title) || string.IsNullOrEmpty(request.Message)) return BadRequest(new SynergyResponse { resultCode = -1, message = "title and message required" }); DateTime? expiresAt = request.ExpiresInHours.HasValue ? DateTime.UtcNow.AddHours(request.ExpiresInHours.Value) : null; int sentCount = 0; if (string.IsNullOrEmpty(request.SynergyId)) { // Broadcast to all users var allUsers = await _context.Users.Select(u => u.Id).ToListAsync(); var bulk = allUsers.Select(uid => new Notification { UserId = uid, Type = request.Type, Title = request.Title, Message = request.Message, CreatedAt = DateTime.UtcNow, ExpiresAt = expiresAt }).ToList(); _context.Notifications.AddRange(bulk); sentCount = bulk.Count; } else { // Send to specific player var user = await _context.Users.FirstOrDefaultAsync(u => u.SynergyId == request.SynergyId); if (user == null) return NotFound(new SynergyResponse { resultCode = -404, message = "Player not found" }); _context.Notifications.Add(new Notification { UserId = user.Id, Type = request.Type, Title = request.Title, Message = request.Message, CreatedAt = DateTime.UtcNow, ExpiresAt = expiresAt }); sentCount = 1; } await _context.SaveChangesAsync(); _logger.LogInformation("Sent notification '{Title}' to {Count} player(s)", request.Title, sentCount); return Ok(new SynergyResponse { resultCode = 0, message = $"Notification sent to {sentCount} player(s)" }); } catch (Exception ex) { _logger.LogError(ex, "Error sending notification"); return StatusCode(500, new SynergyResponse { resultCode = -500, message = "Internal server error" }); } } // DELETE /synergy/notifications/{id}?synergyId=xxx [HttpDelete("{id:int}")] public async Task DeleteNotification(int id, [FromQuery] string synergyId) { try { if (string.IsNullOrEmpty(synergyId)) return BadRequest(new SynergyResponse { resultCode = -1, message = "synergyId required" }); var user = await _context.Users.FirstOrDefaultAsync(u => u.SynergyId == synergyId); if (user == null) return NotFound(new SynergyResponse { resultCode = -404, message = "Player not found" }); var notification = await _context.Notifications .FirstOrDefaultAsync(n => n.Id == id && n.UserId == user.Id); if (notification == null) return NotFound(new SynergyResponse { resultCode = -404, message = "Notification not found" }); _context.Notifications.Remove(notification); await _context.SaveChangesAsync(); _logger.LogInformation("Deleted notification {Id} for {SynergyId}", id, synergyId); return Ok(new SynergyResponse { resultCode = 0, message = "Notification deleted" }); } catch (Exception ex) { _logger.LogError(ex, "Error deleting notification {Id}", id); return StatusCode(500, new SynergyResponse { resultCode = -500, message = "Internal server error" }); } } }