using Opc.Ua;

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

/// <summary>
/// A <see cref="UserIdentity"/> that additionally carries the LDAP-resolved roles
/// (<see cref="OpcUaUserAuthResult.Roles"/>) for the authenticated session. The SDK stores the
/// instance we assign to <c>ImpersonateEventArgs.Identity</c> by reference onto
/// <c>Session.Identity</c>/<c>Session.EffectiveIdentity</c> (no wrapping or copying), so a method
/// handler can later recover the roles via
/// <c>(context as ISessionOperationContext)?.UserIdentity as RoleCarryingUserIdentity</c> — the
/// seam the inbound alarm ack/shelve <c>AlarmAck</c> gate (T18) reads. This type only carries the
/// roles; it makes no auth decision.
/// </summary>
public sealed class RoleCarryingUserIdentity : UserIdentity
{
    /// <summary>
    /// Initializes a new instance, building the base identity from the same UserName token the
    /// SDK decrypted and attaching the authenticator's resolved roles.
    /// </summary>
    /// <param name="token">The user identity token the SDK handed to the impersonation handler.</param>
    /// <param name="roles">The roles resolved by the authenticator (e.g. <c>ReadOnly</c>, <c>AlarmAck</c>).</param>
    public RoleCarryingUserIdentity(UserIdentityToken token, IReadOnlyList<string> roles)
        : base(token)
    {
        ArgumentNullException.ThrowIfNull(roles);
        // Freeze to a defensive copy so a caller mutating the source list can never retroactively
        // alter this session's granted roles, and so the gate's per-ack `.Contains(...)` runs over a
        // known-small frozen array.
        Roles = [.. roles];
    }

    /// <summary>The roles the authenticator granted this session, used by downstream permission gates.</summary>
    public IReadOnlyList<string> Roles { get; }
}
