using Microsoft.VisualStudio.TestTools.UnitTesting;
using NT8.Core.Common.Models;
using NT8.Core.Logging;
using NT8.Core.Sizing;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Threading.Tasks;

namespace NT8.Core.Tests.Sizing
{
    [TestClass]
    public class AdvancedPositionSizerPerformanceTests
    {
        private TestLogger _logger;
        private AdvancedPositionSizer _positionSizer;

        [TestInitialize]
        public void TestInitialize()
        {
            _logger = new TestLogger();
            _positionSizer = new AdvancedPositionSizer(_logger);
        }

        [TestMethod]
        public void AdvancedPositionSizer_Performance_MetricsRecording()
        {
            // Arrange
            var intent = CreateValidIntent();
            var context = CreateTestContext();
            var config = CreateTestSizingConfig(SizingMethod.OptimalF);

            // Act
            var stopwatch = Stopwatch.StartNew();
            var result = _positionSizer.CalculateSize(intent, context, config);
            stopwatch.Stop();

            // Assert
            Assert.IsNotNull(result);
            Assert.IsTrue(result.Contracts >= 0);

            // Check that metrics were recorded
            var metricsSnapshot = _positionSizer.GetMetricsSnapshot();
            Assert.IsNotNull(metricsSnapshot);
            Assert.IsTrue(metricsSnapshot.TotalOperations >= 1);
            Assert.IsTrue(metricsSnapshot.OptimalFOperations >= 1);
            Assert.IsTrue(metricsSnapshot.TotalProcessingTimeMs >= 0);
            Assert.IsTrue(metricsSnapshot.AverageProcessingTimeMs >= 0);
        }

        [TestMethod]
        public void AdvancedPositionSizer_Performance_ObjectPooling()
        {
            // Arrange
            var intent = CreateValidIntent();
            var context = CreateTestContext();
            var config = CreateTestSizingConfig(SizingMethod.KellyCriterion);

            // Act & Assert
            // Run multiple calculations to test object pooling
            for (int i = 0; i < 100; i++)
            {
                var result = _positionSizer.CalculateSize(intent, context, config);
                Assert.IsNotNull(result);
                Assert.IsTrue(result.Contracts >= 0);
            }

            // Check that we still have reasonable performance
            var metricsSnapshot = _positionSizer.GetMetricsSnapshot();
            Assert.IsTrue(metricsSnapshot.TotalOperations >= 100);
            Assert.IsTrue(metricsSnapshot.AverageProcessingTimeMs < 100); // Should be fast with pooling
        }

        [TestMethod]
        public async Task AdvancedPositionSizer_Performance_ConcurrentAccess()
        {
            // Arrange
            var tasks = new List<Task<SizingResult>>();
            var intent = CreateValidIntent();
            var context = CreateTestContext();
            var config = CreateTestSizingConfig(SizingMethod.VolatilityAdjusted);

            // Act
            var stopwatch = Stopwatch.StartNew();
            for (int i = 0; i < 50; i++)
            {
                var task = Task.Run(() => _positionSizer.CalculateSize(intent, context, config));
                tasks.Add(task);
            }

            var results = await Task.WhenAll(tasks);
            stopwatch.Stop();

            // Assert
            Assert.AreEqual(50, results.Length);
            foreach (var result in results)
            {
                Assert.IsNotNull(result);
                Assert.IsTrue(result.Contracts >= 0);
            }

            // Check metrics
            var metricsSnapshot = _positionSizer.GetMetricsSnapshot();
            Assert.IsTrue(metricsSnapshot.TotalOperations >= 50);
            Assert.IsTrue(metricsSnapshot.VolatilityAdjustedOperations >= 50);
        }

        [TestMethod]
        public void AdvancedPositionSizer_Performance_Throughput()
        {
            // Arrange
            var intent = CreateValidIntent();
            var context = CreateTestContext();
            var config = CreateTestSizingConfig(SizingMethod.OptimalF);

            // Act
            var stopwatch = Stopwatch.StartNew();
            const int iterations = 1000;

            for (int i = 0; i < iterations; i++)
            {
                var result = _positionSizer.CalculateSize(intent, context, config);
                Assert.IsNotNull(result);
            }

            stopwatch.Stop();

            // Assert
            var metricsSnapshot = _positionSizer.GetMetricsSnapshot();
            Assert.IsTrue(metricsSnapshot.TotalOperations >= iterations);

            // Calculate throughput
            var throughput = (double)iterations / stopwatch.Elapsed.TotalSeconds;
            Assert.IsTrue(throughput > 100); // Should process at least 100 operations per second

            _logger.LogInformation(String.Format("Processed {0} operations in {1:F2} ms ({2:F2} ops/sec)",
                iterations, stopwatch.Elapsed.TotalMilliseconds, throughput));
        }

        [TestMethod]
        public void AdvancedPositionSizer_Performance_MemoryAllocation()
        {
            // Arrange
            var intent = CreateValidIntent();
            var context = CreateTestContext();
            var config = CreateTestSizingConfig(SizingMethod.KellyCriterion);

            // Force garbage collection to get a clean baseline
            GC.Collect();
            GC.WaitForPendingFinalizers();
            GC.Collect();

            var initialMemory = GC.GetTotalMemory(false);

            // Act
            for (int i = 0; i < 1000; i++)
            {
                var result = _positionSizer.CalculateSize(intent, context, config);
                Assert.IsNotNull(result);
            }

            // Force garbage collection
            GC.Collect();
            GC.WaitForPendingFinalizers();
            var finalMemory = GC.GetTotalMemory(false);

            // Assert - with object pooling, memory growth should be minimal
            var memoryGrowth = finalMemory - initialMemory;
            Assert.IsTrue(memoryGrowth < 10 * 1024 * 1024); // Less than 10MB growth for 1000 operations

            _logger.LogInformation(String.Format("Memory growth: {0:F2} KB for 1000 operations",
                memoryGrowth / 1024.0));
        }

        [TestMethod]
        public void AdvancedPositionSizer_Performance_MetricsConsistency()
        {
            // Arrange
            var intent = CreateValidIntent();
            var context = CreateTestContext();
            var config1 = CreateTestSizingConfig(SizingMethod.OptimalF);
            var config2 = CreateTestSizingConfig(SizingMethod.KellyCriterion);
            var config3 = CreateTestSizingConfig(SizingMethod.VolatilityAdjusted);

            // Act
            // Run mixed operations
            for (int i = 0; i < 30; i++)
            {
                _positionSizer.CalculateSize(intent, context, config1);
                _positionSizer.CalculateSize(intent, context, config2);
                _positionSizer.CalculateSize(intent, context, config3);
            }

            // Assert
            var metricsSnapshot = _positionSizer.GetMetricsSnapshot();
            Assert.IsTrue(metricsSnapshot.TotalOperations >= 90);
            Assert.IsTrue(metricsSnapshot.OptimalFOperations >= 30);
            Assert.IsTrue(metricsSnapshot.KellyCriterionOperations >= 30);
            Assert.IsTrue(metricsSnapshot.VolatilityAdjustedOperations >= 30);

            // Verify timing metrics are reasonable
            Assert.IsTrue(metricsSnapshot.MaxProcessingTimeMs >= metricsSnapshot.MinProcessingTimeMs);
            Assert.IsTrue(metricsSnapshot.AverageProcessingTimeMs >= 0);
        }

        #region Helper Methods

        private StrategyIntent CreateValidIntent(
            string symbol = "ES",
            int stopTicks = 8,
            OrderSide side = OrderSide.Buy)
        {
            return new StrategyIntent(
                symbol: symbol,
                side: side,
                entryType: OrderType.Market,
                limitPrice: null,
                stopTicks: stopTicks,
                targetTicks: 16,
                confidence: 0.8,
                reason: "Test intent",
                metadata: new Dictionary<string, object>()
            );
        }

        private StrategyContext CreateTestContext(string symbol = "ES")
        {
            return new StrategyContext(
                symbol: symbol,
                currentTime: DateTime.UtcNow,
                currentPosition: new Position(symbol, 0, 0, 0, 0, DateTime.UtcNow),
                account: new AccountInfo(50000, 50000, 0, 0, DateTime.UtcNow),
                session: new MarketSession(DateTime.Today.AddHours(9.5), DateTime.Today.AddHours(16), true, "RTH"),
                customData: new Dictionary<string, object>()
            );
        }

        private SizingConfig CreateTestSizingConfig(SizingMethod method)
        {
            var methodParameters = new Dictionary<string, object>();
            if (method == SizingMethod.KellyCriterion)
            {
                methodParameters.Add("kelly_fraction", 0.5);
            }

            return new SizingConfig(
                method: method,
                minContracts: 1,
                maxContracts: 10,
                riskPerTrade: 500,
                methodParameters: methodParameters
            );
        }

        #endregion
    }

    /// <summary>
    /// Test implementation of ILogger for testing
    /// </summary>
    public class TestLogger : ILogger
    {
        public void LogCritical(string message, params object[] args)
        {
            // No-op for testing
        }

        public void LogDebug(string message, params object[] args)
        {
            // No-op for testing
        }

        public void LogError(string message, params object[] args)
        {
            // No-op for testing
        }

        public void LogError(Exception exception, string message, params object[] args)
        {
            // No-op for testing
        }

        public void LogInformation(string message, params object[] args)
        {
            // No-op for testing
            Console.WriteLine("[INFO] " + message, args);
        }

        public void LogWarning(string message, params object[] args)
        {
            // No-op for testing
        }

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