using FluentAssertions;
using Xunit;

namespace ZB.MOM.WW.LmxProxy.Client.Tests;

public class ClientMetricsTests
{
    private static LmxProxyClient.ClientMetrics CreateMetrics() => new();

    [Fact]
    public void IncrementOperationCount_Increments()
    {
        var metrics = CreateMetrics();

        metrics.IncrementOperationCount("Read");
        metrics.IncrementOperationCount("Read");
        metrics.IncrementOperationCount("Read");

        var snapshot = metrics.GetSnapshot();
        snapshot["Read_count"].Should().Be(3L);
    }

    [Fact]
    public void IncrementErrorCount_Increments()
    {
        var metrics = CreateMetrics();

        metrics.IncrementErrorCount("Write");
        metrics.IncrementErrorCount("Write");

        var snapshot = metrics.GetSnapshot();
        snapshot["Write_errors"].Should().Be(2L);
    }

    [Fact]
    public void RecordLatency_StoresValues()
    {
        var metrics = CreateMetrics();

        metrics.RecordLatency("Read", 10);
        metrics.RecordLatency("Read", 20);
        metrics.RecordLatency("Read", 30);

        var snapshot = metrics.GetSnapshot();
        snapshot.Should().ContainKey("Read_avg_latency_ms");
        snapshot.Should().ContainKey("Read_p95_latency_ms");
        snapshot.Should().ContainKey("Read_p99_latency_ms");

        var avg = (double)snapshot["Read_avg_latency_ms"];
        avg.Should().BeApproximately(20.0, 0.1);
    }

    [Fact]
    public void RollingBuffer_CapsAt1000()
    {
        var metrics = CreateMetrics();

        for (int i = 0; i < 1100; i++)
        {
            metrics.RecordLatency("Read", i);
        }

        var snapshot = metrics.GetSnapshot();
        // After 1100 entries, the buffer should have capped at 1000 (oldest removed)
        // The earliest remaining value should be 100 (entries 0-99 were evicted)
        var p95 = (long)snapshot["Read_p95_latency_ms"];
        // p95 of values 100-1099 should be around 1050
        p95.Should().BeGreaterThan(900);
    }

    [Fact]
    public void GetSnapshot_IncludesP95AndP99()
    {
        var metrics = CreateMetrics();

        // Add 100 values: 1, 2, 3, ..., 100
        for (int i = 1; i <= 100; i++)
        {
            metrics.RecordLatency("Op", i);
        }

        var snapshot = metrics.GetSnapshot();

        var p95 = (long)snapshot["Op_p95_latency_ms"];
        var p99 = (long)snapshot["Op_p99_latency_ms"];

        // P95 of 1..100 should be 95
        p95.Should().Be(95);
        // P99 of 1..100 should be 99
        p99.Should().Be(99);
    }

    [Fact]
    public void GetSnapshot_ReturnsEmptyForNoData()
    {
        var metrics = CreateMetrics();

        var snapshot = metrics.GetSnapshot();

        snapshot.Should().BeEmpty();
    }

    [Fact]
    public void GetSnapshot_TracksMultipleOperations()
    {
        var metrics = CreateMetrics();

        metrics.IncrementOperationCount("Read");
        metrics.IncrementOperationCount("Write");
        metrics.IncrementErrorCount("Read");
        metrics.RecordLatency("Read", 10);
        metrics.RecordLatency("Write", 20);

        var snapshot = metrics.GetSnapshot();

        snapshot["Read_count"].Should().Be(1L);
        snapshot["Write_count"].Should().Be(1L);
        snapshot["Read_errors"].Should().Be(1L);
        snapshot.Should().ContainKey("Read_avg_latency_ms");
        snapshot.Should().ContainKey("Write_avg_latency_ms");
    }
}
