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

namespace NT8.Performance.Tests
{
    [TestClass]
    public class Phase4PerformanceTests
    {
        [TestMethod]
        public void ConfluenceScoreCalculation_ShouldBeUnder5ms_Average()
        {
            var scorer = new ConfluenceScorer(new BasicLogger("Phase4PerformanceTests"), 200);
            var intent = CreateIntent(OrderSide.Buy);
            var context = CreateContext();
            var bar = CreateBar();
            var factors = CreateFactors(0.82, 0.76, 0.88, 0.79, 0.81);

            var sw = Stopwatch.StartNew();

            for (var i = 0; i < 1000; i++)
            {
                var score = scorer.CalculateScore(intent, context, bar, factors);
                Assert.IsTrue(score.WeightedScore >= 0.0);
            }

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

        [TestMethod]
        public void RegimeDetection_ShouldBeUnder3ms_Average()
        {
            var volDetector = new VolatilityRegimeDetector(new BasicLogger("Phase4PerformanceTests"), 100);
            var trendDetector = new TrendRegimeDetector(new BasicLogger("Phase4PerformanceTests"));
            var bars = BuildBars(12, 5000.0, 0.75);

            var sw = Stopwatch.StartNew();

            for (var i = 0; i < 1000; i++)
            {
                var vol = volDetector.DetectRegime("ES", 1.0 + ((i % 6) * 0.2), 1.0);
                var trend = trendDetector.DetectTrend("ES", bars, 5000.0);
                Assert.IsTrue(Enum.IsDefined(typeof(VolatilityRegime), vol));
                Assert.IsTrue(Enum.IsDefined(typeof(TrendRegime), trend));
            }

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

        [TestMethod]
        public void GradeFiltering_ShouldBeUnder1ms_Average()
        {
            var filter = new GradeFilter();
            var grades = new TradeGrade[] { TradeGrade.APlus, TradeGrade.A, TradeGrade.B, TradeGrade.C, TradeGrade.D, TradeGrade.F };
            var modes = new RiskMode[] { RiskMode.ECP, RiskMode.PCP, RiskMode.DCP, RiskMode.HR };

            var sw = Stopwatch.StartNew();

            for (var i = 0; i < 5000; i++)
            {
                var grade = grades[i % grades.Length];
                var mode = modes[i % modes.Length];

                var accepted = filter.ShouldAcceptTrade(grade, mode);
                var multiplier = filter.GetSizeMultiplier(grade, mode);

                if (!accepted)
                {
                    Assert.IsTrue(multiplier >= 0.0);
                }
            }

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

        [TestMethod]
        public void RiskModeUpdate_ShouldBeUnder2ms_Average()
        {
            var manager = new RiskModeManager(new BasicLogger("Phase4PerformanceTests"));

            var sw = Stopwatch.StartNew();

            for (var i = 0; i < 2000; i++)
            {
                var pnl = (i % 2 == 0) ? 300.0 : -250.0;
                var winStreak = i % 5;
                var lossStreak = i % 4;

                manager.UpdateRiskMode(pnl, winStreak, lossStreak);
                var mode = manager.GetCurrentMode();
                Assert.IsTrue(mode >= RiskMode.HR);
            }

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

        [TestMethod]
        public void OverallIntelligenceFlow_ShouldBeUnder15ms_Average()
        {
            var scorer = new ConfluenceScorer(new BasicLogger("Phase4PerformanceTests"), 200);
            var volDetector = new VolatilityRegimeDetector(new BasicLogger("Phase4PerformanceTests"), 100);
            var trendDetector = new TrendRegimeDetector(new BasicLogger("Phase4PerformanceTests"));
            var modeManager = new RiskModeManager(new BasicLogger("Phase4PerformanceTests"));
            var filter = new GradeFilter();

            var intent = CreateIntent(OrderSide.Buy);
            var context = CreateContext();
            var bar = CreateBar();
            var factors = CreateFactors(0.84, 0.78, 0.86, 0.75, 0.80);
            var bars = BuildBars(12, 5000.0, 0.5);

            var sw = Stopwatch.StartNew();

            for (var i = 0; i < 500; i++)
            {
                var score = scorer.CalculateScore(intent, context, bar, factors);
                var vol = volDetector.DetectRegime("ES", 1.2 + ((i % 5) * 0.15), 1.0);
                var trend = trendDetector.DetectTrend("ES", bars, 5000.0);

                var lossStreak = (vol == VolatilityRegime.Extreme) ? 3 : (i % 3);
                modeManager.UpdateRiskMode(i % 2 == 0 ? 350.0 : -150.0, i % 4, lossStreak);
                var mode = modeManager.GetCurrentMode();

                var allowed = filter.ShouldAcceptTrade(score.Grade, mode);
                var mult = filter.GetSizeMultiplier(score.Grade, mode);

                Assert.IsTrue(mult >= 0.0);
                Assert.IsTrue(Enum.IsDefined(typeof(TrendRegime), trend));
                Assert.IsTrue(allowed || !allowed);
            }

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

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

        private static StrategyContext CreateContext()
        {
            return new StrategyContext(
                "ES",
                DateTime.UtcNow,
                new Position("ES", 0, 0, 0, 0, DateTime.UtcNow),
                new AccountInfo(100000, 100000, 0, 0, DateTime.UtcNow),
                new MarketSession(DateTime.Today.AddHours(9.5), DateTime.Today.AddHours(16), true, "RTH"),
                new Dictionary<string, object>());
        }

        private static BarData CreateBar()
        {
            return new BarData("ES", DateTime.UtcNow, 5000, 5004, 4998, 5003, 1200, TimeSpan.FromMinutes(1));
        }

        private static List<IFactorCalculator> CreateFactors(double setup, double trend, double vol, double timing, double quality)
        {
            var factors = new List<IFactorCalculator>();
            factors.Add(new FixedFactor(FactorType.Setup, setup));
            factors.Add(new FixedFactor(FactorType.Trend, trend));
            factors.Add(new FixedFactor(FactorType.Volatility, vol));
            factors.Add(new FixedFactor(FactorType.Timing, timing));
            factors.Add(new FixedFactor(FactorType.ExecutionQuality, quality));
            return factors;
        }

        private static List<BarData> BuildBars(int count, double start, double step)
        {
            var list = new List<BarData>();
            var t = DateTime.UtcNow.AddMinutes(-count);

            for (var i = 0; i < count; i++)
            {
                var close = start + (i * step);
                list.Add(new BarData("ES", t.AddMinutes(i), close - 1.0, close + 1.0, close - 2.0, close, 1000 + i, TimeSpan.FromMinutes(1)));
            }

            return list;
        }

        private class FixedFactor : IFactorCalculator
        {
            private readonly FactorType _type;
            private readonly double _score;

            public FixedFactor(FactorType type, double score)
            {
                _type = type;
                _score = score;
            }

            public FactorType Type
            {
                get { return _type; }
            }

            public ConfluenceFactor Calculate(StrategyIntent intent, StrategyContext context, BarData bar)
            {
                return new ConfluenceFactor(_type, "Fixed", _score, 1.0, "fixed", new Dictionary<string, object>());
            }
        }
    }

}
