using GerbilManagerWebAPI.Common; using GerbilManagerWebAPI.Dtos; using GerbilManagerWebAPI.Models; using Gridify; using Microsoft.AspNetCore.Http.HttpResults; using Microsoft.AspNetCore.Mvc; using Microsoft.EntityFrameworkCore; namespace GerbilManagerWebAPI.Endpoints { public static class GerbilEndpoints { public static IEndpointRouteBuilder MapGerbilEndpoints(this IEndpointRouteBuilder app) { var group = app.MapGroup("/gerbils").WithTags("Gerbils"); // GET /gerbils (Gridify: filter/order/page; e.g. status==Active, litterId==…, orderBy=name) group.MapGet("/", async ([AsParameters] GridifyParams query, ApplicationContext db) => TypedResults.Ok(await db.Gerbils.AsNoTracking() .ToPagedResultAsync(query, ToDto))); // GET /gerbils/breeders — distinct non-empty Herkunft values for the Tiere filter dropdown group.MapGet("/breeders", async (ApplicationContext db) => TypedResults.Ok(await db.Gerbils.AsNoTracking() .Where(g => g.OriginBreeder != null && g.OriginBreeder != "") .Select(g => g.OriginBreeder!) .Distinct().OrderBy(b => b).ToListAsync())); // GET /gerbils/{id} group.MapGet("/{id:guid}", async Task, NotFound>> (Guid id, ApplicationContext db) => { var g = await db.Gerbils.AsNoTracking().FirstOrDefaultAsync(x => x.Id == id); return g is null ? TypedResults.NotFound() : TypedResults.Ok(ToDto(g)); }); // POST /gerbils group.MapPost("/", async Task, ValidationProblem>> (GerbilInput input, ApplicationContext db) => { if (string.IsNullOrWhiteSpace(input.Name)) return TypedResults.ValidationProblem(new Dictionary { ["name"] = ["Name is required."] }); var g = new Gerbil { Id = Guid.NewGuid(), Name = input.Name }; Apply(g, input, isCreate: true); db.Gerbils.Add(g); await db.SaveChangesAsync(); return TypedResults.Created($"/gerbils/{g.Id}", ToDto(g)); }); // PUT /gerbils/{id} group.MapPut("/{id:guid}", async Task> (Guid id, GerbilInput input, ApplicationContext db) => { var g = await db.Gerbils.FirstOrDefaultAsync(x => x.Id == id); if (g is null) return TypedResults.NotFound(); g.Name = input.Name; Apply(g, input, isCreate: false); await db.SaveChangesAsync(); return TypedResults.NoContent(); }); // DELETE /gerbils/{id} (409 if referenced as a litter parent) group.MapDelete("/{id:guid}", async Task>> (Guid id, ApplicationContext db) => { var g = await db.Gerbils.FirstOrDefaultAsync(x => x.Id == id); if (g is null) return TypedResults.NotFound(); db.Gerbils.Remove(g); try { await db.SaveChangesAsync(); return TypedResults.NoContent(); } catch (DbUpdateException) { return TypedResults.Conflict("Gerbil is referenced as a litter parent and cannot be deleted."); } }); return app; } private static void Apply(Gerbil g, GerbilInput i, bool isCreate) { g.Gender = i.Gender; g.Status = i.Status ?? (isCreate ? GerbilStatus.Active : g.Status); g.LitterId = i.LitterId; g.OriginContactId = i.OriginContactId; g.ReceiverContactId = i.ReceiverContactId; g.EnclosureId = i.EnclosureId; g.ColorVarietyId = i.ColorVarietyId; g.DateOfBirth = i.DateOfBirth; g.DateOfDeath = i.DateOfDeath; g.CauseOfDeath = i.CauseOfDeath; g.GoHomeDate = i.GoHomeDate; g.Genotype = i.Genotype; g.Notes = i.Notes; g.ImportSource = i.ImportSource; g.ExternalRef = i.ExternalRef; g.OriginBreeder = i.OriginBreeder; g.CharacterTraits = i.CharacterTraits ?? new List(); g.CharacterNote = i.CharacterNote; } internal static GerbilDto ToDto(Gerbil g) => new( g.Id, g.Name, g.Gender, g.Status, g.LitterId, g.OriginContactId, g.ReceiverContactId, g.EnclosureId, g.ColorVarietyId, g.DateOfBirth, g.DateOfDeath, g.CauseOfDeath, g.GoHomeDate, g.Genotype, g.Notes, g.ImportSource, g.ExternalRef, g.OriginBreeder, g.CharacterTraits, g.CharacterNote); } }