using MaBibli.Shared.Dtos;

namespace MaBibli.Api.Services.Isbn;

public interface IBnfClient
{
    /// <summary>
    /// Interroge le SRU de la BnF pour une forme d'ISBN donnée.
    /// Renvoie une liste vide si la BnF ne connaît pas l'ISBN <b>ou</b> si elle est injoignable ;
    /// dans ce second cas <paramref name="avertissement"/> explique pourquoi.
    /// </summary>
    Task<(IReadOnlyList<CandidatLivre> Candidats, string? Avertissement)> RechercherAsync(
        string isbn, string? urlCouverture, CancellationToken ct = default);
}

/// <summary>
/// Client de l'API SRU du catalogue général de la BnF (gratuite, sans clé).
/// </summary>
public sealed class BnfClient(HttpClient http, ILogger<BnfClient> logger) : IBnfClient
{
    /// <summary>Nom du client typé enregistré dans <c>IHttpClientFactory</c>.</summary>
    public const string NomHttpClient = "bnf";

    public const string UrlBase = "https://catalogue.bnf.fr/";

    public async Task<(IReadOnlyList<CandidatLivre> Candidats, string? Avertissement)> RechercherAsync(
        string isbn, string? urlCouverture, CancellationToken ct = default)
    {
        // recordSchema=dublincore et non MARC : dc:title / dc:creator / dc:publisher se mappent
        // directement, là où l'UNIMARC demanderait un décodage complet.
        var url = "api/SRU"
                  + "?version=1.2"
                  + "&operation=searchRetrieve"
                  + $"&query={Uri.EscapeDataString($"bib.isbn all \"{isbn}\"")}"
                  + "&recordSchema=dublincore"
                  + "&maximumRecords=5";

        try
        {
            using var reponse = await http.GetAsync(url, ct);
            if (!reponse.IsSuccessStatusCode)
            {
                logger.LogWarning("BnF a répondu {Code} pour l'ISBN {Isbn}", (int)reponse.StatusCode, isbn);
                return ([], $"La BnF a répondu {(int)reponse.StatusCode} pour {isbn}.");
            }

            var xml = await reponse.Content.ReadAsStringAsync(ct);
            return (BnfSruParser.Parser(xml, isbn, urlCouverture), null);
        }
        catch (Exception ex) when (ex is HttpRequestException or TaskCanceledException)
        {
            // Une source indisponible ne doit pas faire échouer la cascade : on bascule sur la suivante.
            logger.LogWarning(ex, "BnF injoignable pour l'ISBN {Isbn}", isbn);
            return ([], $"BnF injoignable ({ex.GetType().Name}) pour {isbn}.");
        }
        catch (System.Xml.XmlException ex)
        {
            logger.LogWarning(ex, "Réponse BnF illisible pour l'ISBN {Isbn}", isbn);
            return ([], $"Réponse BnF illisible pour {isbn}.");
        }
    }
}
