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/{id}
            group.MapGet("/{id:guid}", async Task<Results<Ok<GerbilDto>, 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<Results<Created<GerbilDto>, ValidationProblem>> (GerbilInput input, ApplicationContext db) =>
            {
                if (string.IsNullOrWhiteSpace(input.Name))
                    return TypedResults.ValidationProblem(new Dictionary<string, string[]> { ["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<Results<NoContent, NotFound>> (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<Results<NoContent, NotFound, Conflict<string>>> (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;
        }

        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);
    }
}
