using System;
using System.Collections.Generic;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using NT8.Core.Common.Models;
using NT8.Core.Intelligence;
using NT8.Core.Logging;

namespace NT8.Core.Tests.Intelligence
{
    [TestClass]
    public class RegimeDetectionTests
    {
        [TestMethod]
        public void VolatilityDetector_ClassifiesLow()
        {
            var detector = CreateVolDetector();
            var regime = detector.DetectRegime("ES", 0.5, 1.0);
            Assert.AreEqual(VolatilityRegime.Low, regime);
        }

        [TestMethod]
        public void VolatilityDetector_ClassifiesBelowNormal()
        {
            var detector = CreateVolDetector();
            var regime = detector.DetectRegime("ES", 0.7, 1.0);
            Assert.AreEqual(VolatilityRegime.BelowNormal, regime);
        }

        [TestMethod]
        public void VolatilityDetector_ClassifiesNormal()
        {
            var detector = CreateVolDetector();
            var regime = detector.DetectRegime("ES", 1.0, 1.0);
            Assert.AreEqual(VolatilityRegime.Normal, regime);
        }

        [TestMethod]
        public void VolatilityDetector_ClassifiesElevated()
        {
            var detector = CreateVolDetector();
            var regime = detector.DetectRegime("ES", 1.3, 1.0);
            Assert.AreEqual(VolatilityRegime.Elevated, regime);
        }

        [TestMethod]
        public void VolatilityDetector_ClassifiesHigh()
        {
            var detector = CreateVolDetector();
            var regime = detector.DetectRegime("ES", 1.7, 1.0);
            Assert.AreEqual(VolatilityRegime.High, regime);
        }

        [TestMethod]
        public void VolatilityDetector_ClassifiesExtreme()
        {
            var detector = CreateVolDetector();
            var regime = detector.DetectRegime("ES", 2.2, 1.0);
            Assert.AreEqual(VolatilityRegime.Extreme, regime);
        }

        [TestMethod]
        public void VolatilityDetector_CalculateScore_ReturnsRatio()
        {
            var detector = CreateVolDetector();
            var score = detector.CalculateVolatilityScore(1.5, 1.0);
            Assert.AreEqual(1.5, score, 0.000001);
        }

        [TestMethod]
        public void VolatilityDetector_TransitionHistory_TracksChanges()
        {
            var detector = CreateVolDetector();
            detector.DetectRegime("NQ", 1.0, 1.0);
            detector.DetectRegime("NQ", 2.3, 1.0);

            var transitions = detector.GetTransitions("NQ");
            Assert.IsTrue(transitions.Count >= 1);
            Assert.AreEqual("NQ", transitions[0].Symbol);
        }

        [TestMethod]
        public void VolatilityDetector_GetCurrentRegime_Unknown_ReturnsNormal()
        {
            var detector = CreateVolDetector();
            var regime = detector.GetCurrentRegime("GC");
            Assert.AreEqual(VolatilityRegime.Normal, regime);
        }

        [TestMethod]
        public void TrendDetector_DetectsStrongUp()
        {
            var detector = CreateTrendDetector();
            var bars = BuildUptrendBars(12, 5000.0, 2.0);

            var regime = detector.DetectTrend("ES", bars, 4995.0);

            Assert.IsTrue(regime == TrendRegime.StrongUp || regime == TrendRegime.WeakUp);
        }

        [TestMethod]
        public void TrendDetector_DetectsStrongDown()
        {
            var detector = CreateTrendDetector();
            var bars = BuildDowntrendBars(12, 5000.0, 2.0);

            var regime = detector.DetectTrend("ES", bars, 5005.0);

            Assert.IsTrue(regime == TrendRegime.StrongDown || regime == TrendRegime.WeakDown);
        }

        [TestMethod]
        public void TrendDetector_CalculateStrength_UptrendPositive()
        {
            var detector = CreateTrendDetector();
            var bars = BuildUptrendBars(10, 100.0, 1.0);

            var strength = detector.CalculateTrendStrength(bars, 95.0);

            Assert.IsTrue(strength > 0.0);
        }

        [TestMethod]
        public void TrendDetector_CalculateStrength_DowntrendNegative()
        {
            var detector = CreateTrendDetector();
            var bars = BuildDowntrendBars(10, 100.0, 1.0);

            var strength = detector.CalculateTrendStrength(bars, 105.0);

            Assert.IsTrue(strength < 0.0);
        }

        [TestMethod]
        public void TrendDetector_IsRanging_FlatBars_True()
        {
            var detector = CreateTrendDetector();
            var bars = BuildRangeBars(10, 100.0, 0.1);

            var ranging = detector.IsRanging(bars, 0.2);

            Assert.IsTrue(ranging);
        }

        [TestMethod]
        public void TrendDetector_AssessTrendQuality_GoodStructure_ReturnsNotPoor()
        {
            var detector = CreateTrendDetector();
            var bars = BuildUptrendBars(12, 100.0, 0.8);

            var quality = detector.AssessTrendQuality(bars);

            Assert.IsTrue(quality == TrendQuality.Fair || quality == TrendQuality.Good || quality == TrendQuality.Excellent);
        }

        [TestMethod]
        public void RegimeManager_UpdateAndGetCurrentRegime_ReturnsState()
        {
            var manager = CreateRegimeManager();
            var bar = CreateBar("ES", 5000, 5004, 4998, 5002, 1000);

            manager.UpdateRegime("ES", bar, 5000.0, 1.0, 1.0);
            var state = manager.GetCurrentRegime("ES");

            Assert.IsNotNull(state);
            Assert.AreEqual("ES", state.Symbol);
        }

        [TestMethod]
        public void RegimeManager_ShouldAdjustStrategy_ExtremeVolatility_True()
        {
            var manager = CreateRegimeManager();
            var bars = BuildUptrendBars(6, 5000.0, 1.0);

            for (var i = 0; i < bars.Count; i++)
            {
                manager.UpdateRegime("ES", bars[i], 5000.0, 2.5, 1.0);
            }

            var intent = CreateIntent(OrderSide.Buy);
            var shouldAdjust = manager.ShouldAdjustStrategy("ES", intent);

            Assert.IsTrue(shouldAdjust);
        }

        [TestMethod]
        public void RegimeManager_TransitionsRecorded_WhenRegimeChanges()
        {
            var manager = CreateRegimeManager();
            var bars = BuildUptrendBars(6, 5000.0, 1.0);

            for (var i = 0; i < bars.Count; i++)
            {
                manager.UpdateRegime("NQ", bars[i], 5000.0, 1.0, 1.0);
            }

            manager.UpdateRegime("NQ", CreateBar("NQ", 5000, 5001, 4990, 4991, 1500), 5000.0, 2.3, 1.0);

            var transitions = manager.GetRecentTransitions("NQ", TimeSpan.FromHours(2));
            Assert.IsTrue(transitions.Count >= 1);
        }

        private static VolatilityRegimeDetector CreateVolDetector()
        {
            return new VolatilityRegimeDetector(new BasicLogger("RegimeDetectionTests"), 50);
        }

        private static TrendRegimeDetector CreateTrendDetector()
        {
            return new TrendRegimeDetector(new BasicLogger("RegimeDetectionTests"));
        }

        private static RegimeManager CreateRegimeManager()
        {
            var logger = new BasicLogger("RegimeManagerTests");
            var vol = new VolatilityRegimeDetector(logger, 50);
            var trend = new TrendRegimeDetector(logger);
            return new RegimeManager(logger, vol, trend, 200, 100);
        }

        private static List<BarData> BuildUptrendBars(int count, double start, double step)
        {
            var result = new List<BarData>();
            var time = DateTime.UtcNow.AddMinutes(-count);
            for (var i = 0; i < count; i++)
            {
                var close = start + (i * step);
                result.Add(new BarData("ES", time.AddMinutes(i), close - 1.0, close + 1.0, close - 2.0, close, 1000 + i, TimeSpan.FromMinutes(1)));
            }
            return result;
        }

        private static List<BarData> BuildDowntrendBars(int count, double start, double step)
        {
            var result = new List<BarData>();
            var time = DateTime.UtcNow.AddMinutes(-count);
            for (var i = 0; i < count; i++)
            {
                var close = start - (i * step);
                result.Add(new BarData("ES", time.AddMinutes(i), close + 1.0, close + 2.0, close - 1.0, close, 1000 + i, TimeSpan.FromMinutes(1)));
            }
            return result;
        }

        private static List<BarData> BuildRangeBars(int count, double center, double amplitude)
        {
            var result = new List<BarData>();
            var time = DateTime.UtcNow.AddMinutes(-count);
            for (var i = 0; i < count; i++)
            {
                var close = center + ((i % 2 == 0) ? amplitude : -amplitude);
                result.Add(new BarData("ES", time.AddMinutes(i), close - 0.05, close + 0.10, close - 0.10, close, 800 + i, TimeSpan.FromMinutes(1)));
            }
            return result;
        }

        private static BarData CreateBar(string symbol, double open, double high, double low, double close, long volume)
        {
            return new BarData(symbol, DateTime.UtcNow, open, high, low, close, volume, TimeSpan.FromMinutes(1));
        }

        private static StrategyIntent CreateIntent(OrderSide side)
        {
            return new StrategyIntent(
                "ES",
                side,
                OrderType.Market,
                null,
                8,
                16,
                0.8,
                "Test",
                new Dictionary<string, object>());
        }
    }
}
