using MaBibli.Shared.Dtos;

namespace MaBibli.Api.Services.Identite;

public interface IFournisseurUtilisateur
{
    /// <summary>Identité de la requête en cours, jamais <c>null</c> (au pire <see cref="UtilisateurCourant.Anonyme"/>).</summary>
    UtilisateurCourant Obtenir();
}

/// <summary>
/// Options de l'identité, section <c>Identite</c> de la configuration.
/// </summary>
public sealed class OptionsIdentite
{
    public const string Section = "Identite";

    /// <summary>
    /// Utilisateur simulé en développement, où SSOwat n'existe pas.
    /// </summary>
    /// <remarks>
    /// CLAUDE.md : « prévoir un utilisateur simulé plutôt que de désactiver l'auth ».
    /// Il n'est utilisé que si les en-têtes SSOwat sont absents, et n'est renseigné que dans
    /// <c>appsettings.Development.json</c> — en production, aucune valeur, donc aucun repli.
    /// </remarks>
    public string? UtilisateurSimule { get; set; }

    public string? EmailSimule { get; set; }

    public string? NomCompletSimule { get; set; }
}

/// <summary>
/// Lit l'identité dans les en-têtes HTTP injectés par SSOwat.
/// </summary>
/// <remarks>
/// nginx authentifie le visiteur via le portail YunoHost puis pousse <c>YNH_USER</c>,
/// <c>YNH_USER_EMAIL</c> et <c>YNH_USER_FULLNAME</c>. L'application se contente de les lire.
/// <para>
/// ⚠️ Ces en-têtes ne sont dignes de confiance que parce que le service n'écoute que sur
/// <c>127.0.0.1</c> derrière nginx : exposé directement, n'importe qui pourrait forger
/// <c>YNH_USER</c>. Cette contrainte se règle dans <c>systemd.service</c> / <c>nginx.conf</c>,
/// qui vivent dans le dépôt <c>mabibli_ynh</c> (phase 7) — pas ici.
/// </para>
/// </remarks>
public sealed class FournisseurUtilisateurSsowat(
    IHttpContextAccessor accesseur,
    Microsoft.Extensions.Options.IOptions<OptionsIdentite> options) : IFournisseurUtilisateur
{
    public const string EnteteUtilisateur = "YNH_USER";
    public const string EnteteEmail = "YNH_USER_EMAIL";
    public const string EnteteNomComplet = "YNH_USER_FULLNAME";

    public UtilisateurCourant Obtenir()
    {
        var entetes = accesseur.HttpContext?.Request.Headers;
        var identifiant = Valeur(entetes, EnteteUtilisateur);

        if (identifiant is not null)
        {
            return new UtilisateurCourant
            {
                Identifiant = identifiant,
                Email = Valeur(entetes, EnteteEmail),
                NomComplet = Valeur(entetes, EnteteNomComplet),
                Simule = false,
            };
        }

        var simule = options.Value;
        if (!string.IsNullOrWhiteSpace(simule.UtilisateurSimule))
        {
            return new UtilisateurCourant
            {
                Identifiant = simule.UtilisateurSimule,
                Email = simule.EmailSimule,
                NomComplet = simule.NomCompletSimule,
                Simule = true,
            };
        }

        // Ni en-tête, ni utilisateur simulé : on n'invente pas d'identité.
        // Le livre sera enregistré sans traçabilité plutôt que sous un faux nom.
        return UtilisateurCourant.Anonyme;
    }

    private static string? Valeur(IHeaderDictionary? entetes, string nom)
    {
        if (entetes is null || !entetes.TryGetValue(nom, out var valeurs))
        {
            return null;
        }

        var valeur = valeurs.ToString().Trim();
        return valeur.Length == 0 ? null : valeur;
    }
}
