using Microsoft.AspNetCore.Http;
using Microsoft.EntityFrameworkCore;
using WebApp.Data;
using WebApp.DTOs;
using WebApp.Entities;
using WebApp.Mapping;

namespace WebApp.Endpoints
{
    public static class EventsEndpoints
    {
        const string GetEventEndpointName = "GetEvent";

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

            // GET /events
            group.MapGet("/",
                async (ApplicationDbContext dbContext, HttpContext httpContext) =>
            {

                var sort = httpContext.Request.Query["sort"].ToString();
                IOrderedQueryable<Event> res;
                var r = dbContext.Events
                           .Include(Eve => Eve.Organisation);

                if (sort is not null && sort.ToUpper() == "ASC")
                {
                    res = r.OrderBy(Eve => Eve.EventId);
                }
                else
                {
                    res = r.OrderByDescending(Eve => Eve.EventId);
                }

                    return await res
                           .Select(Eve => Eve.ToEventSummaryDto()) //EventSummaryDto
                           .AsNoTracking()
                           .ToListAsync();
            });
                

            // GET /events/1
            group.MapGet("/{id}", 
                async (int id, ApplicationDbContext dbContext, HttpContext httpContext, GeneralUseHelpers guhf) =>
            {
                Event? Eve = await dbContext.Events.FindAsync(id);

                if (Eve is null) return Results.NotFound();

                // Sprawdź, czy token należy do organizacji, a jeżeli tak, to do której.
                Token? token = await guhf.GetTokenFromHTTPContext(httpContext);
                Organisation? org = await guhf.GetOrganisationFromToken(token);

                // Jeśli token należy do organizacji, która utworzyła to wydarzenie,
                // to zwróć także EventRegistrations. W przeciwnym razie usuń to pole
                // przed jego wysłaniem!
                if (org is null || org.OrganisationId != Eve.OrganisationId) Eve.EventRegistrations = [];

                // DLACZEGO?
                Eve.Organisation = await guhf.GetOrganisationFromId(Eve.OrganisationId);

                EventDetailsDto EveDto = Eve.ToEventDetailsDto();

                return Results.Ok(EveDto); //EventDetailsDto
            })
            .WithName(GetEventEndpointName);

            // POST /events
            group.MapPost("/", 
                async (EventCreateDto newEvent, ApplicationDbContext dbContext, HttpContext httpContext, GeneralUseHelpers guhf) =>
            {

                // Uzyskaj organizację z tokenu
                Token? token = await guhf.GetTokenFromHTTPContext(httpContext);
                Organisation? org = await guhf.GetOrganisationFromToken(token);
                if (org is null) return Results.Unauthorized();

                // dodajemy id organizacji z tokenu
                Event Eve = newEvent.ToEntity();
                Eve.OrganisationId = org.OrganisationId;

                dbContext.Events.Add(Eve);
                await dbContext.SaveChangesAsync();

                return Results.CreatedAtRoute(
                    GetEventEndpointName, 
                    new { id = Eve.EventId }, 
                    Eve.ToEventDetailsDto()); //EventDetailsDto
            });

            // PUT /events/1
            group.MapPut("/{id}", 
                async (int id, EventUpdateDto updatedEvent, ApplicationDbContext dbContext, GeneralUseHelpers guhf, HttpContext httpContext) =>
            {
                // Uzyskaj organizację z tokenu
                Token? token = await guhf.GetTokenFromHTTPContext(httpContext);
                Organisation? org = await guhf.GetOrganisationFromToken(token);
                if (org is null) return Results.Unauthorized();

                Console.Write(org.OrganisationId);
                var existingEvent = await dbContext.Events.FindAsync(id);
                if (existingEvent is null)
                {
                    return Results.NotFound();
                }

                // Sprawdź, czy organizacja ma prawo
                // do zmodyfikowania tego (EventId = id) eventu.
                if (org.OrganisationId != existingEvent.OrganisationId) return Results.StatusCode(403);

                var originalOrgId = existingEvent.OrganisationId;
                dbContext.Entry(existingEvent)
                         .CurrentValues
                         .SetValues(updatedEvent.ToEntity(id));
                existingEvent.OrganisationId = originalOrgId;

                dbContext.Entry(existingEvent)
                         .Collection(Eve => Eve.EventRegistrations)
                         .IsModified = false;

                await dbContext.SaveChangesAsync();

                return Results.NoContent();
            });

            // DELETE /events/1
            group.MapDelete("/{id}", 
                async (int id, ApplicationDbContext dbContext, GeneralUseHelpers guhf, HttpContext httpContext) =>
            {

                // Uzyskaj organizację z tokenu
                Token? token = await guhf.GetTokenFromHTTPContext(httpContext);
                Organisation? org = await guhf.GetOrganisationFromToken(token);
                if (org is null) return Results.StatusCode(403);

                // Sprawdź, czy organizacja ma prawo
                // do usunięcia tego (EventId = id) eventu.
                Event? Eve = await dbContext.Events.FindAsync(id);
                if (Eve is null) return Results.NotFound();
                else if (org.OrganisationId != Eve.OrganisationId) return Results.StatusCode(403);

                await dbContext.Events
                         .Where(Eve => Eve.EventId == id)
                         .ExecuteDeleteAsync();

                return Results.NoContent();
            });

            // POST /events/search
            group.MapPost("/search/",
                async (EventSearchDto query, ApplicationDbContext dbContext, HttpContext httpContext, GeneralUseHelpers guhf) =>
                {

                    // Uzyskaj organizację z tokenu
                    var sort = httpContext.Request.Query["sort"].ToString();
                    Token? token = await guhf.GetTokenFromHTTPContext(httpContext);
                    Organisation? org = await guhf.GetOrganisationFromToken(token);
                    List<EventSummaryDto> SearchResults = [];


                    
                    List<Event> AllEvents = await dbContext.Events.ToListAsync();
                    if (sort is null || sort.ToUpper() != "ASC")
                    {
                        AllEvents.Reverse(); // aby wyświetlało od najnowszych wydarzeń

                    }

                    foreach(Event e in AllEvents)
                    {
                        bool matchFound = true;
                        // Logika wyszukiwania
                        // Sprawdź wszystkie pola z EventSearchDto, np.
                        if (query.OrganisationId is not null)
                        {
                            // Sprawdź, czy Event należy do query.OrganisationId.
                            if (e.OrganisationId != query.OrganisationId) matchFound = false;
                        }

                        if (query.TitleOrDescription is not null)
                        {
                            var TitleMatch = guhf.SearchString(e.Title, query.TitleOrDescription);
                            var DescMatch  = guhf.SearchString(e.Description, query.TitleOrDescription);
                            if (!TitleMatch && !DescMatch) matchFound = false;
                        }


                        //Zakres dat do wyszukiwania
                        if(query.EventDateFrom is not null)
                        {
                            if (e.EventDate < query.EventDateFrom) matchFound = false;

                        }
                        if(query.EventDateTo is not null)
                        {
                            if (e.EventDate > query.EventDateTo) matchFound = false;
                        }
                            
                       
                        // ...

                        // Jeśli Event jest tym, czego szuka użytkownik,
                        // dodaj go do listy SearchResults.
                        //
                        // Uwaga! Zanim to zrobisz, sprawdź, czy użytkownik
                        // jest twórcą danego wydarzenia! Jeżeli nim nie jest,
                        // wyzeruj EventRegistrations!
                        if (org is null || e.Organisation != org)
                        {
                            e.EventRegistrations.Clear();
                        }

                        // UWAGA! TO NIE POWINNO TAK DZIAŁAĆ!
                        // KTOKOLWIEK WIDZIAŁ, KTOKOLWIEK WIE CZEMU Organisation JEST null?
                        e.Organisation = await guhf.GetOrganisationFromId(e.OrganisationId);

                        if (matchFound) SearchResults.Add(e.ToEventSummaryDto());
                    }

                    return Results.Ok(SearchResults);
                });

            return group;
        }
    }
}
