using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using NT8.Core.MarketData;
using NT8.Core.Tests.Mocks;

namespace NT8.Core.Tests.MarketData
{
    [TestClass]
    public class LiquidityMonitorTests
    {
        private LiquidityMonitor _monitor;

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

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

        [TestMethod]
        public void UpdateSpread_ValidInput_UpdatesCurrentSpread()
        {
            _monitor.UpdateSpread("ES", 5000.00, 5000.25, 1000);

            var spread = _monitor.GetCurrentSpread("ES");
            Assert.AreEqual(0.25, spread, 0.000001);
        }

        [TestMethod]
        public void UpdateSpread_NullSymbol_ThrowsArgumentNullException()
        {
            Assert.ThrowsException<ArgumentNullException>(delegate
            {
                _monitor.UpdateSpread(null, 100, 101, 10);
            });
        }

        [TestMethod]
        public void GetCurrentSpread_NoData_ReturnsZero()
        {
            var spread = _monitor.GetCurrentSpread("NQ");
            Assert.AreEqual(0.0, spread, 0.000001);
        }

        [TestMethod]
        public void GetAverageSpread_WithHistory_ReturnsAverage()
        {
            _monitor.UpdateSpread("ES", 5000.00, 5000.25, 1000);
            _monitor.UpdateSpread("ES", 5000.00, 5000.50, 1000);

            var avg = _monitor.GetAverageSpread("ES");
            Assert.AreEqual(0.375, avg, 0.000001);
        }

        [TestMethod]
        public void GetAverageSpread_NoHistory_ReturnsZero()
        {
            var avg = _monitor.GetAverageSpread("GC");
            Assert.AreEqual(0.0, avg, 0.000001);
        }

        [TestMethod]
        public void GetLiquidityMetrics_WithData_ReturnsCurrentAndAverage()
        {
            _monitor.UpdateSpread("ES", 5000.00, 5000.25, 1000);
            _monitor.UpdateSpread("ES", 5000.00, 5000.50, 1000);

            var metrics = _monitor.GetLiquidityMetrics("ES");

            Assert.AreEqual("ES", metrics.Symbol);
            Assert.AreEqual(0.50, metrics.Spread, 0.000001);
            Assert.AreEqual(0.375, metrics.AverageSpread, 0.000001);
        }

        [TestMethod]
        public void GetLiquidityMetrics_NoData_ReturnsDefaultMetrics()
        {
            var metrics = _monitor.GetLiquidityMetrics("CL");

            Assert.AreEqual("CL", metrics.Symbol);
            Assert.AreEqual(0.0, metrics.Spread, 0.000001);
            Assert.AreEqual(0.0, metrics.AverageSpread, 0.000001);
            Assert.AreEqual(0L, metrics.TotalDepth);
        }

        [TestMethod]
        public void CalculateLiquidityScore_NoData_ReturnsPoor()
        {
            var score = _monitor.CalculateLiquidityScore("MES");
            Assert.AreEqual(LiquidityScore.Poor, score);
        }

        [TestMethod]
        public void CalculateLiquidityScore_SpreadAboveAverage_ReturnsFairOrPoor()
        {
            _monitor.UpdateSpread("ES", 5000.00, 5000.25, 1000);
            _monitor.UpdateSpread("ES", 5000.00, 5000.25, 1000);
            _monitor.UpdateSpread("ES", 5000.00, 5000.50, 1000);

            var score = _monitor.CalculateLiquidityScore("ES");

            var valid = score == LiquidityScore.Fair || score == LiquidityScore.Poor || score == LiquidityScore.Good;
            Assert.IsTrue(valid);
        }

        [TestMethod]
        public void CalculateLiquidityScore_TightSpread_ReturnsGoodOrExcellent()
        {
            _monitor.UpdateSpread("NQ", 18000.00, 18000.25, 1000);
            _monitor.UpdateSpread("NQ", 18000.00, 18000.25, 1000);
            _monitor.UpdateSpread("NQ", 18000.00, 18000.25, 1000);

            var score = _monitor.CalculateLiquidityScore("NQ");

            var valid = score == LiquidityScore.Good || score == LiquidityScore.Excellent;
            Assert.IsTrue(valid);
        }

        [TestMethod]
        public void IsLiquidityAcceptable_ThresholdMet_ReturnsTrue()
        {
            _monitor.UpdateSpread("MNQ", 18000.00, 18000.25, 1000);
            _monitor.UpdateSpread("MNQ", 18000.00, 18000.25, 1000);

            var result = _monitor.IsLiquidityAcceptable("MNQ", LiquidityScore.Poor);
            Assert.IsTrue(result);
        }

        [TestMethod]
        public void IsLiquidityAcceptable_ThresholdTooHigh_CanReturnFalse()
        {
            _monitor.UpdateSpread("GC", 2000.0, 2001.0, 1000);
            _monitor.UpdateSpread("GC", 2000.0, 2002.0, 1000);

            var result = _monitor.IsLiquidityAcceptable("GC", LiquidityScore.Excellent);
            Assert.IsFalse(result);
        }

        [TestMethod]
        public void ClearSpreadHistory_RemovesTrackedValues()
        {
            _monitor.UpdateSpread("CL", 80.00, 80.05, 1000);
            _monitor.UpdateSpread("CL", 80.00, 80.10, 1000);

            _monitor.ClearSpreadHistory("CL");

            Assert.AreEqual(0.0, _monitor.GetCurrentSpread("CL"), 0.000001);
            Assert.AreEqual(0.0, _monitor.GetAverageSpread("CL"), 0.000001);
        }

        [TestMethod]
        public void ConcurrentUpdates_AreThreadSafe()
        {
            var tasks = new List<Task>();

            for (var i = 0; i < 20; i++)
            {
                var local = i;
                tasks.Add(Task.Run(delegate
                {
                    var bid = 5000.0 + (local * 0.01);
                    var ask = bid + 0.25;
                    _monitor.UpdateSpread("ES", bid, ask, 1000 + local);
                }));
            }

            Task.WaitAll(tasks.ToArray());

            var spread = _monitor.GetCurrentSpread("ES");
            Assert.IsTrue(spread > 0.0);
        }

        [TestMethod]
        public void RollingWindow_CapsAt100Samples_ForAverageCalculation()
        {
            for (var i = 0; i < 120; i++)
            {
                _monitor.UpdateSpread("ES", 5000.0, 5000.25 + (i % 2 == 0 ? 0.0 : 0.25), 1000);
            }

            var avg = _monitor.GetAverageSpread("ES");
            Assert.IsTrue(avg >= 0.25);
            Assert.IsTrue(avg <= 0.50);
        }
    }
}
