using GerbilManagerWebAPI.Dtos; using GerbilManagerWebAPI.Models; using Microsoft.AspNetCore.Http.HttpResults; using Microsoft.EntityFrameworkCore; namespace GerbilManagerWebAPI.Endpoints { /// /// ABGABE-STATUS (RPRO3 abgeben_tb / abstat_tb): the pre-handover reservation/sale /// pipeline — verfügbar → reserviert → abgegeben. /// GET /reservations -> list all, newest-updated first. /// GET /reservations?gerbilId= -> the (single) reservation status for one animal, if any. /// POST /reservations -> create/establish a status, returns 201. /// PUT /reservations/{id} -> change status / reservation details. 404 on missing id. /// DELETE /reservations/{id} -> remove. 404 on missing id. /// Decoupled from gerbils/contacts (loose nullable Guid columns, no FK), so rows survive /// the import re-ingest wipe — like Feedback. /// public static class SaleReservationEndpoints { private static readonly string[] AllowedStatuses = { "verfuegbar", "reserviert", "abgegeben" }; /// Normalize a status string to one of the three canonical values; default "verfuegbar". private static string NormalizeStatus(string? raw) { var s = raw?.Trim().ToLowerInvariant(); return AllowedStatuses.Contains(s) ? s! : "verfuegbar"; } public static IEndpointRouteBuilder MapSaleReservationEndpoints(this IEndpointRouteBuilder app) { var group = app.MapGroup("/reservations").WithTags("Reservations"); group.MapGet("/", async (ApplicationContext db, Guid? gerbilId) => { // Order in memory: SQLite (test host) cannot ORDER BY a DateTimeOffset column. var rows = await db.SaleReservations.AsNoTracking().ToListAsync(); var filtered = gerbilId is { } gid ? rows.Where(r => r.GerbilId == gid) : rows; return TypedResults.Ok(filtered .OrderByDescending(r => r.UpdatedAt) .Select(ToDto) .ToList()); }); group.MapPost("/", async Task, BadRequest>> ( SaleReservationInput input, ApplicationContext db) => { if (input.GerbilId == Guid.Empty) return TypedResults.BadRequest("GerbilId ist erforderlich."); var now = DateTimeOffset.UtcNow; var entity = new SaleReservation { Id = Guid.NewGuid(), GerbilId = input.GerbilId, GerbilName = Trim(input.GerbilName), Status = NormalizeStatus(input.Status), ReservedForContactId = input.ReservedForContactId, ContactName = Trim(input.ContactName), AppointmentDate = input.AppointmentDate, Price = input.Price, Note = Trim(input.Note), HandedOverDate = input.HandedOverDate, CreatedAt = now, UpdatedAt = now, }; db.SaleReservations.Add(entity); await db.SaveChangesAsync(); return TypedResults.Created($"/reservations/{entity.Id}", ToDto(entity)); }); group.MapPut("/{id:guid}", async Task, NotFound>> ( Guid id, SaleReservationUpdate input, ApplicationContext db) => { var entity = await db.SaleReservations.FirstOrDefaultAsync(r => r.Id == id); if (entity is null) return TypedResults.NotFound(); if (input.Status is not null) entity.Status = NormalizeStatus(input.Status); if (input.GerbilName is not null) entity.GerbilName = Trim(input.GerbilName); if (input.ContactName is not null) entity.ContactName = Trim(input.ContactName); if (input.Note is not null) entity.Note = Trim(input.Note); // Value-type/nullable fields are always applied from the payload (a null clears them). entity.ReservedForContactId = input.ReservedForContactId; entity.AppointmentDate = input.AppointmentDate; entity.Price = input.Price; entity.HandedOverDate = input.HandedOverDate; entity.UpdatedAt = DateTimeOffset.UtcNow; await db.SaveChangesAsync(); return TypedResults.Ok(ToDto(entity)); }); group.MapDelete("/{id:guid}", async Task> ( Guid id, ApplicationContext db) => { var entity = await db.SaleReservations.FirstOrDefaultAsync(r => r.Id == id); if (entity is null) return TypedResults.NotFound(); db.SaleReservations.Remove(entity); await db.SaveChangesAsync(); return TypedResults.NoContent(); }); return app; } private static string? Trim(string? s) => string.IsNullOrWhiteSpace(s) ? null : s.Trim(); private static SaleReservationDto ToDto(SaleReservation r) => new(r.Id, r.GerbilId, r.GerbilName, r.Status, r.ReservedForContactId, r.ContactName, r.AppointmentDate, r.Price, r.Note, r.HandedOverDate, r.CreatedAt, r.UpdatedAt); } }