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<ActionResult<RegistrationDto>> 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<ActionResult<RegistrationDto>> 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<ActionResult<IEnumerable<RegistrationDto>>> 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<ActionResult<IEnumerable<RegistrationDto>>> 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<ActionResult<RegistrationDto>> 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<ActionResult<RegistrationDto>> 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
        };
    }
}