using Microsoft.AspNetCore.Http;
using ZB.MOM.WW.Auth.AspNetCore;

namespace ZB.MOM.WW.OtOpcUa.Security.Audit;

/// <summary>
/// HTTP-context–backed <see cref="IAuditActorAccessor"/> for the OtOpcUa control-plane.
/// </summary>
/// <remarks>
/// Reads the authenticated principal from <see cref="IHttpContextAccessor"/>:
/// <list type="number">
///   <item>If there is no current <c>HttpContext</c> or the user is not authenticated,
///   returns <see langword="null"/>.</item>
///   <item>Otherwise, returns the <see cref="ZbClaimTypes.Username"/> claim value (the
///   canonical directory login name set at sign-in by <c>AuthEndpoints</c>).</item>
///   <item>Falls back to the <see cref="ZbClaimTypes.Name"/> claim, then to
///   <see cref="System.Security.Principal.IIdentity.Name"/>, in that order.</item>
/// </list>
/// <para>
/// Registered as <b>scoped</b> in <see cref="ZB.MOM.WW.OtOpcUa.Security.ServiceCollectionExtensions.AddOtOpcUaAuth"/>
/// so that it correctly follows the request scope used by Blazor Server interactive components
/// and minimal-API endpoints. <c>IHttpContextAccessor</c> is registered by
/// <c>AddOtOpcUaAuth</c> via <c>services.AddHttpContextAccessor()</c>.
/// </para>
/// </remarks>
public sealed class HttpAuditActorAccessor : IAuditActorAccessor
{
    private readonly IHttpContextAccessor _httpContextAccessor;

    /// <summary>Initializes the accessor with the ASP.NET Core HTTP context accessor.</summary>
    /// <param name="httpContextAccessor">The HTTP context accessor.</param>
    public HttpAuditActorAccessor(IHttpContextAccessor httpContextAccessor)
    {
        _httpContextAccessor = httpContextAccessor;
    }

    /// <inheritdoc />
    public string? CurrentActor
    {
        get
        {
            var user = _httpContextAccessor.HttpContext?.User;
            if (user?.Identity?.IsAuthenticated != true)
                return null;

            // Prefer the canonical login-name claim; fall back to the Name claim or
            // Identity.Name (both of which map to ClaimTypes.Name / ZbClaimTypes.Name).
            return user.FindFirst(ZbClaimTypes.Username)?.Value
                   ?? user.FindFirst(ZbClaimTypes.Name)?.Value
                   ?? user.Identity.Name;
        }
    }
}
