using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; using Microsoft.EntityFrameworkCore; using RacePlannerApi.Data; using RacePlannerApi.DTOs; using RacePlannerApi.Models; namespace RacePlannerApi.Controllers; [ApiController] [Route("api/[controller]")] [Authorize] public class RegistrationsController : ControllerBase { private readonly RacePlannerDbContext _context; public RegistrationsController(RacePlannerDbContext context) { _context = context; } [HttpPost] public async Task> CreateRegistration(CreateRegistrationRequest request) { var userId = GetCurrentUserId(); if (userId == null) { return Unauthorized(); } // Check if event exists and is published var eventEntity = await _context.Events .FirstOrDefaultAsync(e => e.Id == request.EventId); if (eventEntity == null) { return NotFound(new { error = "Event not found" }); } if (eventEntity.Status != EventStatus.Published) { return BadRequest(new { error = "Event is not open for registration" }); } // Check if already registered var existingRegistration = await _context.Registrations .FirstOrDefaultAsync(r => r.EventId == request.EventId && r.ParticipantId == userId.Value); if (existingRegistration != null) { return Conflict(new { error = "Already registered for this event" }); } // Check if event is full if (eventEntity.MaxParticipants.HasValue) { var registrationCount = await _context.Registrations .CountAsync(r => r.EventId == request.EventId && r.Status != RegistrationStatus.Cancelled); if (registrationCount >= eventEntity.MaxParticipants.Value) { return BadRequest(new { error = "Event is full" }); } } var registration = new Registration { EventId = request.EventId, ParticipantId = userId.Value, Status = RegistrationStatus.Pending, Category = request.Category, EmergencyContact = request.EmergencyContact }; _context.Registrations.Add(registration); await _context.SaveChangesAsync(); // Load related data await _context.Entry(registration) .Reference(r => r.Event) .LoadAsync(); await _context.Entry(registration) .Reference(r => r.Participant) .LoadAsync(); await _context.Entry(registration) .Collection(r => r.Payments) .LoadAsync(); return CreatedAtAction( nameof(GetRegistration), new { id = registration.Id }, MapToRegistrationDto(registration)); } [HttpGet("{id}")] public async Task> GetRegistration(Guid id) { var userId = GetCurrentUserId(); if (userId == null) { return Unauthorized(); } var registration = await _context.Registrations .Include(r => r.Event) .Include(r => r.Participant) .Include(r => r.Payments) .FirstOrDefaultAsync(r => r.Id == id); if (registration == null) { return NotFound(); } // Only participant or event organizer can view var isOrganizer = await _context.Events .AnyAsync(e => e.Id == registration.EventId && e.OrganizerId == userId.Value); if (registration.ParticipantId != userId.Value && !isOrganizer) { return Forbid(); } return Ok(MapToRegistrationDto(registration)); } [HttpGet("my-registrations")] public async Task>> GetMyRegistrations() { var userId = GetCurrentUserId(); if (userId == null) { return Unauthorized(); } var registrations = await _context.Registrations .Include(r => r.Event) .Include(r => r.Participant) .Include(r => r.Payments) .Where(r => r.ParticipantId == userId.Value) .OrderBy(r => r.Event.EventDate) .ToListAsync(); return Ok(registrations.Select(MapToRegistrationDto)); } [HttpGet("event/{eventId}")] [Authorize(Roles = "Organizer")] public async Task>> GetEventRegistrations(Guid eventId) { var userId = GetCurrentUserId(); if (userId == null) { return Unauthorized(); } // Verify user is organizer of this event var eventEntity = await _context.Events .FirstOrDefaultAsync(e => e.Id == eventId && e.OrganizerId == userId.Value); if (eventEntity == null) { return NotFound(); } var registrations = await _context.Registrations .Include(r => r.Event) .Include(r => r.Participant) .Include(r => r.Payments) .Where(r => r.EventId == eventId) .OrderBy(r => r.CreatedAt) .ToListAsync(); return Ok(registrations.Select(MapToRegistrationDto)); } [HttpPut("{id}")] public async Task> UpdateRegistration(Guid id, UpdateRegistrationRequest request) { var userId = GetCurrentUserId(); if (userId == null) { return Unauthorized(); } var registration = await _context.Registrations .Include(r => r.Event) .FirstOrDefaultAsync(r => r.Id == id); if (registration == null) { return NotFound(); } // Participants can only update their own registrations // Organizers can update any registration for their events var isOrganizer = registration.Event.OrganizerId == userId.Value; if (registration.ParticipantId != userId.Value && !isOrganizer) { return Forbid(); } // Update fields if (request.Status.HasValue && isOrganizer) { registration.Status = request.Status.Value; } if (request.Category != null) { registration.Category = request.Category; } if (request.EmergencyContact != null) { registration.EmergencyContact = request.EmergencyContact; } registration.UpdatedAt = DateTime.UtcNow; await _context.SaveChangesAsync(); // Reload related data await _context.Entry(registration) .Reference(r => r.Participant) .LoadAsync(); await _context.Entry(registration) .Collection(r => r.Payments) .LoadAsync(); return Ok(MapToRegistrationDto(registration)); } [HttpPost("{id}/cancel")] public async Task> CancelRegistration(Guid id) { var userId = GetCurrentUserId(); if (userId == null) { return Unauthorized(); } var registration = await _context.Registrations .Include(r => r.Event) .Include(r => r.Participant) .Include(r => r.Payments) .FirstOrDefaultAsync(r => r.Id == id); if (registration == null) { return NotFound(); } // Only participant or event organizer can cancel var isOrganizer = registration.Event.OrganizerId == userId.Value; if (registration.ParticipantId != userId.Value && !isOrganizer) { return Forbid(); } registration.Status = RegistrationStatus.Cancelled; registration.UpdatedAt = DateTime.UtcNow; await _context.SaveChangesAsync(); return Ok(MapToRegistrationDto(registration)); } private Guid? GetCurrentUserId() { var userIdClaim = User.FindFirst(System.Security.Claims.ClaimTypes.NameIdentifier)?.Value; if (Guid.TryParse(userIdClaim, out var userId)) { return userId; } return null; } private static RegistrationDto MapToRegistrationDto(Registration registration) { var totalPaid = registration.Payments?.Sum(p => p.Amount) ?? 0; return new RegistrationDto { Id = registration.Id, EventId = registration.EventId, EventName = registration.Event?.Name ?? string.Empty, EventDate = registration.Event?.EventDate ?? DateTime.MinValue, ParticipantId = registration.ParticipantId, ParticipantName = registration.Participant?.Name ?? string.Empty, ParticipantEmail = registration.Participant?.Email ?? string.Empty, Status = registration.Status.ToString(), Category = registration.Category, EmergencyContact = registration.EmergencyContact, CreatedAt = registration.CreatedAt, UpdatedAt = registration.UpdatedAt, TotalPaid = totalPaid, AmountDue = 0 // Will be calculated based on event fee if needed }; } }