using System;
using System.Collections.Generic;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using NT8.Adapters.NinjaTrader;
using NT8.Core.Common.Models;

namespace NT8.Core.Tests.Adapters
{
    /// <summary>
    /// Unit tests for NT8DataConverter.
    /// </summary>
    [TestClass]
    public class NT8DataConverterTests
    {
        [TestMethod]
        public void ConvertBar_WithValidESBar_ShouldCreateBarData()
        {
            var time = new DateTime(2026, 2, 17, 9, 30, 0, DateTimeKind.Utc);

            var result = NT8DataConverter.ConvertBar("ES", time, 4200.0, 4210.0, 4195.0, 4208.0, 10000, 5);

            Assert.AreEqual("ES", result.Symbol);
            Assert.AreEqual(time, result.Time);
            Assert.AreEqual(4200.0, result.Open);
            Assert.AreEqual(4210.0, result.High);
            Assert.AreEqual(4195.0, result.Low);
            Assert.AreEqual(4208.0, result.Close);
            Assert.AreEqual(10000L, result.Volume);
            Assert.AreEqual(TimeSpan.FromMinutes(5), result.BarSize);
        }

        [DataTestMethod]
        [DataRow(null)]
        [DataRow("")]
        [DataRow("   ")]
        public void ConvertBar_WithInvalidSymbol_ShouldThrowArgumentException(string symbol)
        {
            var ex = Assert.ThrowsException<ArgumentException>(
                delegate
                {
                    NT8DataConverter.ConvertBar(symbol, DateTime.UtcNow, 1, 2, 0.5, 1.5, 10, 5);
                });

            Assert.AreEqual("symbol", ex.Message);
        }

        [DataTestMethod]
        [DataRow(0)]
        [DataRow(-1)]
        [DataRow(-60)]
        public void ConvertBar_WithInvalidBarSize_ShouldThrowArgumentException(int barSize)
        {
            var ex = Assert.ThrowsException<ArgumentException>(
                delegate
                {
                    NT8DataConverter.ConvertBar("ES", DateTime.UtcNow, 1, 2, 0.5, 1.5, 10, barSize);
                });

            Assert.AreEqual("barSizeMinutes", ex.Message);
        }

        [TestMethod]
        public void ConvertBar_WithDifferentTimeframes_ShouldSetCorrectBarSize()
        {
            var sizes = new[] { 1, 5, 15, 30, 60, 240, 1440 };

            for (var i = 0; i < sizes.Length; i++)
            {
                var value = sizes[i];
                var result = NT8DataConverter.ConvertBar("ES", DateTime.UtcNow, 1, 2, 0.5, 1.5, 10, value);
                Assert.AreEqual(TimeSpan.FromMinutes(value), result.BarSize);
            }
        }

        [TestMethod]
        public void ConvertBar_WithHighLessThanLow_ShouldStillCreate()
        {
            var result = NT8DataConverter.ConvertBar("ES", DateTime.UtcNow, 100, 95, 105, 99, 1000, 5);

            Assert.AreEqual(95.0, result.High);
            Assert.AreEqual(105.0, result.Low);
        }

        [TestMethod]
        public void ConvertBar_WithZeroVolume_ShouldCreateBar()
        {
            var result = NT8DataConverter.ConvertBar("MES", DateTime.UtcNow, 5000, 5005, 4995, 5001, 0, 1);

            Assert.AreEqual(0L, result.Volume);
        }

        [TestMethod]
        public void ConvertBar_WithNegativePrices_ShouldHandleCorrectly()
        {
            var result = NT8DataConverter.ConvertBar("ZN", DateTime.UtcNow, -1.2, -0.9, -1.4, -1.0, 2500, 5);

            Assert.AreEqual(-1.2, result.Open);
            Assert.AreEqual(-0.9, result.High);
            Assert.AreEqual(-1.4, result.Low);
            Assert.AreEqual(-1.0, result.Close);
        }

        [TestMethod]
        public void ConvertBar_WithLargeVolume_ShouldHandleCorrectly()
        {
            var result = NT8DataConverter.ConvertBar("NQ", DateTime.UtcNow, 100, 110, 95, 108, 10000000, 5);

            Assert.AreEqual(10000000L, result.Volume);
        }

        [TestMethod]
        public void ConvertAccount_WithPositiveValues_ShouldCreateAccountInfo()
        {
            var now = DateTime.UtcNow;
            var result = NT8DataConverter.ConvertAccount(100000, 250000, 1250.50, 0.05, now);

            Assert.AreEqual(100000.0, result.Equity);
            Assert.AreEqual(250000.0, result.BuyingPower);
            Assert.AreEqual(1250.50, result.DailyPnL);
            Assert.AreEqual(0.05, result.MaxDrawdown);
            Assert.AreEqual(now, result.LastUpdate);
        }

        [TestMethod]
        public void ConvertAccount_WithNegativePnL_ShouldHandleCorrectly()
        {
            var result = NT8DataConverter.ConvertAccount(100000, 250000, -2500.75, 0.05, DateTime.UtcNow);

            Assert.AreEqual(-2500.75, result.DailyPnL);
        }

        [TestMethod]
        public void ConvertAccount_WithZeroValues_ShouldCreateAccount()
        {
            var result = NT8DataConverter.ConvertAccount(0, 0, 0, 0, DateTime.UtcNow);

            Assert.AreEqual(0.0, result.Equity);
            Assert.AreEqual(0.0, result.BuyingPower);
            Assert.AreEqual(0.0, result.DailyPnL);
            Assert.AreEqual(0.0, result.MaxDrawdown);
        }

        [TestMethod]
        public void ConvertAccount_WithLargeEquity_ShouldHandleCorrectly()
        {
            var result = NT8DataConverter.ConvertAccount(10000000, 25000000, 5000, 100000, DateTime.UtcNow);

            Assert.AreEqual(10000000.0, result.Equity);
            Assert.AreEqual(25000000.0, result.BuyingPower);
        }

        [TestMethod]
        public void ConvertPosition_WithLongPosition_ShouldCreatePosition()
        {
            var result = NT8DataConverter.ConvertPosition("ES", 2, 4200.50, 250, 500, DateTime.UtcNow);

            Assert.AreEqual("ES", result.Symbol);
            Assert.IsTrue(result.Quantity > 0);
            Assert.AreEqual(2, result.Quantity);
        }

        [TestMethod]
        public void ConvertPosition_WithShortPosition_ShouldHandleNegativeQuantity()
        {
            var result = NT8DataConverter.ConvertPosition("ES", -1, 4200.50, -150, 200, DateTime.UtcNow);

            Assert.IsTrue(result.Quantity < 0);
            Assert.AreEqual(-1, result.Quantity);
        }

        [TestMethod]
        public void ConvertPosition_WithFlatPosition_ShouldHandleZeroQuantity()
        {
            var result = NT8DataConverter.ConvertPosition("ES", 0, 0, 0, 0, DateTime.UtcNow);

            Assert.AreEqual(0, result.Quantity);
            Assert.AreEqual(0.0, result.AveragePrice);
        }

        [DataTestMethod]
        [DataRow(null)]
        [DataRow("")]
        [DataRow("   ")]
        public void ConvertPosition_WithInvalidSymbol_ShouldThrowArgumentException(string symbol)
        {
            var ex = Assert.ThrowsException<ArgumentException>(
                delegate
                {
                    NT8DataConverter.ConvertPosition(symbol, 1, 1, 1, 1, DateTime.UtcNow);
                });

            Assert.AreEqual("symbol", ex.Message);
        }

        [TestMethod]
        public void ConvertPosition_WithNegativeUnrealizedPnL_ShouldHandleCorrectly()
        {
            var result = NT8DataConverter.ConvertPosition("ES", 1, 4200.50, -350.25, 20, DateTime.UtcNow);

            Assert.AreEqual(-350.25, result.UnrealizedPnL);
        }

        [TestMethod]
        public void ConvertSession_WithRTHSession_ShouldCreateMarketSession()
        {
            var start = new DateTime(2026, 2, 17, 9, 30, 0, DateTimeKind.Utc);
            var end = new DateTime(2026, 2, 17, 16, 0, 0, DateTimeKind.Utc);

            var result = NT8DataConverter.ConvertSession(start, end, true, "RTH");

            Assert.IsTrue(result.IsRth);
            Assert.AreEqual("RTH", result.SessionName);
        }

        [TestMethod]
        public void ConvertSession_WithETHSession_ShouldCreateMarketSession()
        {
            var start = new DateTime(2026, 2, 17, 18, 0, 0, DateTimeKind.Utc);
            var end = new DateTime(2026, 2, 18, 9, 30, 0, DateTimeKind.Utc);

            var result = NT8DataConverter.ConvertSession(start, end, false, "ETH");

            Assert.IsFalse(result.IsRth);
            Assert.AreEqual(start, result.SessionStart);
            Assert.AreEqual(end, result.SessionEnd);
        }

        [DataTestMethod]
        [DataRow(null)]
        [DataRow("")]
        [DataRow("   ")]
        public void ConvertSession_WithInvalidName_ShouldThrowArgumentException(string name)
        {
            var ex = Assert.ThrowsException<ArgumentException>(
                delegate
                {
                    NT8DataConverter.ConvertSession(DateTime.UtcNow, DateTime.UtcNow.AddHours(1), true, name);
                });

            Assert.AreEqual("sessionName", ex.Message);
        }

        [TestMethod]
        public void ConvertSession_WithEndBeforeStart_ShouldThrowArgumentException()
        {
            var start = new DateTime(2026, 2, 17, 16, 0, 0, DateTimeKind.Utc);
            var end = new DateTime(2026, 2, 17, 9, 30, 0, DateTimeKind.Utc);

            var ex = Assert.ThrowsException<ArgumentException>(
                delegate
                {
                    NT8DataConverter.ConvertSession(start, end, true, "RTH");
                });

            Assert.AreEqual("sessionEnd", ex.Message);
        }

        [TestMethod]
        public void ConvertContext_WithValidInputs_ShouldCreateStrategyContext()
        {
            var position = CreatePosition();
            var account = CreateAccount();
            var session = CreateSession();
            var customData = new Dictionary<string, object>();
            customData.Add("a", 1);
            customData.Add("b", "value");

            var result = NT8DataConverter.ConvertContext("ES", DateTime.UtcNow, position, account, session, customData);

            Assert.AreEqual("ES", result.Symbol);
            Assert.AreEqual(position, result.CurrentPosition);
            Assert.AreEqual(account, result.Account);
            Assert.AreEqual(session, result.Session);
            Assert.AreEqual(2, result.CustomData.Count);
            Assert.AreEqual(1, (int)result.CustomData["a"]);
            Assert.AreEqual("value", (string)result.CustomData["b"]);
        }

        [TestMethod]
        public void ConvertContext_WithNullCustomData_ShouldCreateEmptyDictionary()
        {
            var result = NT8DataConverter.ConvertContext("ES", DateTime.UtcNow, CreatePosition(), CreateAccount(), CreateSession(), null);

            Assert.IsNotNull(result.CustomData);
            Assert.AreEqual(0, result.CustomData.Count);
        }

        [DataTestMethod]
        [DataRow(null)]
        [DataRow("")]
        [DataRow("   ")]
        public void ConvertContext_WithInvalidSymbol_ShouldThrowArgumentException(string symbol)
        {
            var ex = Assert.ThrowsException<ArgumentException>(
                delegate
                {
                    NT8DataConverter.ConvertContext(symbol, DateTime.UtcNow, CreatePosition(), CreateAccount(), CreateSession(), null);
                });

            Assert.AreEqual("symbol", ex.Message);
        }

        [TestMethod]
        public void ConvertContext_WithNullPosition_ShouldThrowArgumentNullException()
        {
            var ex = Assert.ThrowsException<ArgumentNullException>(
                delegate
                {
                    NT8DataConverter.ConvertContext("ES", DateTime.UtcNow, null, CreateAccount(), CreateSession(), null);
                });

            Assert.AreEqual("currentPosition", ex.ParamName);
        }

        [TestMethod]
        public void ConvertContext_WithNullAccount_ShouldThrowArgumentNullException()
        {
            var ex = Assert.ThrowsException<ArgumentNullException>(
                delegate
                {
                    NT8DataConverter.ConvertContext("ES", DateTime.UtcNow, CreatePosition(), null, CreateSession(), null);
                });

            Assert.AreEqual("account", ex.ParamName);
        }

        [TestMethod]
        public void ConvertContext_WithNullSession_ShouldThrowArgumentNullException()
        {
            var ex = Assert.ThrowsException<ArgumentNullException>(
                delegate
                {
                    NT8DataConverter.ConvertContext("ES", DateTime.UtcNow, CreatePosition(), CreateAccount(), null, null);
                });

            Assert.AreEqual("session", ex.ParamName);
        }

        private static Position CreatePosition()
        {
            return new Position("ES", 1, 4200.0, 10.0, 5.0, DateTime.UtcNow);
        }

        private static AccountInfo CreateAccount()
        {
            return new AccountInfo(100000.0, 250000.0, 500.0, 2500.0, DateTime.UtcNow);
        }

        private static MarketSession CreateSession()
        {
            return new MarketSession(DateTime.UtcNow.Date.AddHours(9.5), DateTime.UtcNow.Date.AddHours(16), true, "RTH");
        }
    }
}
