using GerbilManagerWebAPI.Common;
using GerbilManagerWebAPI.Dtos;
using GerbilManagerWebAPI.Models;
using Gridify;
using Microsoft.AspNetCore.Http.HttpResults;
using Microsoft.EntityFrameworkCore;

namespace GerbilManagerWebAPI.Endpoints
{
    public static class EnclosureEndpoints
    {
        public static IEndpointRouteBuilder MapEnclosureEndpoints(this IEndpointRouteBuilder app)
        {
            var group = app.MapGroup("/enclosures").WithTags("Enclosures");

            group.MapGet("/", async ([AsParameters] GridifyParams query, ApplicationContext db) =>
                TypedResults.Ok(await db.Enclosures.AsNoTracking().ToPagedResultAsync(query, ToDto)));

            group.MapGet("/{id:guid}", async Task<Results<Ok<EnclosureDto>, NotFound>> (Guid id, ApplicationContext db) =>
            {
                var e = await db.Enclosures.AsNoTracking().FirstOrDefaultAsync(x => x.Id == id);
                return e is null ? TypedResults.NotFound() : TypedResults.Ok(ToDto(e));
            });

            group.MapPost("/", async (EnclosureInput input, ApplicationContext db) =>
            {
                var e = new Enclosure { Id = Guid.NewGuid(), Name = input.Name, Notes = input.Notes };
                db.Enclosures.Add(e);
                await db.SaveChangesAsync();
                return TypedResults.Created($"/enclosures/{e.Id}", ToDto(e));
            });

            group.MapPut("/{id:guid}", async Task<Results<NoContent, NotFound>> (Guid id, EnclosureInput input, ApplicationContext db) =>
            {
                var e = await db.Enclosures.FirstOrDefaultAsync(x => x.Id == id);
                if (e is null) return TypedResults.NotFound();
                e.Name = input.Name; e.Notes = input.Notes;
                await db.SaveChangesAsync();
                return TypedResults.NoContent();
            });

            // 409 if the enclosure still houses gerbils.
            group.MapDelete("/{id:guid}", async Task<Results<NoContent, NotFound, Conflict<string>>> (Guid id, ApplicationContext db) =>
            {
                var e = await db.Enclosures.FirstOrDefaultAsync(x => x.Id == id);
                if (e is null) return TypedResults.NotFound();
                bool occupied = await db.Gerbils.AnyAsync(g => g.EnclosureId == id);
                if (occupied) return TypedResults.Conflict("Enclosure still contains gerbils and cannot be deleted.");
                db.Enclosures.Remove(e);
                await db.SaveChangesAsync();
                return TypedResults.NoContent();
            });

            return app;
        }

        private static EnclosureDto ToDto(Enclosure e) => new(e.Id, e.Name, e.Notes);
    }
}
