namespace MaBibli.Shared.Isbn;

/// <summary>
/// Mise en forme d'un ISSN : quatre chiffres, un tiret, quatre caractères (<c>2466-6718</c>).
/// </summary>
/// <remarks>
/// <b>Bien plus simple que <see cref="FormatageIsbn"/></b>, et ce n'est pas un hasard : un ISSN
/// se coupe <i>toujours</i> au même endroit, alors que les tranches d'un ISBN dépendent du
/// groupe linguistique puis de l'éditeur. Il n'y a donc aucune table à embarquer, et aucune
/// coupure ne peut être fausse.
/// <para>
/// ⚠️ <b>La forme à tiret est la forme canonique, y compris en base.</b> C'est le seul point où
/// ce fichier s'écarte de la règle « la valeur stockée reste nue » tenue pour l'ISBN, et il y a
/// une raison précise : <see cref="CodePeriodique.IssnDepuis"/> produit déjà un ISSN à tiret
/// depuis le code-barres, et la BnF interroge <c>bib.issn</c> avec le tiret. Un ISSN saisi à la
/// main sans tiret ne se serait donc rapproché de rien — ni de la revue déjà créée par un scan,
/// ni d'une notice BnF.
/// </para>
/// <para>
/// Les champs de <b>saisie</b> gardent en revanche ce que l'utilisateur tape : la canonisation a
/// lieu à la validation, jamais à la frappe, sans quoi le tiret se battrait avec le curseur.
/// </para>
/// </remarks>
public static class FormatageIssn
{
    /// <summary>Nombre de caractères d'un ISSN, tiret non compris.</summary>
    private const int Longueur = 8;

    /// <summary>
    /// Les huit caractères de l'ISSN, sans tiret ni espace, ou <c>null</c> si la saisie n'en
    /// est pas un.
    /// </summary>
    /// <remarks>
    /// La dernière position peut valoir <c>X</c> : c'est une clé modulo 11, comme celle d'un
    /// ISBN-10. Elle n'est <b>pas vérifiée</b> ici — un ISSN mal recopié doit pouvoir être
    /// enregistré et corrigé, pas refusé au motif d'un chiffre.
    /// </remarks>
    public static string? Normaliser(string? saisie)
    {
        if (string.IsNullOrWhiteSpace(saisie))
        {
            return null;
        }

        Span<char> tampon = stackalloc char[Longueur];
        var ecrits = 0;

        foreach (var caractere in saisie)
        {
            if (char.IsWhiteSpace(caractere) || caractere is '-' or '–')
            {
                continue;
            }

            if (ecrits == Longueur)
            {
                return null;
            }

            var majuscule = char.ToUpperInvariant(caractere);
            var valide = char.IsAsciiDigit(majuscule) || (majuscule == 'X' && ecrits == Longueur - 1);

            if (!valide)
            {
                return null;
            }

            tampon[ecrits++] = majuscule;
        }

        return ecrits == Longueur ? new string(tampon) : null;
    }

    /// <summary>
    /// Forme d'affichage à tiret. Une saisie qui n'est pas un ISSN est rendue <b>telle quelle</b> :
    /// un code mal recopié doit se voir mal recopié, pas déguisé par un tiret.
    /// </summary>
    public static string? Afficher(string? valeur)
    {
        if (Normaliser(valeur) is not { } nu)
        {
            return valeur;
        }

        return $"{nu[..4]}-{nu[4..]}";
    }

    /// <summary>
    /// Forme à ranger en base : le tiret quand c'est un ISSN, la saisie élaguée sinon.
    /// </summary>
    public static string? Canonique(string? valeur) =>
        string.IsNullOrWhiteSpace(valeur) ? null : Afficher(valeur.Trim());
}
