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

namespace GerbilManagerWebAPI.Endpoints
{
    /// <summary>
    /// 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.
    /// </summary>
    public static class SaleReservationEndpoints
    {
        private static readonly string[] AllowedStatuses = { "verfuegbar", "reserviert", "abgegeben" };

        /// <summary>Normalize a status string to one of the three canonical values; default "verfuegbar".</summary>
        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<Results<Created<SaleReservationDto>, BadRequest<string>>> (
                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<Results<Ok<SaleReservationDto>, 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<Results<NoContent, NotFound>> (
                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);
    }
}
