using MaBibli.Shared.Dtos;

namespace MaBibli.Api.Services.Isbn;

/// <summary>
/// Transformation d'une édition OpenLibrary en candidat. Fonctions pures, testables sans réseau.
/// </summary>
public static class OpenLibraryMapper
{
    /// <summary>
    /// Clés d'auteurs portées par l'édition elle-même (<c>/authors/OL…A</c>), éventuellement vide.
    /// </summary>
    /// <remarks>
    /// Ces clés ne sont <b>pas</b> des noms : chacune impose un appel à
    /// <c>/authors/{id}.json</c>. Oublier ce second appel donne un titre rempli et un auteur vide
    /// — symptôme exact observé chez BookLogr.
    /// </remarks>
    public static IReadOnlyList<string> ClesAuteurs(OpenLibraryEdition edition) =>
        edition.Authors?
            .Select(a => a.Key)
            .Where(k => !string.IsNullOrWhiteSpace(k))
            .Select(k => k!)
            .ToList() ?? [];

    /// <summary>Clés d'auteurs portées par l'œuvre, utilisées en repli.</summary>
    public static IReadOnlyList<string> ClesAuteurs(OpenLibraryWork work) =>
        work.Authors?
            .Select(a => a.Author?.Key)
            .Where(k => !string.IsNullOrWhiteSpace(k))
            .Select(k => k!)
            .ToList() ?? [];

    /// <summary>Clé de l'œuvre rattachée (<c>/works/OL…W</c>), ou <c>null</c>.</summary>
    public static string? CleOeuvre(OpenLibraryEdition edition) =>
        edition.Works?.Select(w => w.Key).FirstOrDefault(k => !string.IsNullOrWhiteSpace(k));

    /// <summary>Nom affichable d'un auteur OpenLibrary.</summary>
    public static string? Nom(OpenLibraryAuthor auteur)
    {
        var nom = auteur.Name ?? auteur.PersonalName;
        return string.IsNullOrWhiteSpace(nom) ? null : nom.Trim();
    }

    /// <summary>
    /// Assemble le candidat final. <paramref name="nomsAuteurs"/> vient des appels
    /// <c>/authors/{id}.json</c> déjà effectués par le client.
    /// </summary>
    public static CandidatLivre? VersCandidat(
        OpenLibraryEdition edition,
        IReadOnlyList<string> nomsAuteurs,
        string isbnInterroge,
        string? urlCouverture)
    {
        var titre = edition.Title?.Trim();
        if (string.IsNullOrEmpty(titre))
        {
            return null;
        }

        if (!string.IsNullOrWhiteSpace(edition.Subtitle))
        {
            titre = $"{titre} : {edition.Subtitle.Trim()}";
        }

        // Les noms résolus par /authors/{id}.json arrivent déjà séparés. En dernier repli la
        // mention de responsabilité n'est qu'une phrase : on la garde entière plutôt que de
        // parier sur ses virgules, qui séparent aussi bien deux auteurs qu'un « Zola, Émile ».
        var auteurs = nomsAuteurs.Count > 0
            ? nomsAuteurs
            : Liste(NettoyerByStatement(edition.ByStatement));

        return new CandidatLivre
        {
            Titre = titre,
            Auteurs = auteurs,
            Editeur = edition.Publishers?.FirstOrDefault()?.Trim().Trim('[', ']'),
            Annee = string.IsNullOrWhiteSpace(edition.PublishDate) ? null : edition.PublishDate.Trim(),
            Langue = edition.Languages?
                .Select(l => l.Key)
                .FirstOrDefault(k => !string.IsNullOrWhiteSpace(k))?
                .Replace("/languages/", string.Empty),
            CoverUrl = urlCouverture,
            Source = SourceMetadonnees.OpenLibrary,
            IsbnInterroge = isbnInterroge,
            IdentifiantSource = edition.Key,
        };
    }

    private static IReadOnlyList<string> Liste(string? valeur) =>
        string.IsNullOrWhiteSpace(valeur) ? [] : [valeur];

    /// <summary>
    /// Dernier repli quand aucun auteur n'est résolvable : la mention de responsabilité brute,
    /// débarrassée de sa ponctuation finale (« Thomas H. Cormen ... [et al.]. »).
    /// </summary>
    private static string? NettoyerByStatement(string? brut)
    {
        var s = brut?.Trim();
        if (string.IsNullOrEmpty(s))
        {
            return null;
        }

        var i = s.IndexOf(" ... ", StringComparison.Ordinal);
        if (i >= 0)
        {
            s = s[..i];
        }

        s = s.TrimEnd('.', ' ', ',');
        return s.Length == 0 ? null : s;
    }
}
