using Shouldly;
using Xunit;
using ZB.MOM.WW.OtOpcUa.Security.Audit;

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

/// <summary>
/// Unit tests for <see cref="AuditActor"/> — the static resolution helper that sources the
/// <c>Actor</c> field of a canonical <c>ZB.MOM.WW.Audit.AuditEvent</c> from the current
/// HTTP principal and falls back to a configurable value when no principal is available.
/// </summary>
public sealed class AuditActorTests
{
    /// <summary>
    /// <see cref="AuditActor.Resolve(IAuditActorAccessor?)"/> returns the accessor's value
    /// when the accessor returns a non-null string.
    /// </summary>
    [Fact]
    public void Resolve_returns_accessor_value_when_present()
    {
        var accessor = new StubAccessor("alice");

        AuditActor.Resolve(accessor).ShouldBe("alice");
    }

    /// <summary>
    /// <see cref="AuditActor.Resolve(IAuditActorAccessor?)"/> returns
    /// <see cref="AuditActor.SystemFallback"/> when the accessor returns null
    /// (unauthenticated / no HTTP context).
    /// </summary>
    [Fact]
    public void Resolve_returns_system_fallback_when_accessor_returns_null()
    {
        var accessor = new StubAccessor(null);

        AuditActor.Resolve(accessor).ShouldBe(AuditActor.SystemFallback);
    }

    /// <summary>
    /// <see cref="AuditActor.Resolve(IAuditActorAccessor?)"/> returns
    /// <see cref="AuditActor.SystemFallback"/> when the accessor reference itself is null
    /// (e.g. in a background/non-HTTP context where DI did not inject the accessor).
    /// </summary>
    [Fact]
    public void Resolve_returns_system_fallback_when_accessor_is_null()
    {
        AuditActor.Resolve(null).ShouldBe(AuditActor.SystemFallback);
    }

    /// <summary>
    /// <see cref="AuditActor.Resolve(IAuditActorAccessor?,string)"/> uses the explicit
    /// fallback string rather than <see cref="AuditActor.SystemFallback"/> when the accessor
    /// returns null.
    /// </summary>
    [Fact]
    public void Resolve_uses_explicit_fallback_when_accessor_returns_null()
    {
        var accessor = new StubAccessor(null);

        AuditActor.Resolve(accessor, "scheduler").ShouldBe("scheduler");
    }

    /// <summary>
    /// <see cref="AuditActor.Resolve(IAuditActorAccessor?,string)"/> prefers the accessor's
    /// value over the explicit fallback when the accessor returns a non-null string.
    /// </summary>
    [Fact]
    public void Resolve_prefers_accessor_value_over_explicit_fallback()
    {
        var accessor = new StubAccessor("bob");

        AuditActor.Resolve(accessor, "scheduler").ShouldBe("bob");
    }

    // ── stub ──────────────────────────────────────────────────────────────────────

    private sealed class StubAccessor(string? value) : IAuditActorAccessor
    {
        public string? CurrentActor { get; } = value;
    }
}
