using System.Text.Json.Serialization; using Cleanuparr.Domain.Enums; using Cleanuparr.Persistence; using Cleanuparr.Persistence.Models.Events; using Microsoft.AspNetCore.Mvc; using Microsoft.EntityFrameworkCore; namespace Cleanuparr.Api.Controllers; [ApiController] [Route("api/[controller]")] public class EventsController : ControllerBase { private readonly EventsContext _context; public EventsController(EventsContext context) { _context = context; } /// /// Gets events with pagination and filtering /// [HttpGet] public async Task>> GetEvents( [FromQuery] int page = 1, [FromQuery] int pageSize = 100, [FromQuery] string? severity = null, [FromQuery] string? eventType = null, [FromQuery] DateTime? fromDate = null, [FromQuery] DateTime? toDate = null, [FromQuery] string? search = null) { // Validate pagination parameters if (page < 1) page = 1; if (pageSize < 1) pageSize = 100; if (pageSize > 1000) pageSize = 1000; // Cap at 1000 for performance var query = _context.Events.AsQueryable(); // Apply filters if (!string.IsNullOrWhiteSpace(severity)) { if (Enum.TryParse(severity, true, out var severityEnum)) query = query.Where(e => e.Severity == severityEnum); } if (!string.IsNullOrWhiteSpace(eventType)) { if (Enum.TryParse(eventType, true, out var eventTypeEnum)) query = query.Where(e => e.EventType == eventTypeEnum); } // Apply date range filters if (fromDate.HasValue) { query = query.Where(e => e.Timestamp >= fromDate.Value); } if (toDate.HasValue) { query = query.Where(e => e.Timestamp <= toDate.Value); } // Apply search filter if provided if (!string.IsNullOrWhiteSpace(search)) { string pattern = EventsContext.GetLikePattern(search); query = query.Where(e => EF.Functions.Like(e.Message, pattern) || EF.Functions.Like(e.Data, pattern) || EF.Functions.Like(e.TrackingId.ToString(), pattern) ); } // Count total matching records for pagination var totalCount = await query.CountAsync(); // Calculate pagination var totalPages = (int)Math.Ceiling(totalCount / (double)pageSize); var skip = (page - 1) * pageSize; // Get paginated data var events = await query .OrderByDescending(e => e.Timestamp) .Skip(skip) .Take(pageSize) .ToListAsync(); events = events .OrderBy(e => e.Timestamp) .ToList(); // Return paginated result var result = new PaginatedResult { Items = events, Page = page, PageSize = pageSize, TotalCount = totalCount, TotalPages = totalPages }; return Ok(result); } /// /// Gets a specific event by ID /// [HttpGet("{id}")] public async Task> GetEvent(Guid id) { var eventEntity = await _context.Events.FindAsync(id); if (eventEntity == null) return NotFound(); return Ok(eventEntity); } /// /// Gets events by tracking ID /// [HttpGet("tracking/{trackingId}")] public async Task>> GetEventsByTracking(Guid trackingId) { var events = await _context.Events .Where(e => e.TrackingId == trackingId) .OrderBy(e => e.Timestamp) .ToListAsync(); return Ok(events); } /// /// Gets event statistics /// [HttpGet("stats")] public async Task> GetEventStats() { var stats = new { TotalEvents = await _context.Events.CountAsync(), EventsBySeverity = await _context.Events .GroupBy(e => e.Severity) .Select(g => new { Severity = g.Key.ToString(), Count = g.Count() }) .ToListAsync(), EventsByType = await _context.Events .GroupBy(e => e.EventType) .Select(g => new { EventType = g.Key.ToString(), Count = g.Count() }) .OrderByDescending(x => x.Count) .Take(10) .ToListAsync(), RecentEventsCount = await _context.Events .Where(e => e.Timestamp > DateTime.UtcNow.AddHours(-24)) .CountAsync() }; return Ok(stats); } /// /// Manually triggers cleanup of old events /// [HttpPost("cleanup")] public async Task> CleanupOldEvents([FromQuery] int retentionDays = 30) { var cutoffDate = DateTime.UtcNow.AddDays(-retentionDays); await _context.Events .Where(e => e.Timestamp < cutoffDate) .ExecuteDeleteAsync(); return Ok(); } /// /// Gets unique event types /// [HttpGet("types")] public async Task>> GetEventTypes() { var types = Enum.GetNames(typeof(EventType)).ToList(); return Ok(types); } /// /// Gets unique severities /// [HttpGet("severities")] public async Task>> GetSeverities() { var severities = Enum.GetNames(typeof(EventSeverity)).ToList(); return Ok(severities); } } /// /// Represents a paginated result set /// /// Type of items in the result public class PaginatedResult { /// /// The items in the current page /// public List Items { get; set; } = new(); /// /// Current page number (1-based) /// public int Page { get; set; } /// /// Number of items per page /// public int PageSize { get; set; } /// /// Total number of items across all pages /// public int TotalCount { get; set; } /// /// Total number of pages /// public int TotalPages { get; set; } /// /// Whether there is a previous page /// [JsonIgnore] public bool HasPrevious => Page > 1; /// /// Whether there is a next page /// [JsonIgnore] public bool HasNext => Page < TotalPages; }