using System;
using System.Diagnostics;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using NT8.Core.Execution;
using NT8.Core.MarketData;
using NT8.Core.OMS;

namespace NT8.Performance.Tests
{
    [TestClass]
    public class Phase3PerformanceTests
    {
        [TestMethod]
        public void OrderTypeValidation_ShouldBeUnder2ms_Average()
        {
            var validator = new OrderTypeValidator(new PerfLogger<OrderTypeValidator>());
            var request = new LimitOrderRequest("ES", OrderSide.Buy, 1, 5000m, TimeInForce.Day);
            var sw = Stopwatch.StartNew();

            for (var i = 0; i < 1000; i++)
            {
                var result = validator.ValidateLimitOrder(request, 5001m);
                Assert.IsTrue(result.IsValid);
            }

            sw.Stop();
            var avgMs = sw.Elapsed.TotalMilliseconds / 1000.0;
            Assert.IsTrue(avgMs < 2.0, string.Format("Average validation time {0:F4}ms exceeded 2ms", avgMs));
        }

        [TestMethod]
        public void ExecutionQualityCalculation_ShouldBeUnder3ms_Average()
        {
            var tracker = new ExecutionQualityTracker(new PerfLogger<ExecutionQualityTracker>());
            var t0 = DateTime.UtcNow;
            var sw = Stopwatch.StartNew();

            for (var i = 0; i < 1000; i++)
            {
                var orderId = string.Format("ES-PERF-{0}", i);
                tracker.RecordExecution(orderId, 5000m, 5000.25m, t0.AddMilliseconds(5), t0.AddMilliseconds(2), t0);
            }

            sw.Stop();
            var avgMs = sw.Elapsed.TotalMilliseconds / 1000.0;
            Assert.IsTrue(avgMs < 3.0, string.Format("Average execution tracking time {0:F4}ms exceeded 3ms", avgMs));
        }

        [TestMethod]
        public void LiquidityUpdate_ShouldBeUnder1ms_Average()
        {
            var monitor = new LiquidityMonitor(new PerfLogger<LiquidityMonitor>());
            var sw = Stopwatch.StartNew();

            for (var i = 0; i < 2000; i++)
            {
                var bid = 5000.0 + (i % 10) * 0.01;
                var ask = bid + 0.25;
                monitor.UpdateSpread("ES", bid, ask, 1000 + i);
            }

            sw.Stop();
            var avgMs = sw.Elapsed.TotalMilliseconds / 2000.0;
            Assert.IsTrue(avgMs < 1.0, string.Format("Average liquidity update time {0:F4}ms exceeded 1ms", avgMs));
        }

        [TestMethod]
        public void TrailingStopUpdate_ShouldBeUnder2ms_Average()
        {
            var manager = new TrailingStopManager(new PerfLogger<TrailingStopManager>());
            var position = new OrderStatus
            {
                OrderId = "PERF-TRAIL-1",
                Symbol = "ES",
                Side = OrderSide.Buy,
                Quantity = 1,
                FilledQuantity = 1,
                AverageFillPrice = 5000m,
                State = OrderState.Working,
                CreatedTime = DateTime.UtcNow
            };

            manager.StartTrailing("PERF-TRAIL-1", position, new NT8.Core.Execution.TrailingStopConfig(8));

            var sw = Stopwatch.StartNew();
            for (var i = 0; i < 1000; i++)
            {
                manager.UpdateTrailingStop("PERF-TRAIL-1", 5000m + (i * 0.01m));
            }
            sw.Stop();

            var avgMs = sw.Elapsed.TotalMilliseconds / 1000.0;
            Assert.IsTrue(avgMs < 2.0, string.Format("Average trailing stop update time {0:F4}ms exceeded 2ms", avgMs));
        }

        [TestMethod]
        public void OverallExecutionFlow_ShouldBeUnder15ms_Average()
        {
            var validator = new OrderTypeValidator(new PerfLogger<OrderTypeValidator>());
            var tracker = new ExecutionQualityTracker(new PerfLogger<ExecutionQualityTracker>());
            var monitor = new LiquidityMonitor(new PerfLogger<LiquidityMonitor>());
            var detector = new DuplicateOrderDetector(new PerfLogger<DuplicateOrderDetector>(), TimeSpan.FromSeconds(5));

            var sw = Stopwatch.StartNew();

            for (var i = 0; i < 500; i++)
            {
                monitor.UpdateSpread("ES", 5000.0, 5000.25, 1000);
                var request = new LimitOrderRequest("ES", OrderSide.Buy, 1, 5000m, TimeInForce.Day);
                var valid = validator.ValidateLimitOrder(request, 5001m);
                Assert.IsTrue(valid.IsValid);

                detector.RecordOrderIntent(request);

                var t0 = DateTime.UtcNow;
                tracker.RecordExecution(string.Format("ES-FLOW-{0}", i), 5000m, 5000.25m, t0.AddMilliseconds(5), t0.AddMilliseconds(2), t0);
            }

            sw.Stop();
            var avgMs = sw.Elapsed.TotalMilliseconds / 500.0;
            Assert.IsTrue(avgMs < 15.0, string.Format("Average end-to-end flow time {0:F4}ms exceeded 15ms", avgMs));
        }
    }

    internal class PerfLogger<T> : Microsoft.Extensions.Logging.ILogger<T>
    {
        public IDisposable BeginScope<TState>(TState state)
        {
            return new PerfDisposable();
        }

        public bool IsEnabled(Microsoft.Extensions.Logging.LogLevel logLevel)
        {
            return true;
        }

        public void Log<TState>(
            Microsoft.Extensions.Logging.LogLevel logLevel,
            Microsoft.Extensions.Logging.EventId eventId,
            TState state,
            Exception exception,
            Func<TState, Exception, string> formatter)
        {
        }
    }

    internal class PerfDisposable : IDisposable
    {
        public void Dispose()
        {
        }
    }
}
