namespace MaBibli.Shared.Textes;

/// <summary>
/// Clé d'identification d'une <b>œuvre</b> à partir d'un titre, indépendamment de l'édition.
/// </summary>
/// <remarks>
/// C'est le rapprochement le moins mauvais dont on dispose, et il faut être clair sur ses limites.
/// <para>
/// <b>Pourquoi pas l'ISBN ?</b> Parce qu'il désigne une <i>édition</i>, pas une œuvre : le poche
/// de 1998, le grand format de 1991 et la réédition de 2015 portent trois ISBN différents pour le
/// même roman. Mesuré sur la bibliographie BnF de Bernard Werber : 183 notices de livres pour
/// 51 œuvres, chacune revenant en moyenne trois fois sous trois ISBN. Comparer les ISBN
/// répondrait donc « vous ne l'avez pas » à propos d'un livre posé sur l'étagère, ce qui est le
/// pire résultat possible pour une liste d'envies.
/// </para>
/// <para>
/// <b>Ce que la clé fait.</b> Elle coupe la mention de responsabilité ISBD (« <c> / </c> ») puis
/// le sous-titre (« <c> : </c> »), et met le reste à plat. C'est ce qui réunit
/// « Les thanatonautes », « Les thanatonautes : roman » et
/// « Les thanatonautes / Bernard Werber ».
/// </para>
/// <para>
/// <b>Ce qu'elle rate</b>, et qu'il ne faut pas prétendre autrement :
/// <list type="bullet">
/// <item>Deux œuvres distinctes partageant un titre principal sont confondues (« Œuvres »,
/// « Nouvelles »). Le risque est borné : la comparaison n'a lieu qu'<b>à auteur donné</b>.</item>
/// <item>Un tome n'est pas rapproché de son recueil : « Troisième humanité » et
/// « Troisième humanité. Tome 1 » restent deux lignes. Le point n'est pas coupé, faute de savoir
/// distinguer « Tome 1 » d'un titre qui contient légitimement un point.</item>
/// <item>Un titre retraduit ou changé au fil des rééditions n'est pas rapproché du tout.</item>
/// <item>Une œuvre possédée à l'intérieur d'une intégrale n'est pas repérée : l'intégrale a son
/// propre titre.</item>
/// </list>
/// Conséquence assumée : l'écran de bibliographie peut afficher comme « à découvrir » un livre
/// déjà présent sous un autre titre. Il ne fait donc que <b>griser</b> ce qu'il reconnaît, sans
/// jamais masquer une ligne — un faux négatif se voit et se corrige d'un coup d'œil, alors qu'une
/// ligne masquée à tort serait invisible.
/// </para>
/// </remarks>
public static class CleOeuvre
{
    /// <summary>Sépare le titre de la mention de responsabilité : « Germinal / Émile Zola ».</summary>
    private const string SeparateurResponsabilite = " / ";

    /// <summary>Sépare le titre de son sous-titre : « Les thanatonautes : roman ».</summary>
    private const string SeparateurSousTitre = " : ";

    /// <summary>
    /// Clé comparable d'un titre : « Les thanatonautes : roman » → <c>les thanatonautes</c>.
    /// </summary>
    public static string Cle(string? titre)
    {
        var s = titre?.Trim();
        if (string.IsNullOrEmpty(s))
        {
            return string.Empty;
        }

        s = Tronquer(s, SeparateurResponsabilite);
        var sansSousTitre = Tronquer(s, SeparateurSousTitre);

        // Un titre qui n'est QUE son sous-titre (« : roman ») ne doit pas se réduire à rien :
        // mieux vaut alors garder la chaîne entière que produire une clé vide, qui se
        // confondrait avec toutes les autres clés vides.
        var retenu = NormalisationTexte.Normaliser(sansSousTitre);
        return retenu.Length > 0 ? retenu : NormalisationTexte.Normaliser(s);
    }

    private static string Tronquer(string s, string separateur)
    {
        var i = s.IndexOf(separateur, StringComparison.Ordinal);
        return i >= 0 ? s[..i].Trim() : s;
    }
}
