using MaBibli.Client.Services;
using MaBibli.Shared.Dtos;
namespace MaBibli.Tests;
///
/// Regroupement des tomes d'une série au catalogue (2026-08-22).
///
///
/// L'écran n'est pas éprouvable ici, mais la règle l'est : ces tests portent sur ce qui a été
/// décidé — la place du bloc, l'ordre des tomes à l'intérieur, et le fait que rien ne
/// disparaisse ni ne se duplique.
///
public class GroupementCatalogueTests
{
private static LivreDto Livre(int id, string titre) =>
new()
{
Id = id,
Titre = titre,
Format = MaBibli.Shared.Entites.Format.Physique,
DateAjout = DateTime.UtcNow,
};
private static SerieDto Serie(int id, string titre, params (int Position, int LivreId)[] tomes) =>
new()
{
Id = id,
Titre = titre,
Elements =
[
.. tomes.Select(t => new ElementSerieDto
{
Id = t.LivreId,
Position = t.Position,
Titre = $"tome {t.Position}",
LivreId = t.LivreId,
}),
],
};
///
/// ⚠️ Le cas qui a tranché la décision : le bloc se pose à la place du PREMIER tome, pour
/// que l'ordre général du catalogue ne bouge pas.
///
[Fact]
public void Le_bloc_prend_la_place_de_son_premier_tome()
{
List catalogue =
[
Livre(1, "Dracula"),
Livre(2, "L'Éclat de cristal"),
Livre(3, "Dune"),
Livre(4, "Terre natale"),
];
var entrees = GroupementCatalogue.Grouper(
catalogue, [Serie(9, "La Légende de Drizzt", (1, 2), (0, 4))]);
Assert.Collection(
entrees,
e => Assert.Equal("Dracula", Assert.Single(e.Livres).Titre),
e =>
{
Assert.Equal("La Légende de Drizzt", e.Serie!.Titre);
// À l'intérieur, l'ordre est celui de LECTURE, pas celui du catalogue.
Assert.Equal(["Terre natale", "L'Éclat de cristal"], e.Livres.Select(l => l.Titre));
},
e => Assert.Equal("Dune", Assert.Single(e.Livres).Titre));
}
/// Aucun livre ne disparaît, aucun n'apparaît deux fois : c'est la règle du catalogue.
[Fact]
public void Le_regroupement_ne_perd_ni_ne_duplique_aucun_livre()
{
List catalogue = [Livre(1, "A"), Livre(2, "B"), Livre(3, "C")];
// Le livre 2 est rattaché à DEUX séries — le modèle l'autorise.
var entrees = GroupementCatalogue.Grouper(
catalogue, [Serie(1, "Zeta", (0, 2)), Serie(2, "Alpha", (0, 2), (1, 3))]);
Assert.Equal([1, 2, 3], entrees.SelectMany(e => e.Livres).Select(l => l.Id).Order());
}
///
/// ⚠️ La série retenue est la première par ordre alphabétique, et non celle que l'API a
/// rendue en premier : sans cela, le bloc changerait de nom d'un chargement à l'autre.
///
[Fact]
public void Un_livre_de_deux_series_est_range_sous_la_premiere_alphabetiquement()
{
var entrees = GroupementCatalogue.Grouper(
[Livre(2, "B")], [Serie(1, "Zeta", (0, 2)), Serie(2, "Alpha", (0, 2))]);
Assert.Equal("Alpha", Assert.Single(entrees).Serie!.Titre);
}
/// Sans série connue — hors-ligne sans instantané, ou groupement défait — la liste est plate.
[Fact]
public void Sans_series_la_liste_reste_celle_du_catalogue()
{
List catalogue = [Livre(1, "A"), Livre(2, "B")];
var entrees = GroupementCatalogue.Grouper(catalogue, null);
Assert.All(entrees, e => Assert.Null(e.Serie));
Assert.Equal(["A", "B"], entrees.Select(e => e.Livres[0].Titre));
}
///
/// Un seul tome possédé forme quand même un bloc : c'est ce qui dit à quoi ce livre
/// appartient, et ce qui mène à la série où figurent les tomes manquants.
///
[Fact]
public void Un_seul_tome_possede_forme_quand_meme_un_bloc()
{
var entrees = GroupementCatalogue.Grouper(
[Livre(4, "Terre natale")], [Serie(9, "L'Elfe noir", (0, 4), (1, 5))]);
Assert.Equal("L'Elfe noir", Assert.Single(entrees).Serie!.Titre);
}
///
/// Les tomes filtrés restent absents : le regroupement rassemble ce que la liste contient,
/// il ne va pas rechercher ce qu'un filtre a écarté.
///
[Fact]
public void Un_filtre_ne_ramene_pas_les_tomes_ecartes()
{
var entrees = GroupementCatalogue.Grouper(
[Livre(5, "L'Éclat de cristal")], [Serie(9, "Drizzt", (0, 4), (1, 5))]);
Assert.Equal(["L'Éclat de cristal"], Assert.Single(entrees).Livres.Select(l => l.Titre));
}
}