using MaBibli.Shared.Dtos;
using MaBibli.Shared.Isbn;

namespace MaBibli.Api.Services.Isbn;

public interface IIsbnLookupService
{
    /// <summary>
    /// Cherche toutes les notices correspondant à un ISBN.
    /// </summary>
    /// <returns><c>null</c> si l'ISBN fourni n'est pas un ISBN valide.</returns>
    Task<ResultatLookupIsbn?> RechercherAsync(string isbnBrut, CancellationToken ct = default);
}

/// <summary>
/// Cascade de lookup ISBN : <b>BnF d'abord, OpenLibrary ensuite</b>.
/// </summary>
/// <remarks>
/// L'ordre est dicté par CLAUDE.md : la collection est majoritairement francophone et le dépôt
/// légal français donne à la BnF la meilleure couverture possible sur ce fonds, là où OpenLibrary
/// est lacunaire. OpenLibrary n'est interrogée que si la BnF ne renvoie rien sous
/// <b>aucune</b> des deux formes d'ISBN.
/// </remarks>
public sealed class IsbnLookupService(
    IBnfClient bnf,
    IOpenLibraryClient openLibrary,
    ILogger<IsbnLookupService> logger) : IIsbnLookupService
{
    public async Task<ResultatLookupIsbn?> RechercherAsync(string isbnBrut, CancellationToken ct = default)
    {
        var isbn = IsbnUtils.Normaliser(isbnBrut);
        if (!IsbnUtils.EstValide(isbn))
        {
            return null;
        }

        // La couverture ne vient jamais de la BnF : son Dublin Core n'en fournit aucune.
        // On construit l'URL OpenLibrary quelle que soit la source des métadonnées.
        var couverture = IsbnUtils.UrlCouverture(isbn!);

        IsbnUtils.TryConvertirEnIsbn10(isbn, out var isbn10);

        var avertissements = new List<string>();

        // 1. BnF avec l'ISBN tel que scanné.
        var (candidats, avertissement) = await bnf.RechercherAsync(isbn!, couverture, ct);
        Ajouter(avertissements, avertissement);

        // 2. BnF avec l'ISBN-10 converti. Indispensable : la BnF indexe l'ISBN tel qu'imprimé,
        //    et les ouvrages d'avant 2007 ne portent qu'un ISBN-10.
        if (candidats.Count == 0 && isbn10 is not null)
        {
            var (candidats10, avertissement10) = await bnf.RechercherAsync(isbn10, couverture, ct);
            Ajouter(avertissements, avertissement10);
            candidats = candidats10;
        }

        // 3. OpenLibrary en second rideau, seulement si la BnF n'a rien donné du tout.
        if (candidats.Count == 0)
        {
            var (candidatsOl, avertissementOl) = await openLibrary.RechercherAsync(isbn!, couverture, ct);
            Ajouter(avertissements, avertissementOl);
            candidats = candidatsOl;
        }

        logger.LogInformation(
            "Lookup ISBN {Isbn} : {Nombre} candidat(s), {Avertissements} avertissement(s)",
            isbn, candidats.Count, avertissements.Count);

        return new ResultatLookupIsbn
        {
            IsbnDemande = isbn!,
            Isbn10 = isbn10,
            Candidats = candidats,
            Avertissements = avertissements,
        };
    }

    private static void Ajouter(List<string> avertissements, string? message)
    {
        if (!string.IsNullOrEmpty(message))
        {
            avertissements.Add(message);
        }
    }
}
