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

namespace NT8.Integration.Tests
{
    [TestClass]
    public class Phase3IntegrationTests
    {
        [TestMethod]
        public void Flow_LiquidityToOrderValidationToExecutionTracking_Works()
        {
            var marketLogger = new IntegrationMockLogger<LiquidityMonitor>();
            var liquidityMonitor = new LiquidityMonitor(marketLogger);
            liquidityMonitor.UpdateSpread("ES", 5000.00, 5000.25, 1000);
            var acceptable = liquidityMonitor.IsLiquidityAcceptable("ES", LiquidityScore.Poor);
            Assert.IsTrue(acceptable);

            var orderValidator = new OrderTypeValidator(new IntegrationMockLogger<OrderTypeValidator>());
            var limit = new LimitOrderRequest("ES", OrderSide.Buy, 1, 5000m, TimeInForce.Day);
            var validation = orderValidator.ValidateLimitOrder(limit, 5001m);
            Assert.IsTrue(validation.IsValid);

            var tracker = new ExecutionQualityTracker(new IntegrationMockLogger<ExecutionQualityTracker>());
            var t0 = DateTime.UtcNow;
            tracker.RecordExecution("ES-INT-1", 5000m, 5000.25m, t0.AddMilliseconds(10), t0.AddMilliseconds(3), t0);
            var metrics = tracker.GetExecutionMetrics("ES-INT-1");
            Assert.IsNotNull(metrics);
        }

        [TestMethod]
        public void Flow_DuplicateDetection_PreventsSecondOrder()
        {
            var detector = new DuplicateOrderDetector(new IntegrationMockLogger<DuplicateOrderDetector>(), TimeSpan.FromSeconds(5));
            var request = new OrderRequest
            {
                Symbol = "NQ",
                Side = OrderSide.Buy,
                Type = OrderType.Market,
                Quantity = 2,
                TimeInForce = TimeInForce.Day,
                ClientOrderId = "INT-DUP-1"
            };

            detector.RecordOrderIntent(request);
            var duplicate = detector.IsDuplicateOrder(request);

            Assert.IsTrue(duplicate);
        }

        [TestMethod]
        public void Flow_CircuitBreaker_TripsOnFailures_ThenBlocksOrders()
        {
            var breaker = new ExecutionCircuitBreaker(new IntegrationMockLogger<ExecutionCircuitBreaker>(), 3, TimeSpan.FromSeconds(30), TimeSpan.FromSeconds(5), 100, 3);

            breaker.OnFailure();
            breaker.OnFailure();
            breaker.OnFailure();

            var state = breaker.GetState();
            Assert.AreEqual(CircuitBreakerStatus.Open, state.Status);
            Assert.IsFalse(breaker.ShouldAllowOrder());
        }

        [TestMethod]
        public void Flow_MultiTargets_WithTrailingManager_ProvidesActions()
        {
            var targetManager = new MultiLevelTargetManager(new IntegrationMockLogger<MultiLevelTargetManager>());
            var trailingManager = new TrailingStopManager(new IntegrationMockLogger<TrailingStopManager>());

            targetManager.SetTargets("ORD-INTEG-1", new MultiLevelTargets(8, 2, 16, 2, 32, 1));
            var targetResult = targetManager.OnTargetHit("ORD-INTEG-1", 1, 5002m);
            Assert.AreEqual(TargetAction.PartialClose, targetResult.Action);

            var position = new OrderStatus
            {
                OrderId = "ORD-INTEG-1",
                Symbol = "ES",
                Side = OrderSide.Buy,
                Quantity = 5,
                FilledQuantity = 5,
                AverageFillPrice = 5000m,
                State = OrderState.Working
            };

            trailingManager.StartTrailing("ORD-INTEG-1", position, new NT8.Core.Execution.TrailingStopConfig(8));
            var stop = trailingManager.GetCurrentStopPrice("ORD-INTEG-1");
            Assert.IsTrue(stop.HasValue);
        }

        [TestMethod]
        public void Flow_RMultipleTargets_WithSlippageImpact_ComputesValues()
        {
            var rCalc = new RMultipleCalculator(new IntegrationMockLogger<RMultipleCalculator>());
            var slippageCalc = new SlippageCalculator();

            var position = new NT8.Core.Common.Models.Position("ES", 2, 5000.0, 0, 0, DateTime.UtcNow);
            var rValue = rCalc.CalculateRValue(position, 4998.0, 50.0);
            Assert.IsTrue(rValue > 0.0);

            var targets = rCalc.CreateRBasedTargets(5000.0, 4998.0, new double[] { 1.0, 2.0, 3.0 });
            Assert.IsTrue(targets.TP1Ticks > 0);

            var slippage = slippageCalc.CalculateSlippage(OrderType.Market, 5000m, 5000.25m);
            var impact = slippageCalc.SlippageImpact(slippage, 2, 12.5m, OrderSide.Buy);
            Assert.IsTrue(impact <= 0m || impact >= 0m);
        }

        [TestMethod]
        public void Flow_ContractRollHandler_ReturnsActiveContractAndRollPeriod()
        {
            var handler = new ContractRollHandler(new IntegrationMockLogger<ContractRollHandler>());

            var symbol = "ES";
            var date = new DateTime(DateTime.UtcNow.Year, 3, 10);

            var activeContract = handler.GetActiveContract(symbol, date);
            Assert.IsFalse(string.IsNullOrEmpty(activeContract));

            var rollCheck = handler.IsRollPeriod(symbol, date);
            Assert.IsTrue(rollCheck || !rollCheck);
        }

        [TestMethod]
        public void Flow_SessionManager_WithLiquidityMonitor_ClassifiesSession()
        {
            var sessionManager = new SessionManager(new IntegrationMockLogger<SessionManager>());
            var liquidityMonitor = new LiquidityMonitor(new IntegrationMockLogger<LiquidityMonitor>());

            var now = DateTime.UtcNow;
            var session = sessionManager.GetCurrentSession("ES", now);
            Assert.IsFalse(string.IsNullOrEmpty(session.Symbol));

            liquidityMonitor.UpdateSpread("ES", 5000.0, 5000.25, 1200);
            var score = liquidityMonitor.CalculateLiquidityScore("ES");
            Assert.IsTrue(score == LiquidityScore.Poor || score == LiquidityScore.Fair || score == LiquidityScore.Good || score == LiquidityScore.Excellent);
        }

        [TestMethod]
        public void Flow_CircuitBreaker_RecoversThroughHalfOpenAndReset()
        {
            var breaker = new ExecutionCircuitBreaker(new IntegrationMockLogger<ExecutionCircuitBreaker>(), 1, TimeSpan.FromMilliseconds(10), TimeSpan.FromMilliseconds(5), 100, 3);
            breaker.OnFailure();
            Assert.AreEqual(CircuitBreakerStatus.Open, breaker.GetState().Status);

            System.Threading.Thread.Sleep(20);
            var allowed = breaker.ShouldAllowOrder();
            Assert.IsTrue(allowed);

            breaker.OnSuccess();
            Assert.AreEqual(CircuitBreakerStatus.Closed, breaker.GetState().Status);
        }

        [TestMethod]
        public void Flow_DuplicateDetector_ClearOldIntents_AllowsAfterWindow()
        {
            var detector = new DuplicateOrderDetector(new IntegrationMockLogger<DuplicateOrderDetector>(), TimeSpan.FromMilliseconds(10));
            var request = new OrderRequest
            {
                Symbol = "GC",
                Side = OrderSide.Sell,
                Type = OrderType.Market,
                Quantity = 1,
                TimeInForce = TimeInForce.Day,
                ClientOrderId = "INT-DUP-2"
            };

            detector.RecordOrderIntent(request);
            Assert.IsTrue(detector.IsDuplicateOrder(request));

            System.Threading.Thread.Sleep(20);
            detector.ClearOldIntents(TimeSpan.FromMilliseconds(10));
            Assert.IsFalse(detector.IsDuplicateOrder(request));
        }
    }

    internal class IntegrationMockLogger<T> : ILogger<T>
    {
        public IDisposable BeginScope<TState>(TState state)
        {
            return new IntegrationMockDisposable();
        }

        public bool IsEnabled(LogLevel logLevel)
        {
            return true;
        }

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

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