namespace MaBibli.Shared.Dtos;

/// <summary>
/// Une série telle qu'exposée par l'API — trilogie, cycle, intégrale.
/// </summary>
/// <remarks>
/// <b>La liste est rendue à plat</b>, chaque série portant l'identifiant de sa parente, et c'est
/// le client qui rebâtit l'arbre. Deux raisons : un JSON récursif se cache mal dans un
/// instantané hors-ligne, et une seule lecture suffit alors à servir la liste <i>et</i> le
/// détail — donc un seul instantané à tenir à jour.
/// </remarks>
public record SerieDto
{
    public required int Id { get; init; }

    public required string Titre { get; init; }

    /// <summary>La série qui contient celle-ci, si elle fait partie d'un cycle.</summary>
    public int? SerieParenteId { get; init; }

    /// <summary>Rang dans la série parente. Sans objet à la racine.</summary>
    public int Position { get; init; }

    /// <summary>Les tomes, dans l'ordre de <b>lecture</b>.</summary>
    public IReadOnlyList<ElementSerieDto> Elements { get; init; } = [];

    /// <summary>Qui a créé la série. Trace d'affichage : la série est commune au foyer.</summary>
    public string? AjoutePar { get; init; }

    /// <summary>Tomes possédés sur tomes recensés — « 4 sur 7 », le cœur de l'écran.</summary>
    public int NombrePossedes => Elements.Count(e => e.Possede);
}

/// <summary>
/// Une place dans l'ordre de lecture, occupée ou non.
/// </summary>
/// <remarks>
/// ⚠️ Un élément sans <see cref="LivreId"/> n'est <b>pas</b> une anomalie : c'est un tome qu'on
/// ne possède pas, et c'est précisément ce que l'écran doit montrer.
/// </remarks>
public record ElementSerieDto
{
    public required int Id { get; init; }

    public int Position { get; init; }

    /// <summary>Titre du tome : celui du livre quand il est rattaché, sinon celui qui a été saisi.</summary>
    public required string Titre { get; init; }

    public int? LivreId { get; init; }

    public bool Possede => LivreId is not null;

    /// <summary>Auteurs du livre rattaché, pour ne pas avoir à ouvrir sa fiche.</summary>
    public string? Auteurs { get; init; }

    /// <summary>À qui le tome est prêté, s'il est dehors. Commun au foyer, comme tout prêt.</summary>
    public string? PreteA { get; init; }
}

/// <summary>Charge utile de création ou de renommage d'une série.</summary>
public record EnregistrementSerie
{
    public string Titre { get; set; } = string.Empty;

    /// <summary>Série parente, pour ranger une trilogie dans un cycle. <c>null</c> = à la racine.</summary>
    public int? SerieParenteId { get; set; }
}

/// <summary>
/// Charge utile d'ajout d'un tome : soit un livre du catalogue, soit un simple titre.
/// </summary>
/// <remarks>
/// Les deux voies existent parce qu'une saga se recense d'un coup — souvent avant d'en posséder
/// la moitié — puis se remplit au fil des achats.
/// </remarks>
public record AjoutElementSerie
{
    /// <summary>Livre du catalogue à placer ici. <c>null</c> = un tome qu'on ne possède pas.</summary>
    public int? LivreId { get; set; }

    /// <summary>Titre du tome. Obligatoire sans livre ; repris du livre sinon.</summary>
    public string? Titre { get; set; }
}

/// <summary>Nouvel ordre de lecture : la liste <b>entière</b> des identifiants d'éléments.</summary>
/// <remarks>
/// Comme pour la liste d'envies, on prend la liste complète plutôt qu'un déplacement unitaire :
/// une seule opération à vérifier, et c'est ce dont a besoin le glisser-déposer comme les
/// flèches.
/// </remarks>
public record OrdreElementsSerie
{
    public List<int> Ids { get; set; } = [];
}

/// <summary>Nouvel ordre des séries directement contenues dans un cycle.</summary>
public record OrdreSeries
{
    public List<int> Ids { get; set; } = [];
}

/// <summary>Déplacement d'une série dans l'ordre de lecture de son cycle.</summary>
public readonly record struct DeplacementSerie(int SerieId, int ParenteId, int Delta);
