using System;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using NT8.Core.Execution;
using NT8.Core.Tests.Mocks;

namespace NT8.Core.Tests.Execution
{
    [TestClass]
    public class ExecutionQualityTrackerTests
    {
        private ExecutionQualityTracker _tracker;

        [TestInitialize]
        public void Setup()
        {
            _tracker = new ExecutionQualityTracker(new MockLogger<ExecutionQualityTracker>());
        }

        [TestMethod]
        public void Constructor_NullLogger_ThrowsArgumentNullException()
        {
            Assert.ThrowsException<ArgumentNullException>(delegate
            {
                new ExecutionQualityTracker(null);
            });
        }

        [TestMethod]
        public void RecordExecution_ValidInput_StoresMetrics()
        {
            var now = DateTime.UtcNow;
            _tracker.RecordExecution("ES-1", 5000m, 5000.25m, now.AddMilliseconds(20), now.AddMilliseconds(5), now);

            var metrics = _tracker.GetExecutionMetrics("ES-1");
            Assert.IsNotNull(metrics);
            Assert.AreEqual("ES-1", metrics.OrderId);
        }

        [TestMethod]
        public void RecordExecution_NullOrderId_ThrowsArgumentNullException()
        {
            Assert.ThrowsException<ArgumentNullException>(delegate
            {
                _tracker.RecordExecution(null, 1m, 1m, DateTime.UtcNow, DateTime.UtcNow, DateTime.UtcNow);
            });
        }

        [TestMethod]
        public void GetExecutionMetrics_UnknownOrder_ReturnsNull()
        {
            var metrics = _tracker.GetExecutionMetrics("UNKNOWN-1");
            Assert.IsNull(metrics);
        }

        [TestMethod]
        public void GetExecutionMetrics_NullOrderId_ThrowsArgumentNullException()
        {
            Assert.ThrowsException<ArgumentNullException>(delegate
            {
                _tracker.GetExecutionMetrics(null);
            });
        }

        [TestMethod]
        public void GetSymbolStatistics_NoHistory_ReturnsDefaults()
        {
            var stats = _tracker.GetSymbolStatistics("ES");

            Assert.AreEqual("ES", stats.Symbol);
            Assert.AreEqual(0.0, stats.AverageSlippage, 0.000001);
            Assert.AreEqual(TimeSpan.Zero, stats.AverageFillLatency);
            Assert.AreEqual(ExecutionQuality.Poor, stats.AverageQuality);
        }

        [TestMethod]
        public void GetSymbolStatistics_WithHistory_ComputesAverages()
        {
            var t0 = DateTime.UtcNow;
            _tracker.RecordExecution("ES-1", 5000m, 5000.25m, t0.AddMilliseconds(30), t0.AddMilliseconds(5), t0);
            _tracker.RecordExecution("ES-2", 5000m, 4999.75m, t0.AddMilliseconds(40), t0.AddMilliseconds(10), t0);

            var stats = _tracker.GetSymbolStatistics("ES");

            Assert.AreEqual("ES", stats.Symbol);
            Assert.AreEqual(2, stats.PositiveSlippageCount + stats.NegativeSlippageCount);
            Assert.IsTrue(stats.AverageFillLatency.TotalMilliseconds > 0.0);
        }

        [TestMethod]
        public void GetSymbolStatistics_NullSymbol_ThrowsArgumentNullException()
        {
            Assert.ThrowsException<ArgumentNullException>(delegate
            {
                _tracker.GetSymbolStatistics(null);
            });
        }

        [TestMethod]
        public void GetAverageSlippage_WithHistory_ReturnsAverage()
        {
            var t0 = DateTime.UtcNow;
            _tracker.RecordExecution("NQ-1", 100m, 101m, t0.AddMilliseconds(5), t0.AddMilliseconds(2), t0);
            _tracker.RecordExecution("NQ-2", 100m, 99m, t0.AddMilliseconds(6), t0.AddMilliseconds(2), t0);

            var avg = _tracker.GetAverageSlippage("NQ");
            Assert.AreEqual(0.0, avg, 0.000001);
        }

        [TestMethod]
        public void GetAverageSlippage_NullSymbol_ThrowsArgumentNullException()
        {
            Assert.ThrowsException<ArgumentNullException>(delegate
            {
                _tracker.GetAverageSlippage(null);
            });
        }

        [TestMethod]
        public void IsExecutionQualityAcceptable_WhenNoDataAndThresholdFair_ReturnsTrue_ByCurrentEnumOrdering()
        {
            var ok = _tracker.IsExecutionQualityAcceptable("GC", ExecutionQuality.Fair);
            Assert.IsTrue(ok);
        }

        [TestMethod]
        public void IsExecutionQualityAcceptable_WithLowThreshold_ReturnsTrue()
        {
            var ok = _tracker.IsExecutionQualityAcceptable("GC", ExecutionQuality.Poor);
            Assert.IsTrue(ok);
        }

        [TestMethod]
        public void IsExecutionQualityAcceptable_NullSymbol_ThrowsArgumentNullException()
        {
            Assert.ThrowsException<ArgumentNullException>(delegate
            {
                _tracker.IsExecutionQualityAcceptable(null, ExecutionQuality.Poor);
            });
        }

        [TestMethod]
        public void GetTotalExecutionCount_AfterRecords_ReturnsCount()
        {
            var t0 = DateTime.UtcNow;
            _tracker.RecordExecution("MES-1", 1m, 1m, t0.AddMilliseconds(2), t0.AddMilliseconds(1), t0);
            _tracker.RecordExecution("MES-2", 1m, 1m, t0.AddMilliseconds(2), t0.AddMilliseconds(1), t0);

            Assert.AreEqual(2, _tracker.GetTotalExecutionCount());
        }

        [TestMethod]
        public void ClearSymbolHistory_RemovesHistoryForSymbol()
        {
            var t0 = DateTime.UtcNow;
            _tracker.RecordExecution("CL-1", 80m, 80.1m, t0.AddMilliseconds(2), t0.AddMilliseconds(1), t0);
            _tracker.ClearSymbolHistory("CL");

            var stats = _tracker.GetSymbolStatistics("CL");
            Assert.AreEqual(0.0, stats.AverageSlippage, 0.000001);
            Assert.AreEqual(ExecutionQuality.Poor, stats.AverageQuality);
        }

        [TestMethod]
        public void ClearSymbolHistory_NullSymbol_ThrowsArgumentNullException()
        {
            Assert.ThrowsException<ArgumentNullException>(delegate
            {
                _tracker.ClearSymbolHistory(null);
            });
        }

        [TestMethod]
        public void RollingWindow_UsesLast100Executions()
        {
            var t0 = DateTime.UtcNow;
            for (var i = 0; i < 120; i++)
            {
                _tracker.RecordExecution("RTY-" + i, 2000m, 2000m + (i % 2 == 0 ? 0.25m : -0.25m), t0.AddMilliseconds(10), t0.AddMilliseconds(5), t0);
            }

            var stats = _tracker.GetSymbolStatistics("RTY");
            Assert.IsTrue(stats.PositiveSlippageCount + stats.NegativeSlippageCount <= 100);
        }

        [TestMethod]
        public void RecordExecution_PositiveSlippage_TagsPositiveType()
        {
            var t0 = DateTime.UtcNow;
            _tracker.RecordExecution("ES-POS", 100m, 101m, t0.AddMilliseconds(10), t0.AddMilliseconds(5), t0);

            var metrics = _tracker.GetExecutionMetrics("ES-POS");
            Assert.AreEqual(SlippageType.Positive, metrics.SlippageType);
        }

        [TestMethod]
        public void RecordExecution_NegativeSlippage_TagsNegativeType()
        {
            var t0 = DateTime.UtcNow;
            _tracker.RecordExecution("ES-NEG", 100m, 99m, t0.AddMilliseconds(10), t0.AddMilliseconds(5), t0);

            var metrics = _tracker.GetExecutionMetrics("ES-NEG");
            Assert.AreEqual(SlippageType.Negative, metrics.SlippageType);
        }
    }
}
