using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using Microsoft.Extensions.Logging;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using NT8.Core.OMS;
using NT8.Core.Tests.Mocks;

namespace NT8.Core.Tests.OMS
{
    [TestClass]
    public class BasicOrderManagerTests
    {
        private MockLogger<BasicOrderManager> _mockLogger;
        private MockNT8OrderAdapter _mockAdapter;
        private BasicOrderManager _orderManager;

        [TestInitialize]
        public void Setup()
        {
            _mockLogger = new MockLogger<BasicOrderManager>();
            _mockAdapter = new MockNT8OrderAdapter();
            _orderManager = new BasicOrderManager(_mockLogger, _mockAdapter);
        }

        [TestCleanup]
        public void Cleanup()
        {
            if (_orderManager != null)
            {
                _orderManager.Dispose();
            }
        }

        [TestMethod]
        public async Task SubmitOrderAsync_ValidRequest_ReturnsSuccessResult()
        {
            // Arrange
            var request = new OrderRequest
            {
                Symbol = "ES",
                Side = OrderSide.Buy,
                Type = OrderType.Market,
                Quantity = 1,
                ClientOrderId = "TEST123"
            };

            // Act
            var result = await _orderManager.SubmitOrderAsync(request);

            // Assert
            Assert.IsTrue(result.Success);
            Assert.IsNotNull(result.OrderId);
            Assert.AreEqual(request, result.Request);
            Assert.AreEqual("Order submitted successfully", result.Message);
        }

        [TestMethod]
        public async Task SubmitOrderAsync_NullRequest_ThrowsArgumentNullException()
        {
            // Arrange
            OrderRequest request = null;

            // Act & Assert
            await Assert.ThrowsExceptionAsync<ArgumentNullException>(
                () => _orderManager.SubmitOrderAsync(request));
        }

        [TestMethod]
        public async Task SubmitOrderAsync_InvalidRequest_ReturnsFailureResult()
        {
            // Arrange
            var request = new OrderRequest
            {
                Symbol = "", // Invalid symbol
                Side = OrderSide.Buy,
                Type = OrderType.Market,
                Quantity = 1
            };

            // Act
            var result = await _orderManager.SubmitOrderAsync(request);

            // Assert
            Assert.IsFalse(result.Success);
            Assert.IsNull(result.OrderId);
        }

        [TestMethod]
        public async Task SubmitOrderAsync_Nt8SubmissionFails_ReturnsFailureResult()
        {
            // Arrange
            _mockAdapter.ShouldSucceed = false;
            _mockAdapter.ShouldFail = true;

            var request = new OrderRequest
            {
                Symbol = "ES",
                Side = OrderSide.Buy,
                Type = OrderType.Market,
                Quantity = 1,
                ClientOrderId = "TEST123"
            };

            // Act
            var result = await _orderManager.SubmitOrderAsync(request);

            // Assert
            Assert.IsFalse(result.Success);
            Assert.IsNotNull(result.OrderId); // Order ID is generated before NT8 submission
            Assert.AreEqual("Order submission failed at NT8 level", result.Message);
        }

        [TestMethod]
        public async Task ModifyOrderAsync_ValidRequest_ReturnsTrue()
        {
            // Arrange
            var request = new OrderRequest
            {
                Symbol = "ES",
                Side = OrderSide.Buy,
                Type = OrderType.Limit,
                Quantity = 1,
                LimitPrice = 4000m,
                ClientOrderId = "TEST123"
            };

            var submitResult = await _orderManager.SubmitOrderAsync(request);
            var modification = new OrderModification(submitResult.OrderId)
            {
                NewQuantity = 2
            };

            // Act
            var result = await _orderManager.ModifyOrderAsync(modification);

            // Assert
            Assert.IsTrue(result);
        }

        [TestMethod]
        public async Task ModifyOrderAsync_NullRequest_ThrowsArgumentNullException()
        {
            // Arrange
            OrderModification modification = null;

            // Act & Assert
            await Assert.ThrowsExceptionAsync<ArgumentNullException>(
                () => _orderManager.ModifyOrderAsync(modification));
        }

        [TestMethod]
        public async Task CancelOrderAsync_ValidRequest_ReturnsTrue()
        {
            // Arrange
            var request = new OrderRequest
            {
                Symbol = "ES",
                Side = OrderSide.Buy,
                Type = OrderType.Market,
                Quantity = 1,
                ClientOrderId = "TEST123"
            };

            var submitResult = await _orderManager.SubmitOrderAsync(request);
            var cancellation = new OrderCancellation(submitResult.OrderId, "Test cancellation");

            // Act
            var result = await _orderManager.CancelOrderAsync(cancellation);

            // Assert
            Assert.IsTrue(result);
        }

        [TestMethod]
        public async Task CancelOrderAsync_NullRequest_ThrowsArgumentNullException()
        {
            // Arrange
            OrderCancellation cancellation = null;

            // Act & Assert
            await Assert.ThrowsExceptionAsync<ArgumentNullException>(
                () => _orderManager.CancelOrderAsync(cancellation));
        }

        [TestMethod]
        public async Task GetOrderStatusAsync_ExistingOrder_ReturnsOrderStatus()
        {
            // Arrange
            var request = new OrderRequest
            {
                Symbol = "ES",
                Side = OrderSide.Buy,
                Type = OrderType.Market,
                Quantity = 1,
                ClientOrderId = "TEST123"
            };

            var submitResult = await _orderManager.SubmitOrderAsync(request);

            // Act
            var status = await _orderManager.GetOrderStatusAsync(submitResult.OrderId);

            // Assert
            Assert.IsNotNull(status);
            Assert.AreEqual(submitResult.OrderId, status.OrderId);
            Assert.AreEqual("ES", status.Symbol);
        }

        [TestMethod]
        public async Task GetOrderStatusAsync_NonExistentOrder_ReturnsNull()
        {
            // Act
            var status = await _orderManager.GetOrderStatusAsync("NONEXISTENT");

            // Assert
            Assert.IsNull(status);
        }

        [TestMethod]
        public async Task GetOrderStatusAsync_NullOrderId_ThrowsArgumentNullException()
        {
            // Act & Assert
            await Assert.ThrowsExceptionAsync<ArgumentNullException>(
                () => _orderManager.GetOrderStatusAsync(null));
        }

        [TestMethod]
        public async Task GetActiveOrdersAsync_HasActiveOrders_ReturnsList()
        {
            // Arrange
            var request1 = new OrderRequest
            {
                Symbol = "ES",
                Side = OrderSide.Buy,
                Type = OrderType.Market,
                Quantity = 1,
                ClientOrderId = "TEST123"
            };

            var request2 = new OrderRequest
            {
                Symbol = "NQ",
                Side = OrderSide.Sell,
                Type = OrderType.Market,
                Quantity = 2,
                ClientOrderId = "TEST124"
            };

            await _orderManager.SubmitOrderAsync(request1);
            await _orderManager.SubmitOrderAsync(request2);

            // Act
            var activeOrders = await _orderManager.GetActiveOrdersAsync();

            // Assert
            Assert.IsNotNull(activeOrders);
            Assert.IsTrue(activeOrders.Count >= 2);
        }

        [TestMethod]
        public async Task GetOrdersBySymbolAsync_ValidSymbol_ReturnsFilteredOrders()
        {
            // Arrange
            var request1 = new OrderRequest
            {
                Symbol = "ES",
                Side = OrderSide.Buy,
                Type = OrderType.Market,
                Quantity = 1,
                ClientOrderId = "TEST123"
            };

            var request2 = new OrderRequest
            {
                Symbol = "NQ",
                Side = OrderSide.Sell,
                Type = OrderType.Market,
                Quantity = 2,
                ClientOrderId = "TEST124"
            };

            await _orderManager.SubmitOrderAsync(request1);
            await _orderManager.SubmitOrderAsync(request2);

            // Act
            var esOrders = await _orderManager.GetOrdersBySymbolAsync("ES");

            // Assert
            Assert.IsNotNull(esOrders);
            foreach (var order in esOrders)
            {
                Assert.AreEqual("ES", order.Symbol, true); // Case insensitive comparison
            }
        }

        [TestMethod]
        public async Task GetOrdersBySymbolAsync_NullSymbol_ThrowsArgumentNullException()
        {
            // Act & Assert
            await Assert.ThrowsExceptionAsync<ArgumentNullException>(
                () => _orderManager.GetOrdersBySymbolAsync(null));
        }

        [TestMethod]
        public async Task FlattenSymbolAsync_ValidSymbol_CancelsOrders()
        {
            // Arrange
            var request1 = new OrderRequest
            {
                Symbol = "ES",
                Side = OrderSide.Buy,
                Type = OrderType.Market,
                Quantity = 1,
                ClientOrderId = "TEST123"
            };

            var request2 = new OrderRequest
            {
                Symbol = "ES", // Same symbol
                Side = OrderSide.Sell,
                Type = OrderType.Market,
                Quantity = 2,
                ClientOrderId = "TEST124"
            };

            await _orderManager.SubmitOrderAsync(request1);
            await _orderManager.SubmitOrderAsync(request2);

            // Act
            var result = await _orderManager.FlattenSymbolAsync("ES");

            // Assert
            Assert.IsTrue(result);
        }

        [TestMethod]
        public async Task FlattenAllAsync_CancelsAllOrders()
        {
            // Arrange
            var request1 = new OrderRequest
            {
                Symbol = "ES",
                Side = OrderSide.Buy,
                Type = OrderType.Market,
                Quantity = 1,
                ClientOrderId = "TEST123"
            };

            var request2 = new OrderRequest
            {
                Symbol = "NQ",
                Side = OrderSide.Sell,
                Type = OrderType.Market,
                Quantity = 2,
                ClientOrderId = "TEST124"
            };

            await _orderManager.SubmitOrderAsync(request1);
            await _orderManager.SubmitOrderAsync(request2);

            // Act
            var result = await _orderManager.FlattenAllAsync();

            // Assert
            Assert.IsTrue(result);
        }

        [TestMethod]
        public void SubscribeAndUnsubscribeToOrderUpdates_WorksCorrectly()
        {
            // Arrange - First create an order so the manager knows about it
            var request = new OrderRequest
            {
                Symbol = "ES",
                Side = OrderSide.Buy,
                Type = OrderType.Market,
                Quantity = 1,
                ClientOrderId = "TEST_CLIENT_ORDER"
            };

            var submitResult = _orderManager.SubmitOrderAsync(request).Result;
            Assert.IsTrue(submitResult.Success);
            string orderId = submitResult.OrderId;
            Assert.IsNotNull(orderId);

            bool callbackCalled = false;
            Action<OrderStatus> callback = delegate(OrderStatus statusParam) { callbackCalled = true; };

            // Act - subscribe
            _orderManager.SubscribeToOrderUpdates(callback);

            // Simulate an order update via the mock adapter for the known order
            var statusUpdate = new OrderStatus
            {
                OrderId = orderId,  // Use the actual order ID from the created order
                Symbol = "ES",
                State = OrderState.Filled
            };
            _mockAdapter.FireOrderUpdate(statusUpdate);

            // Assert that callback was called
            Assert.IsTrue(callbackCalled, "Callback should have been called after subscription and order update");

            // Reset flag
            callbackCalled = false;

            // Act - unsubscribe
            _orderManager.UnsubscribeFromOrderUpdates(callback);

            // Simulate another order update for the same order
            var statusUpdate2 = new OrderStatus
            {
                OrderId = orderId,  // Use the same order ID
                Symbol = "ES",
                State = OrderState.Cancelled
            };
            _mockAdapter.FireOrderUpdate(statusUpdate2);

            // Assert that callback was NOT called after unsubscribe
            Assert.IsFalse(callbackCalled, "Callback should NOT have been called after unsubscription");
        }

        [TestMethod]
        public void SubscribeToOrderUpdates_NullCallback_ThrowsArgumentNullException()
        {
            // Act & Assert
            Assert.ThrowsException<ArgumentNullException>(
                () => _orderManager.SubscribeToOrderUpdates(null));
        }

        [TestMethod]
        public void UnsubscribeFromOrderUpdates_NullCallback_ThrowsArgumentNullException()
        {
            // Act & Assert
            Assert.ThrowsException<ArgumentNullException>(
                () => _orderManager.UnsubscribeFromOrderUpdates(null));
        }

        [TestMethod]
        public async Task OrderStateTransition_ValidTransitions_AreAllowed()
        {
            // Arrange - create an order and submit it
            var request = new OrderRequest
            {
                Symbol = "ES",
                Side = OrderSide.Buy,
                Type = OrderType.Market,
                Quantity = 1,
                ClientOrderId = "TEST123"
            };

            var submitResult = await _orderManager.SubmitOrderAsync(request);
            Assert.IsNotNull(submitResult.OrderId);

            // Act - simulate state updates through the mock adapter
            var pendingStatus = new OrderStatus
            {
                OrderId = submitResult.OrderId,
                Symbol = "ES",
                State = OrderState.Pending
            };

            var submittedStatus = new OrderStatus
            {
                OrderId = submitResult.OrderId,
                Symbol = "ES",
                State = OrderState.Submitted
            };

            var acceptedStatus = new OrderStatus
            {
                OrderId = submitResult.OrderId,
                Symbol = "ES",
                State = OrderState.Accepted
            };

            var workingStatus = new OrderStatus
            {
                OrderId = submitResult.OrderId,
                Symbol = "ES",
                State = OrderState.Working
            };

            // Simulate the state transitions
            _mockAdapter.FireOrderUpdate(pendingStatus);
            _mockAdapter.FireOrderUpdate(submittedStatus);
            _mockAdapter.FireOrderUpdate(acceptedStatus);
            _mockAdapter.FireOrderUpdate(workingStatus);

            // Assert - get the final status and verify it's in working state
            var finalStatus = await _orderManager.GetOrderStatusAsync(submitResult.OrderId);
            Assert.AreEqual(OrderState.Working, finalStatus.State);
        }

        [TestMethod]
        public async Task Dispose_DisposesResources()
        {
            // Arrange
            var orderManager = new BasicOrderManager(_mockLogger, _mockAdapter);

            // Act
            orderManager.Dispose();

            // Assert - no exception should be thrown
            // Additional assertions could check if resources were properly cleaned up
        }
    }
}
