using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Http.HttpResults;
using Microsoft.EntityFrameworkCore;
using System.Security.Cryptography;
using WebApp.Data;
using WebApp.DTOs;
using WebApp.Entities;
using WebApp.Mapping;

namespace WebApp.Endpoints
{
    public static class EventsRegistrationEndpoints
    {
        const string GetEventEndpointRegistrationName = "GetEventRegistration";

        public static RouteGroupBuilder MapEventsRegistrationEndpoints(this WebApplication app)
        {
            var group = app.MapGroup("api/events")
                                    .WithParameterValidation();

            // POST /api/events/join/{id}
            group.MapPost("/join/{id}",
                async (int id, ApplicationDbContext dbContext, HttpContext httpContext, GeneralUseHelpers guhf) =>
            {
                Event? Eve = await dbContext.Events.FindAsync(id);
                if (Eve is null)
                    return Results.Json(new { success = false, error_msg = "Event not found." });

                Token? token = await guhf.GetTokenFromHTTPContext(httpContext);
                User? user = await guhf.GetUserFromToken(token);

                if (user is null || user.IsOrganisation)
                    return Results.Json(new { success = false, error_msg = "Unauthorized or organisations cannot register for events." });

                if (await dbContext.EventRegistrations.AnyAsync(er => er.UserId == user.UserId && er.EventId == id))
                    return Results.Json(new { success = false, error_msg = "You are already registered for this event." });

                if (Eve.EventDate < DateTime.UtcNow)
                    return Results.Json(new { success = false, error_msg = "This event has already ended." });

                EventRegistration registration = new EventRegistration
                {
                    UserId = user.UserId,
                    EventId = id,
                    RegisteredAt = DateTime.UtcNow
                };
                dbContext.EventRegistrations.Add(registration);
                await dbContext.SaveChangesAsync();

                return Results.Json(new { success = true });
            });

            // POST /api/events/leave/{id}
            group.MapPost("/leave/{id}",
                async (int id, ApplicationDbContext dbContext, HttpContext httpContext, GeneralUseHelpers guhf) =>
            {
                Event? Eve = await dbContext.Events.FindAsync(id);
                if (Eve is null)
                    return Results.Json(new { success = false, error_msg = "Event not found." });

                Token? token = await guhf.GetTokenFromHTTPContext(httpContext);
                User? user = await guhf.GetUserFromToken(token);

                if (user is null)
                    return Results.Json(new { success = false, error_msg = "Unauthorized." });
                
                if (!await dbContext.EventRegistrations.AnyAsync(er => er.UserId == user.UserId && er.EventId == id))
                    return Results.Json(new { success = false, error_msg = "You are not registered for this event." });

                if (Eve.EventDate < DateTime.UtcNow)
                    return Results.Json(new { success = false, error_msg = "This event has already ended." });

                EventRegistration? registration = await dbContext.EventRegistrations
                    .FirstOrDefaultAsync(er => er.UserId == user.UserId && er.EventId == id);

                dbContext.EventRegistrations.Remove(registration);
                await dbContext.SaveChangesAsync();

                return Results.Json(new { success = true });
            });

            // GET /api/events/registrations/{id}
            group.MapGet("/registrations/{id}",
                async (int id, ApplicationDbContext dbContext, HttpContext httpContext, GeneralUseHelpers guhf) =>
            {
                Event? Eve = await dbContext.Events.FindAsync(id);
                if (Eve is null)
                    return Results.Json(new { success = false, error_msg = "Event not found." });

                Token? token = await guhf.GetTokenFromHTTPContext(httpContext);
                Organisation? org = await guhf.GetOrganisationFromToken(token);
                if (org is null || org.OrganisationId != Eve.OrganisationId)
                    return Results.Json(new { success = false, error_msg = "Unauthorized." });

                var registrations = await dbContext.EventRegistrations
                    .Where(er => er.EventId == id)
                    .Select(er => er.ToEventRegistrationDto())
                    .ToListAsync();

                return Results.Json(new
                {
                    success = true,
                    registrations
                });
            });

            // POST /api/events/remove/{id}/{userId}
            group.MapPost("/remove/{id}/{userId}",
                async (int id, int userId, ApplicationDbContext dbContext, HttpContext httpContext, GeneralUseHelpers guhf) =>
            {
                Event? Eve = await dbContext.Events.FindAsync(id);
                if (Eve is null)
                    return Results.Json(new { success = false, error_msg = "Event not found." });

                Token? token = await guhf.GetTokenFromHTTPContext(httpContext);
                Organisation? org = await guhf.GetOrganisationFromToken(token);
                if (org is null || org.OrganisationId != Eve.OrganisationId)
                    return Results.Json(new { success = false, error_msg = "Unauthorized." });

                EventRegistration? registration = await dbContext.EventRegistrations
                    .FirstOrDefaultAsync(er => er.UserId == userId && er.EventId == id);

                if (registration is null)
                    return Results.Json(new { success = false, error_msg = "Registration not found." });

                dbContext.EventRegistrations.Remove(registration);
                await dbContext.SaveChangesAsync();

                return Results.Json(new { success = true });
            });

            return group;
        }
    }
}
