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

namespace NT8.Core.Tests.Intelligence
{
    [TestClass]
    public class RiskModeManagerTests
    {
        [TestMethod]
        public void Constructor_NullLogger_ThrowsArgumentNullException()
        {
            Assert.ThrowsException<ArgumentNullException>(delegate
            {
                new RiskModeManager(null);
            });
        }

        [TestMethod]
        public void Constructor_DefaultMode_IsPCP()
        {
            var manager = CreateManager();
            Assert.AreEqual(RiskMode.PCP, manager.GetCurrentMode());
        }

        [TestMethod]
        public void GetModeConfig_ECP_ReturnsExpectedValues()
        {
            var manager = CreateManager();
            var config = manager.GetModeConfig(RiskMode.ECP);

            Assert.IsNotNull(config);
            Assert.AreEqual(RiskMode.ECP, config.Mode);
            Assert.AreEqual(1.5, config.SizeMultiplier, 0.000001);
            Assert.AreEqual(TradeGrade.B, config.MinimumGrade);
        }

        [TestMethod]
        public void UpdateRiskMode_StrongPerformance_TransitionsToECP()
        {
            var manager = CreateManager();

            manager.UpdateRiskMode(600.0, 5, 0);

            Assert.AreEqual(RiskMode.ECP, manager.GetCurrentMode());
        }

        [TestMethod]
        public void UpdateRiskMode_DailyLoss_TransitionsToDCP()
        {
            var manager = CreateManager();

            manager.UpdateRiskMode(-250.0, 0, 1);

            Assert.AreEqual(RiskMode.DCP, manager.GetCurrentMode());
        }

        [TestMethod]
        public void UpdateRiskMode_LossStreak3_TransitionsToHR()
        {
            var manager = CreateManager();

            manager.UpdateRiskMode(0.0, 0, 3);

            Assert.AreEqual(RiskMode.HR, manager.GetCurrentMode());
        }

        [TestMethod]
        public void OverrideMode_SetsManualOverrideAndMode()
        {
            var manager = CreateManager();

            manager.OverrideMode(RiskMode.HR, "Risk officer action");
            var state = manager.GetState();

            Assert.AreEqual(RiskMode.HR, manager.GetCurrentMode());
            Assert.IsTrue(state.IsManualOverride);
            Assert.AreEqual("Risk officer action", state.LastTransitionReason);
        }

        [TestMethod]
        public void UpdateRiskMode_WhenManualOverride_DoesNotChangeMode()
        {
            var manager = CreateManager();
            manager.OverrideMode(RiskMode.DCP, "Manual hold");

            manager.UpdateRiskMode(1000.0, 5, 0);

            Assert.AreEqual(RiskMode.DCP, manager.GetCurrentMode());
            Assert.IsTrue(manager.GetState().IsManualOverride);
        }

        [TestMethod]
        public void ResetToDefault_FromManualOverride_ResetsToPCP()
        {
            var manager = CreateManager();
            manager.OverrideMode(RiskMode.HR, "Stop trading");

            manager.ResetToDefault();

            var state = manager.GetState();
            Assert.AreEqual(RiskMode.PCP, manager.GetCurrentMode());
            Assert.IsFalse(state.IsManualOverride);
            Assert.AreEqual("Reset to default", state.LastTransitionReason);
        }

        [TestMethod]
        public void ShouldTransitionMode_ExtremeVolatility_ReturnsTrue()
        {
            var manager = CreateManager();
            var metrics = new PerformanceMetrics(0.0, 1, 0, 0.6, 0.8, VolatilityRegime.Extreme);

            var shouldTransition = manager.ShouldTransitionMode(RiskMode.PCP, metrics);

            Assert.IsTrue(shouldTransition);
        }

        [TestMethod]
        public void ShouldTransitionMode_NoChangeConditions_ReturnsFalse()
        {
            var manager = CreateManager();
            var metrics = new PerformanceMetrics(50.0, 1, 0, 0.7, 0.8, VolatilityRegime.Normal);

            var shouldTransition = manager.ShouldTransitionMode(RiskMode.PCP, metrics);

            Assert.IsFalse(shouldTransition);
        }

        [TestMethod]
        public void ShouldTransitionMode_FromDCPWithRecovery_ReturnsTrue()
        {
            var manager = CreateManager();
            var metrics = new PerformanceMetrics(50.0, 2, 0, 0.8, 0.9, VolatilityRegime.Normal);

            var shouldTransition = manager.ShouldTransitionMode(RiskMode.DCP, metrics);

            Assert.IsTrue(shouldTransition);
        }

        [TestMethod]
        public void UpdateRiskMode_FromDCPWithRecovery_TransitionsToPCP()
        {
            var manager = CreateManager();
            manager.OverrideMode(RiskMode.DCP, "Set DCP");
            manager.ResetToDefault();
            manager.OverrideMode(RiskMode.DCP, "Re-enter DCP");
            manager.ResetToDefault();

            manager.OverrideMode(RiskMode.DCP, "Start in DCP");
            manager.ResetToDefault();
            manager.OverrideMode(RiskMode.DCP, "Start in DCP again");
            manager.ResetToDefault();

            // put in DCP without manual override
            manager.UpdateRiskMode(-300.0, 0, 1);
            Assert.AreEqual(RiskMode.DCP, manager.GetCurrentMode());

            manager.UpdateRiskMode(100.0, 2, 0);

            Assert.AreEqual(RiskMode.PCP, manager.GetCurrentMode());
        }

        [TestMethod]
        public void OverrideMode_EmptyReason_ThrowsArgumentNullException()
        {
            var manager = CreateManager();

            Assert.ThrowsException<ArgumentNullException>(delegate
            {
                manager.OverrideMode(RiskMode.HR, string.Empty);
            });
        }

        [TestMethod]
        public void UpdateRiskMode_NegativeStreaks_ThrowsArgumentException()
        {
            var manager = CreateManager();

            Assert.ThrowsException<ArgumentException>(delegate
            {
                manager.UpdateRiskMode(0.0, -1, 0);
            });

            Assert.ThrowsException<ArgumentException>(delegate
            {
                manager.UpdateRiskMode(0.0, 0, -1);
            });
        }

        private static RiskModeManager CreateManager()
        {
            return new RiskModeManager(new BasicLogger("RiskModeManagerTests"));
        }
    }
}
