using System;
using System.Threading.Tasks;
using FluentAssertions;
using Xunit;
using ZB.MOM.WW.LmxProxy.Host.Metrics;

namespace ZB.MOM.WW.LmxProxy.Host.Tests.Metrics
{
    public class PerformanceMetricsTests
    {
        [Fact]
        public void RecordOperation_TracksCountAndDuration()
        {
            using var metrics = new PerformanceMetrics();

            for (int i = 0; i < 5; i++)
            {
                metrics.RecordOperation("TestOp", TimeSpan.FromMilliseconds(10), true);
            }

            var stats = metrics.GetStatistics();
            stats.Should().ContainKey("TestOp");
            stats["TestOp"].TotalCount.Should().Be(5);
        }

        [Fact]
        public void RecordOperation_TracksSuccessAndFailure()
        {
            using var metrics = new PerformanceMetrics();

            for (int i = 0; i < 3; i++)
            {
                metrics.RecordOperation("TestOp", TimeSpan.FromMilliseconds(10), true);
            }
            for (int i = 0; i < 2; i++)
            {
                metrics.RecordOperation("TestOp", TimeSpan.FromMilliseconds(10), false);
            }

            var stats = metrics.GetStatistics();
            stats["TestOp"].SuccessRate.Should().BeApproximately(0.6, 0.001);
        }

        [Fact]
        public void GetStatistics_CalculatesP95Correctly()
        {
            using var metrics = new PerformanceMetrics();

            for (int i = 1; i <= 100; i++)
            {
                metrics.RecordOperation("TestOp", TimeSpan.FromMilliseconds(i), true);
            }

            var stats = metrics.GetStatistics();
            stats["TestOp"].Percentile95Milliseconds.Should().BeApproximately(95.0, 1.0);
        }

        [Fact]
        public void RollingBuffer_CapsAt1000Samples()
        {
            using var metrics = new PerformanceMetrics();

            for (int i = 0; i < 1500; i++)
            {
                metrics.RecordOperation("TestOp", TimeSpan.FromMilliseconds(i), true);
            }

            var stats = metrics.GetStatistics();
            // TotalCount tracks all 1500 but percentile is computed from the last 1000
            stats["TestOp"].TotalCount.Should().Be(1500);
            // The rolling buffer should have entries from 500-1499
            // P95 of 500..1499 should be around 1449
            stats["TestOp"].Percentile95Milliseconds.Should().BeGreaterThan(1000);
        }

        [Fact]
        public void BeginOperation_RecordsDurationOnDispose()
        {
            using var metrics = new PerformanceMetrics();

            using (var scope = metrics.BeginOperation("TestOp"))
            {
                System.Threading.Thread.Sleep(50);
            }

            var stats = metrics.GetStatistics();
            stats.Should().ContainKey("TestOp");
            stats["TestOp"].TotalCount.Should().Be(1);
            stats["TestOp"].AverageMilliseconds.Should().BeGreaterOrEqualTo(40);
        }

        [Fact]
        public void TimingScope_DefaultsToSuccess()
        {
            using var metrics = new PerformanceMetrics();

            using (metrics.BeginOperation("TestOp"))
            {
                // Do nothing — default is success
            }

            var stats = metrics.GetStatistics();
            stats["TestOp"].SuccessCount.Should().Be(1);
        }

        [Fact]
        public void TimingScope_RespectsSetSuccessFalse()
        {
            using var metrics = new PerformanceMetrics();

            using (var scope = metrics.BeginOperation("TestOp"))
            {
                scope.SetSuccess(false);
            }

            var stats = metrics.GetStatistics();
            stats["TestOp"].SuccessCount.Should().Be(0);
            stats["TestOp"].TotalCount.Should().Be(1);
        }

        [Fact]
        public void GetMetrics_ReturnsNullForUnknownOperation()
        {
            using var metrics = new PerformanceMetrics();

            var result = metrics.GetMetrics("DoesNotExist");

            result.Should().BeNull();
        }

        [Fact]
        public void GetAllMetrics_ReturnsAllTrackedOperations()
        {
            using var metrics = new PerformanceMetrics();

            metrics.RecordOperation("Read", TimeSpan.FromMilliseconds(10), true);
            metrics.RecordOperation("Write", TimeSpan.FromMilliseconds(20), true);
            metrics.RecordOperation("Subscribe", TimeSpan.FromMilliseconds(5), true);

            var all = metrics.GetAllMetrics();
            all.Should().ContainKey("Read");
            all.Should().ContainKey("Write");
            all.Should().ContainKey("Subscribe");
            all.Count.Should().Be(3);
        }
    }
}
