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

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

public sealed class GatewayMetricsTests
{
    /// <summary>Verifies that snapshot reflects all metric updates.</summary>
    [Fact]
    public void GetSnapshot_ReflectsSessionWorkerCommandEventAndFaultUpdates()
    {
        using GatewayMetrics metrics = new();

        metrics.SessionOpened();
        metrics.WorkerStarted(TimeSpan.FromMilliseconds(250));
        metrics.CommandStarted("Register");
        metrics.CommandSucceeded("Register", TimeSpan.FromMilliseconds(10));
        metrics.CommandStarted("WriteSecured");
        metrics.CommandFailed("WriteSecured", "AuthorizationFailed", TimeSpan.FromMilliseconds(12));
        metrics.EventReceived("session-1", "OnDataChange");
        metrics.EventReceived("session-1", "OnDataChange");
        metrics.SetWorkerEventQueueDepth(7);
        metrics.AdjustGrpcEventStreamQueueDepth(3);
        metrics.QueueOverflow("session-events");
        metrics.Fault("CommandTimeout");
        metrics.WorkerKilled("CommandTimeout");
        metrics.WorkerStopped("Killed");
        metrics.HeartbeatFailed("session-1");
        metrics.StreamDisconnected("ClientCancelled");
        metrics.SessionClosed();

        GatewayMetricsSnapshot snapshot = metrics.GetSnapshot();

        Assert.Equal(0, snapshot.OpenSessions);
        Assert.Equal(0, snapshot.WorkersRunning);
        Assert.Equal(7, snapshot.WorkerEventQueueDepth);
        Assert.Equal(3, snapshot.GrpcEventStreamQueueDepth);
        Assert.Equal(1, snapshot.SessionsOpened);
        Assert.Equal(1, snapshot.SessionsClosed);
        Assert.Equal(2, snapshot.CommandsStarted);
        Assert.Equal(1, snapshot.CommandsSucceeded);
        Assert.Equal(1, snapshot.CommandsFailed);
        Assert.Equal(2, snapshot.EventsReceived);
        Assert.Equal(1, snapshot.QueueOverflows);
        Assert.Equal(1, snapshot.Faults);
        Assert.Equal(1, snapshot.WorkerKills);
        Assert.Equal(1, snapshot.WorkerExits);
        Assert.Equal(1, snapshot.HeartbeatFailures);
        Assert.Equal(1, snapshot.StreamDisconnects);
        Assert.Equal(1, snapshot.CommandFailuresByMethod["WriteSecured"]);
        Assert.Equal(2, snapshot.EventsByFamily["OnDataChange"]);
        Assert.Equal(2, snapshot.EventsBySession["session-1"]);
    }

    /// <summary>Verifies that negative queue depth is rejected.</summary>
    [Fact]
    public void SetEventQueueDepth_RejectsNegativeDepth()
    {
        using GatewayMetrics metrics = new();

        ArgumentOutOfRangeException exception = Assert.Throws<ArgumentOutOfRangeException>(
            () => metrics.SetWorkerEventQueueDepth(-1));

        Assert.Equal("depth", exception.ParamName);
    }

    /// <summary>Verifies that removing session events only affects that session.</summary>
    [Fact]
    public void RemoveSessionEvents_RemovesOnlyThatSession()
    {
        using GatewayMetrics metrics = new();

        metrics.EventReceived("session-1", "OnDataChange");
        metrics.EventReceived("session-2", "OnWriteComplete");
        metrics.RemoveSessionEvents("session-1");

        GatewayMetricsSnapshot snapshot = metrics.GetSnapshot();

        Assert.Equal(2, snapshot.EventsReceived);
        Assert.False(snapshot.EventsBySession.ContainsKey("session-1"));
        Assert.Equal(1, snapshot.EventsBySession["session-2"]);
        Assert.Equal(1, snapshot.EventsByFamily["OnDataChange"]);
        Assert.Equal(1, snapshot.EventsByFamily["OnWriteComplete"]);
    }
}
