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 PaymentsController : ControllerBase
{
    private readonly RacePlannerDbContext _context;

    public PaymentsController(RacePlannerDbContext context)
    {
        _context = context;
    }

    [HttpPost]
    [Authorize(Roles = "Organizer")]
    public async Task<ActionResult<PaymentDto>> RecordPayment(CreatePaymentRequest request)
    {
        var userId = GetCurrentUserId();
        if (userId == null)
        {
            return Unauthorized();
        }

        // Load registration with event
        var registration = await _context.Registrations
            .Include(r => r.Event)
            .FirstOrDefaultAsync(r => r.Id == request.RegistrationId);

        if (registration == null)
        {
            return NotFound(new { error = "Registration not found" });
        }

        // Verify user is organizer of the event
        if (registration.Event.OrganizerId != userId.Value)
        {
            return Forbid();
        }

        // Check registration is not cancelled
        if (registration.Status == RegistrationStatus.Cancelled)
        {
            return BadRequest(new { error = "Cannot record payment for cancelled registration" });
        }

        var payment = new Payment
        {
            RegistrationId = request.RegistrationId,
            Amount = request.Amount,
            Method = request.Method,
            TransactionId = request.TransactionId,
            Notes = request.Notes,
            PaymentDate = request.PaymentDate ?? DateTime.UtcNow
        };

        _context.Payments.Add(payment);
        await _context.SaveChangesAsync();

        // Update registration status if fully paid (assuming a fixed fee amount would be known)
        // For now, we'll just record the payment

        return CreatedAtAction(
            nameof(GetPayment),
            new { id = payment.Id },
            MapToPaymentDto(payment));
    }

    [HttpGet("{id}")]
    [Authorize(Roles = "Organizer")]
    public async Task<ActionResult<PaymentDto>> GetPayment(Guid id)
    {
        var userId = GetCurrentUserId();
        if (userId == null)
        {
            return Unauthorized();
        }

        var payment = await _context.Payments
            .Include(p => p.Registration)
            .ThenInclude(r => r.Event)
            .FirstOrDefaultAsync(p => p.Id == id);

        if (payment == null)
        {
            return NotFound();
        }

        // Verify user is organizer
        if (payment.Registration.Event.OrganizerId != userId.Value)
        {
            return Forbid();
        }

        return Ok(MapToPaymentDto(payment));
    }

    [HttpGet("registration/{registrationId}")]
    [Authorize(Roles = "Organizer")]
    public async Task<ActionResult<IEnumerable<PaymentDto>>> GetRegistrationPayments(Guid registrationId)
    {
        var userId = GetCurrentUserId();
        if (userId == null)
        {
            return Unauthorized();
        }

        // Verify organizer has access to this registration
        var registration = await _context.Registrations
            .Include(r => r.Event)
            .FirstOrDefaultAsync(r => r.Id == registrationId);

        if (registration == null)
        {
            return NotFound();
        }

        if (registration.Event.OrganizerId != userId.Value)
        {
            return Forbid();
        }

        var payments = await _context.Payments
            .Where(p => p.RegistrationId == registrationId)
            .OrderByDescending(p => p.PaymentDate)
            .ToListAsync();

        return Ok(payments.Select(MapToPaymentDto));
    }

    [HttpGet("registration/{registrationId}/status")]
    [Authorize]
    public async Task<ActionResult<object>> GetPaymentStatus(Guid registrationId)
    {
        var userId = GetCurrentUserId();
        if (userId == null)
        {
            return Unauthorized();
        }

        var registration = await _context.Registrations
            .Include(r => r.Event)
            .Include(r => r.Payments)
            .FirstOrDefaultAsync(r => r.Id == registrationId);

        if (registration == null)
        {
            return NotFound();
        }

        // Participant can view their own, organizer can view any
        var isOrganizer = registration.Event.OrganizerId == userId.Value;

        if (registration.ParticipantId != userId.Value && !isOrganizer)
        {
            return Forbid();
        }

        var totalPaid = registration.Payments.Sum(p => p.Amount);
        var status = totalPaid > 0 ? "paid" : "unpaid";

        return Ok(new
        {
            RegistrationId = registrationId,
            TotalPaid = totalPaid,
            PaymentCount = registration.Payments.Count,
            Status = status,
            Payments = registration.Payments.Select(MapToPaymentDto)
        });
    }

    [HttpGet("event/{eventId}/report")]
    [Authorize(Roles = "Organizer")]
    public async Task<ActionResult<PaymentReportDto>> GetPaymentReport(Guid eventId)
    {
        var userId = GetCurrentUserId();
        if (userId == null)
        {
            return Unauthorized();
        }

        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.Participant)
            .Include(r => r.Payments)
            .Where(r => r.EventId == eventId && r.Status != RegistrationStatus.Cancelled)
            .ToListAsync();

        var totalCollected = registrations.Sum(r => r.Payments.Sum(p => p.Amount));
        var totalRegistrations = registrations.Count;
        var paidRegistrations = registrations.Count(r => r.Payments.Any());
        var unpaidRegistrations = registrations.Count(r => !r.Payments.Any());

        var report = new PaymentReportDto
        {
            EventId = eventId,
            EventName = eventEntity.Name,
            TotalCollected = totalCollected,
            TotalPending = 0, // Would require event fee structure
            TotalOutstanding = 0, // Would require event fee structure
            TotalRegistrations = totalRegistrations,
            PaidRegistrations = paidRegistrations,
            PartialRegistrations = 0, // Would require partial payment logic
            UnpaidRegistrations = unpaidRegistrations,
            Registrations = registrations.Select(r => new RegistrationPaymentDto
            {
                RegistrationId = r.Id,
                ParticipantName = r.Participant.Name,
                Status = r.Status.ToString(),
                TotalPaid = r.Payments.Sum(p => p.Amount),
                AmountDue = 0, // Would require event fee structure
                PaymentStatus = r.Payments.Any() ? "paid" : "unpaid",
                Payments = r.Payments.Select(MapToPaymentDto).ToList()
            }).ToList()
        };

        return Ok(report);
    }

    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 PaymentDto MapToPaymentDto(Payment payment)
    {
        return new PaymentDto
        {
            Id = payment.Id,
            RegistrationId = payment.RegistrationId,
            Amount = payment.Amount,
            Method = payment.Method.ToString(),
            TransactionId = payment.TransactionId,
            Notes = payment.Notes,
            PaymentDate = payment.PaymentDate,
            CreatedAt = payment.CreatedAt
        };
    }
}