using ZB.MOM.WW.MxGateway.Server.Diagnostics;

namespace ZB.MOM.WW.MxGateway.Tests.Diagnostics;

public sealed class GatewayLogRedactorSeamTests
{
    /// <summary>Redacting a log property containing a bearer API key masks the secret while preserving the key id prefix.</summary>
    [Fact]
    public void Redact_MasksApiKeyInClientIdentity()
    {
        GatewayLogRedactorSeam redactor = new();
        Dictionary<string, object?> props = new() { ["ClientIdentity"] = "Bearer mxgw_operator01_super-secret" };
        redactor.Redact(props);
        Assert.Equal("Bearer mxgw_operator01_[redacted]", props["ClientIdentity"]);
    }

    /// <summary>A command value carried by a credential-bearing command never reaches a sink in the clear.</summary>
    [Fact]
    public void Redact_MasksCommandValueForCredentialBearingCommand()
    {
        GatewayLogRedactorSeam redactor = new();
        Dictionary<string, object?> props = new()
        {
            ["CommandMethod"] = "AuthenticateUser",
            ["CommandValue"] = "operator01:hunter2"
        };

        redactor.Redact(props);

        Assert.Equal(GatewayLogRedactor.RedactedValue, props["CommandValue"]);
    }

    /// <summary>
    /// Value logging is off by default, so an ordinary command value is masked too — the seam
    /// exposes no opt-in, matching <see cref="GatewayLogRedactor.RedactCommandValue"/>'s default.
    /// </summary>
    [Fact]
    public void Redact_MasksCommandValueForOrdinaryCommand()
    {
        GatewayLogRedactorSeam redactor = new();
        Dictionary<string, object?> props = new()
        {
            ["CommandMethod"] = "Read",
            ["CommandValue"] = "plaintext-tag-value"
        };

        redactor.Redact(props);

        Assert.Equal(GatewayLogRedactor.RedactedValue, props["CommandValue"]);
    }

    /// <summary>A command value with no accompanying method is still masked — an unknown method cannot be cleared.</summary>
    [Fact]
    public void Redact_MasksCommandValueWhenCommandMethodAbsent()
    {
        GatewayLogRedactorSeam redactor = new();
        Dictionary<string, object?> props = new() { ["CommandValue"] = "plaintext-tag-value" };

        redactor.Redact(props);

        Assert.Equal(GatewayLogRedactor.RedactedValue, props["CommandValue"]);
    }

    /// <summary>A null command value stays null rather than becoming the redaction placeholder.</summary>
    [Fact]
    public void Redact_LeavesNullCommandValueNull()
    {
        GatewayLogRedactorSeam redactor = new();
        Dictionary<string, object?> props = new() { ["CommandValue"] = null };

        redactor.Redact(props);

        Assert.Null(props["CommandValue"]);
    }

    /// <summary>An event carrying no command value is untouched — the seam must not invent the property.</summary>
    [Fact]
    public void Redact_DoesNotAddCommandValueWhenAbsent()
    {
        GatewayLogRedactorSeam redactor = new();
        Dictionary<string, object?> props = new() { ["ClientIdentity"] = "anonymous" };

        redactor.Redact(props);

        Assert.False(props.ContainsKey("CommandValue"));
    }
}
