using MaBibli.Api.Services.Prets;
using MaBibli.Shared.Dtos;

namespace MaBibli.Api.Endpoints;

public static class PretsEndpoints
{
    /// <summary>
    /// Prêts : qui a quoi, depuis quand, et l'historique de ce qui est revenu.
    /// </summary>
    /// <remarks>
    /// <b>Aucun point d'entrée ne filtre sur l'utilisateur courant</b>, et aucun n'enregistre qui
    /// a saisi le prêt. La bibliothèque est commune : tout le foyer voit les mêmes prêts, et
    /// n'importe qui peut noter le retour d'un livre qu'il a récupéré.
    /// <para>
    /// L'emprunteur est du <b>texte libre</b> : ce sont des personnes extérieures au foyer, sans
    /// compte YunoHost. Ne jamais le rapprocher d'un identifiant d'utilisateur.
    /// </para>
    /// </remarks>
    public static IEndpointRouteBuilder MapPretsEndpoints(this IEndpointRouteBuilder routes)
    {
        var prets = routes.MapGroup("/api/prets").WithTags("Prêts");

        prets.MapGet("/en-cours", async (IServicePrets service, CancellationToken ct) =>
                Results.Ok(await service.EnCoursAsync(ct)))
            .WithName("ListerPretsEnCours")
            .WithSummary("Les livres qui ne sont pas à la maison, du prêt le plus ancien au plus récent.")
            .WithDescription(
                "L'ordre est volontairement croissant : ce qu'on cherche ici, c'est le livre "
                + "sorti depuis des mois qu'on avait oublié, pas celui prêté hier.")
            .Produces<IReadOnlyList<PretDto>>();

        prets.MapPost("/{id:int}/retour", async (
                int id,
                RetourPret retour,
                IServicePrets service,
                CancellationToken ct) =>
            {
                var resultat = await service.RendreAsync(id, retour, ct);

                if (resultat.EstIntrouvable)
                {
                    return Results.NotFound();
                }

                return resultat.Erreur is not null
                    ? Results.BadRequest(new { erreur = resultat.Erreur })
                    : Results.Ok(resultat.Pret);
            })
            .WithName("MarquerPretRendu")
            .WithSummary("Clôt un prêt en posant sa date de retour. Le prêt n'est pas supprimé : il devient l'historique.")
            .Produces<PretDto>()
            .Produces(StatusCodes.Status400BadRequest)
            .Produces(StatusCodes.Status404NotFound);

        var parLivre = routes.MapGroup("/api/livres/{livreId:int}/prets").WithTags("Prêts");

        parLivre.MapGet("/", async (int livreId, IServicePrets service, CancellationToken ct) =>
            {
                var historique = await service.HistoriqueAsync(livreId, ct);
                return historique is null ? Results.NotFound() : Results.Ok(historique);
            })
            .WithName("HistoriquePretsLivre")
            .WithSummary("Tous les prêts de ce livre, clos comme en cours, du plus récent au plus ancien.")
            .WithDescription(
                "Une liste vide signifie « jamais prêté » ; un livre inconnu répond 404. "
                + "Les prêts clos ne disparaissent jamais : c'est la raison d'être de la table.")
            .Produces<IReadOnlyList<PretDto>>()
            .Produces(StatusCodes.Status404NotFound);

        parLivre.MapPost("/", async (
                int livreId,
                EnregistrementPret saisie,
                IServicePrets service,
                CancellationToken ct) =>
            {
                var resultat = await service.PreterAsync(livreId, saisie, ct);

                if (resultat.EstIntrouvable)
                {
                    return Results.NotFound();
                }

                return resultat.Erreur is not null
                    ? Results.BadRequest(new { erreur = resultat.Erreur })
                    : Results.Created($"/api/prets/{resultat.Pret!.Id}", resultat.Pret);
            })
            .WithName("PreterLivre")
            .WithSummary("Prête ce livre à quelqu'un.")
            .WithDescription(
                "Refusé si le livre est déjà sorti (un exemplaire ne se prête pas deux fois) ou "
                + "s'il est au format numérique (une fiche d'ebook n'a pas d'exemplaire à confier).")
            .Produces<PretDto>(StatusCodes.Status201Created)
            .Produces(StatusCodes.Status400BadRequest)
            .Produces(StatusCodes.Status404NotFound);

        return routes;
    }
}
