using MaBibli.Api.Services.Identite;
using MaBibli.Api.Services.Revues;
using MaBibli.Shared.Dtos;

namespace MaBibli.Api.Endpoints;

public static class RevuesEndpoints
{
    /// <summary>
    /// Revues et magazines : une fiche par revue, les numéros à l'intérieur.
    /// </summary>
    /// <remarks>
    /// <b>Communes au foyer</b>, comme le catalogue : aucun point d'entrée ne prend d'identité,
    /// et « ajoutePar » n'est qu'une trace. La lecture est unique — <c>GET /api/revues</c> rend
    /// les revues avec leurs numéros, ce qui donne un seul instantané hors-ligne.
    /// </remarks>
    public static IEndpointRouteBuilder MapRevuesEndpoints(this IEndpointRouteBuilder routes)
    {
        var groupe = routes.MapGroup("/api/revues").WithTags("Revues");

        groupe.MapGet("/", async (IServiceRevues service, CancellationToken ct) =>
                Results.Ok(await service.ListerAsync(ct)))
            .WithName("ListerRevues")
            .WithSummary("Toutes les revues, avec les numéros possédés, du plus récent au plus ancien.")
            .Produces<IReadOnlyList<RevueDto>>();

        groupe.MapPost("/", async (
                EnregistrementRevue saisie,
                IServiceRevues service,
                IFournisseurUtilisateur utilisateurs,
                CancellationToken ct) =>
            {
                var resultat = await service.CreerOuRetrouverAsync(
                    saisie, utilisateurs.Obtenir().Identifiant, ct);

                return resultat.Erreur is not null
                    ? Results.BadRequest(new { erreur = resultat.Erreur })
                    : Results.Ok(resultat.Revue);
            })
            .WithName("CreerRevue")
            .WithSummary("Crée la revue, ou rend celle qui existe déjà (même ISSN, ou même titre).")
            .WithDescription(
                "« Créer ou retrouver » et non « créer » : scanner le code-barres d'un second "
                + "numéro retombe forcément sur la même revue, et le geste de l'utilisateur est "
                + "toujours « voici un numéro de plus ».")
            .Produces<RevueDto>()
            .Produces(StatusCodes.Status400BadRequest);

        groupe.MapPut("/{id:int}", async (
                int id, EnregistrementRevue saisie, IServiceRevues service, CancellationToken ct) =>
                Repondre(await service.ModifierAsync(id, saisie, ct)))
            .WithName("ModifierRevue")
            .Produces<RevueDto>()
            .Produces(StatusCodes.Status400BadRequest)
            .Produces(StatusCodes.Status404NotFound);

        groupe.MapDelete("/{id:int}", async (int id, IServiceRevues service, CancellationToken ct) =>
                await service.SupprimerAsync(id, ct) ? Results.NoContent() : Results.NotFound())
            .WithName("SupprimerRevue")
            .WithSummary("Supprime la revue et les numéros qu'elle porte : ils n'existent que par elle.")
            .Produces(StatusCodes.Status204NoContent)
            .Produces(StatusCodes.Status404NotFound);

        groupe.MapPost("/{id:int}/numeros", async (
                int id, AjoutNumeroRevue saisie, IServiceRevues service, CancellationToken ct) =>
                Repondre(await service.AjouterNumeroAsync(id, saisie, ct)))
            .WithName("AjouterNumeroRevue")
            .WithSummary("Ajoute un numéro possédé. Le numéro est obligatoire et unique dans la revue.")
            .Produces<RevueDto>()
            .Produces(StatusCodes.Status400BadRequest)
            .Produces(StatusCodes.Status404NotFound);

        groupe.MapPut("/numeros/{numeroId:int}", async (
                int numeroId, AjoutNumeroRevue saisie, IServiceRevues service, CancellationToken ct) =>
                Repondre(await service.ModifierNumeroAsync(numeroId, saisie, ct)))
            .WithName("ModifierNumeroRevue")
            .WithSummary("Modifie un numéro : numéro, parution, note, couverture et articles à la une.")
            .WithDescription(
                "La couverture d'un numéro et sa une ne viennent d'aucune source — l'ISSN "
                + "désigne la revue, pas la parution — et se saisissent donc après coup.")
            .Produces<RevueDto>()
            .Produces(StatusCodes.Status400BadRequest)
            .Produces(StatusCodes.Status404NotFound);

        groupe.MapDelete("/numeros/{numeroId:int}", async (
                int numeroId, IServiceRevues service, CancellationToken ct) =>
                await service.RetirerNumeroAsync(numeroId, ct)
                    ? Results.NoContent()
                    : Results.NotFound())
            .WithName("RetirerNumeroRevue")
            .Produces(StatusCodes.Status204NoContent)
            .Produces(StatusCodes.Status404NotFound);

        return routes;
    }

    private static IResult Repondre(ResultatRevue resultat)
    {
        if (resultat.EstIntrouvable)
        {
            return Results.NotFound();
        }

        return resultat.Erreur is not null
            ? Results.BadRequest(new { erreur = resultat.Erreur })
            : Results.Ok(resultat.Revue);
    }
}
