using System;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using NT8.Core.Execution;
using NT8.Core.OMS;
using NT8.Core.Tests.Mocks;

namespace NT8.Core.Tests.Execution
{
    [TestClass]
    public class TrailingStopManagerTests
    {
        private TrailingStopManager _manager;

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

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

        [TestMethod]
        public void StartTrailing_ValidLongPosition_CreatesStop()
        {
            var position = CreatePosition(OrderSide.Buy, 5000m);
            var config = new NT8.Core.Execution.TrailingStopConfig(8);

            _manager.StartTrailing("ORD-1", position, config);

            var stop = _manager.GetCurrentStopPrice("ORD-1");
            Assert.IsTrue(stop.HasValue);
        }

        [TestMethod]
        public void StartTrailing_ValidShortPosition_CreatesStop()
        {
            var position = CreatePosition(OrderSide.Sell, 5000m);
            var config = new NT8.Core.Execution.TrailingStopConfig(8);

            _manager.StartTrailing("ORD-2", position, config);

            var stop = _manager.GetCurrentStopPrice("ORD-2");
            Assert.IsTrue(stop.HasValue);
        }

        [TestMethod]
        public void StartTrailing_NullOrderId_ThrowsArgumentNullException()
        {
            var position = CreatePosition(OrderSide.Buy, 5000m);
            var config = new NT8.Core.Execution.TrailingStopConfig(8);

            Assert.ThrowsException<ArgumentNullException>(delegate
            {
                _manager.StartTrailing(null, position, config);
            });
        }

        [TestMethod]
        public void StartTrailing_NullPosition_ThrowsArgumentNullException()
        {
            var config = new NT8.Core.Execution.TrailingStopConfig(8);

            Assert.ThrowsException<ArgumentNullException>(delegate
            {
                _manager.StartTrailing("ORD-3", null, config);
            });
        }

        [TestMethod]
        public void UpdateTrailingStop_UnknownOrder_ReturnsNull()
        {
            var result = _manager.UpdateTrailingStop("UNKNOWN", 5001m);
            Assert.IsFalse(result.HasValue);
        }

        [TestMethod]
        public void UpdateTrailingStop_LongImprovingPrice_CanReturnUpdatedStop()
        {
            var position = CreatePosition(OrderSide.Buy, 5000m);
            var config = new NT8.Core.Execution.TrailingStopConfig(8);
            _manager.StartTrailing("ORD-4", position, config);

            var updated = _manager.UpdateTrailingStop("ORD-4", 5005m);
            var current = _manager.GetCurrentStopPrice("ORD-4");

            Assert.IsTrue(current.HasValue);
            if (updated.HasValue)
            {
                Assert.IsTrue(updated.Value <= 5005m);
            }
        }

        [TestMethod]
        public void UpdateTrailingStop_ShortImprovingPrice_CanReturnUpdatedStop()
        {
            var position = CreatePosition(OrderSide.Sell, 5000m);
            var config = new NT8.Core.Execution.TrailingStopConfig(8);
            _manager.StartTrailing("ORD-5", position, config);

            var updated = _manager.UpdateTrailingStop("ORD-5", 4995m);
            var current = _manager.GetCurrentStopPrice("ORD-5");

            Assert.IsTrue(current.HasValue);
            if (updated.HasValue)
            {
                Assert.IsTrue(updated.Value >= 4995m || updated.Value <= 5000m);
            }
        }

        [TestMethod]
        public void CalculateNewStopPrice_FixedTrailing_Long_ReturnsValue()
        {
            var position = CreatePosition(OrderSide.Buy, 5000m);
            var stop = _manager.CalculateNewStopPrice(StopType.FixedTrailing, position, 5001m);
            Assert.IsTrue(stop > 0m);
        }

        [TestMethod]
        public void CalculateNewStopPrice_ATRTrailing_Short_ReturnsValue()
        {
            var position = CreatePosition(OrderSide.Sell, 5000m);
            var stop = _manager.CalculateNewStopPrice(StopType.ATRTrailing, position, 4998m);
            Assert.IsTrue(stop > 0m);
        }

        [TestMethod]
        public void CalculateNewStopPrice_PercentageTrailing_ReturnsValue()
        {
            var position = CreatePosition(OrderSide.Buy, 5000m);
            var stop = _manager.CalculateNewStopPrice(StopType.PercentageTrailing, position, 5010m);
            Assert.IsTrue(stop > 0m);
        }

        [TestMethod]
        public void ShouldMoveToBreakeven_EnabledAndInProfit_ReturnsTrue()
        {
            var position = CreatePosition(OrderSide.Buy, 5000m);
            var config = new AutoBreakevenConfig(4, true, 1, true);

            var shouldMove = _manager.ShouldMoveToBreakeven(position, 5002m, config);
            Assert.IsTrue(shouldMove);
        }

        [TestMethod]
        public void ShouldMoveToBreakeven_Disabled_ReturnsFalse()
        {
            var position = CreatePosition(OrderSide.Buy, 5000m);
            var config = new AutoBreakevenConfig(4, true, 1, false);

            var shouldMove = _manager.ShouldMoveToBreakeven(position, 5005m, config);
            Assert.IsFalse(shouldMove);
        }

        [TestMethod]
        public void DeactivateTrailing_PreventsFurtherUpdates()
        {
            var position = CreatePosition(OrderSide.Buy, 5000m);
            var config = new NT8.Core.Execution.TrailingStopConfig(8);
            _manager.StartTrailing("ORD-6", position, config);
            _manager.DeactivateTrailing("ORD-6");

            var updated = _manager.UpdateTrailingStop("ORD-6", 5010m);
            Assert.IsFalse(updated.HasValue);
        }

        [TestMethod]
        public void RemoveTrailing_DeletesTracking()
        {
            var position = CreatePosition(OrderSide.Buy, 5000m);
            var config = new NT8.Core.Execution.TrailingStopConfig(8);
            _manager.StartTrailing("ORD-7", position, config);

            _manager.RemoveTrailing("ORD-7");

            var current = _manager.GetCurrentStopPrice("ORD-7");
            Assert.IsFalse(current.HasValue);
        }

        [TestMethod]
        public void GetCurrentStopPrice_NullOrderId_ThrowsArgumentNullException()
        {
            Assert.ThrowsException<ArgumentNullException>(delegate
            {
                _manager.GetCurrentStopPrice(null);
            });
        }

        private static OrderStatus CreatePosition(OrderSide side, decimal avgFillPrice)
        {
            return new OrderStatus
            {
                OrderId = Guid.NewGuid().ToString(),
                Symbol = "ES",
                Side = side,
                Quantity = 1,
                AverageFillPrice = avgFillPrice,
                State = OrderState.Working,
                FilledQuantity = 1,
                CreatedTime = DateTime.UtcNow
            };
        }
    }
}
