using NT8.Core.Common.Models;
using NT8.Core.Intelligence;
using NT8.Core.Logging;
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;

namespace NT8.Core.Sizing
{
    /// <summary>
    /// Advanced position sizer with Optimal f, Kelly Criterion, and volatility-adjusted methods
    /// Implements sophisticated position sizing algorithms for professional trading
    /// </summary>
    public class AdvancedPositionSizer : IPositionSizer
    {
        private readonly ILogger _logger;

        // Performance metrics
        private readonly SizingMetrics _metrics = new SizingMetrics();

        // Object pools for frequently used objects
        private readonly ConcurrentQueue<Dictionary<string, object>> _dictionaryPool = new ConcurrentQueue<Dictionary<string, object>>();
        private readonly ConcurrentQueue<List<TradeResult>> _tradeListPool = new ConcurrentQueue<List<TradeResult>>();

        // Pool sizes
        private const int MaxPoolSize = 100;

        public AdvancedPositionSizer(ILogger logger)
        {
            if (logger == null) throw new ArgumentNullException("logger");
            _logger = logger;
        }

        public SizingResult CalculateSize(StrategyIntent intent, StrategyContext context, SizingConfig config)
        {
            if (intent == null) throw new ArgumentNullException("intent");
            if (context == null) throw new ArgumentNullException("context");
            if (config == null) throw new ArgumentNullException("config");

            var startTime = DateTime.UtcNow;

            // Validate intent is suitable for sizing
            if (!intent.IsValid())
            {
                _logger.LogWarning("Invalid strategy intent provided for sizing: {0}", intent);

                Dictionary<string, object> errorCalcs;
                if (!_dictionaryPool.TryDequeue(out errorCalcs))
                {
                    errorCalcs = new Dictionary<string, object>();
                }
                errorCalcs.Clear();
                errorCalcs.Add("error", "Invalid intent");

                var result = new SizingResult(0, 0, config.Method, errorCalcs);

                // Record metrics
                var endTime = DateTime.UtcNow;
                var processingTime = (endTime - startTime).TotalMilliseconds;
                _metrics.RecordOperation(config.Method, (long)processingTime);

                return result;
            }

            SizingResult sizingResult;

            switch (config.Method)
            {
                case SizingMethod.OptimalF:
                    sizingResult = CalculateOptimalF(intent, context, config);
                    break;
                case SizingMethod.KellyCriterion:
                    sizingResult = CalculateKellyCriterion(intent, context, config);
                    break;
                case SizingMethod.VolatilityAdjusted:
                    sizingResult = CalculateVolatilityAdjustedSizing(intent, context, config);
                    break;
                default:
                    throw new NotSupportedException(String.Format("Sizing method {0} not supported in AdvancedPositionSizer", config.Method));
            }

            // Record metrics
            var endTime2 = DateTime.UtcNow;
            var processingTime2 = (endTime2 - startTime).TotalMilliseconds;
            _metrics.RecordOperation(config.Method, (long)processingTime2);

            return sizingResult;
        }

        private SizingResult CalculateOptimalF(StrategyIntent intent, StrategyContext context, SizingConfig config)
        {
            // Get trade history for calculating Optimal f
            List<TradeResult> tradeHistory;
            if (!_tradeListPool.TryDequeue(out tradeHistory))
            {
                tradeHistory = new List<TradeResult>();
            }
            tradeHistory.Clear();
            tradeHistory.AddRange(GetRecentTradeHistory(context, config));

            if (tradeHistory.Count == 0)
            {
                // Return trade history to pool
                if (_tradeListPool.Count < MaxPoolSize)
                {
                    _tradeListPool.Enqueue(tradeHistory);
                }

                // Fall back to fixed risk if no trade history
                return CalculateFixedRiskFallback(intent, context, config);
            }

            // Calculate Optimal f
            var optimalF = CalculateOptimalFValue(tradeHistory);

            // Get account information
            var equity = context.Account.Equity;
            var maxLoss = GetMaximumLossFromHistory(tradeHistory);

            // Calculate optimal contracts using Optimal f formula
            // Contracts = (Optimal f * Equity) / Max Loss
            var optimalContracts = (optimalF * equity) / Math.Abs(maxLoss);

            // Round down to whole contracts (conservative approach)
            var contracts = (int)Math.Floor(optimalContracts);

            // Apply min/max clamping
            contracts = Math.Max(config.MinContracts, Math.Min(config.MaxContracts, contracts));

            // Calculate actual risk with final contract count
            var tickValue = GetTickValue(intent.Symbol);
            var riskPerContract = intent.StopTicks * tickValue;
            var actualRisk = contracts * riskPerContract;

            _logger.LogDebug("Optimal f sizing: {0} f={1:F4} ${2:F2}→{3:F2}→{4} contracts, ${5:F2} actual risk",
                intent.Symbol, optimalF, equity, optimalContracts, contracts, actualRisk);

            Dictionary<string, object> calculations;
            if (!_dictionaryPool.TryDequeue(out calculations))
            {
                calculations = new Dictionary<string, object>();
            }
            calculations.Clear();
            calculations.Add("optimal_f", optimalF);
            calculations.Add("equity", equity);
            calculations.Add("max_loss", maxLoss);
            calculations.Add("optimal_contracts", optimalContracts);
            calculations.Add("clamped_contracts", contracts);
            calculations.Add("stop_ticks", intent.StopTicks);
            calculations.Add("tick_value", tickValue);
            calculations.Add("risk_per_contract", riskPerContract);
            calculations.Add("actual_risk", actualRisk);
            calculations.Add("min_contracts", config.MinContracts);
            calculations.Add("max_contracts", config.MaxContracts);

            // Return trade history to pool
            if (_tradeListPool.Count < MaxPoolSize)
            {
                _tradeListPool.Enqueue(tradeHistory);
            }

            var result = new SizingResult(
                contracts: contracts,
                riskAmount: actualRisk,
                method: SizingMethod.OptimalF,
                calculations: calculations
            );

            return result;
        }

        private SizingResult CalculateKellyCriterion(StrategyIntent intent, StrategyContext context, SizingConfig config)
        {
            // Get trade history for calculating win rate and average win/loss
            List<TradeResult> tradeHistory;
            if (!_tradeListPool.TryDequeue(out tradeHistory))
            {
                tradeHistory = new List<TradeResult>();
            }
            tradeHistory.Clear();
            tradeHistory.AddRange(GetRecentTradeHistory(context, config));

            if (tradeHistory.Count == 0)
            {
                // Return trade history to pool
                if (_tradeListPool.Count < MaxPoolSize)
                {
                    _tradeListPool.Enqueue(tradeHistory);
                }

                // Fall back to fixed risk if no trade history
                return CalculateFixedRiskFallback(intent, context, config);
            }

            // Calculate Kelly Criterion parameters
            var winRate = CalculateWinRate(tradeHistory);
            var avgWin = CalculateAverageWin(tradeHistory);
            var avgLoss = CalculateAverageLoss(tradeHistory);

            // Calculate Kelly Criterion fraction
            // K = (bp - q) / b
            // Where: b = avgWin/avgLoss (odds), p = winRate, q = 1 - winRate
            var odds = avgWin / Math.Abs(avgLoss);
            var kellyFraction = ((odds * winRate) - (1 - winRate)) / odds;

            // Apply fractional Kelly to reduce risk (typically use 25%-50% of full Kelly)
            var fractionalKelly = GetParameterValue<double>(config, "kelly_fraction", 0.5);
            var adjustedKelly = kellyFraction * fractionalKelly;

            // Calculate position size based on Kelly Criterion
            var equity = context.Account.Equity;
            var tickValue = GetTickValue(intent.Symbol);
            var riskPerContract = intent.StopTicks * tickValue;

            // Kelly position size = (Kelly Fraction * Equity) / Risk per contract
            var kellyContracts = (adjustedKelly * equity) / riskPerContract;
            var contracts = (int)Math.Floor(Math.Abs(kellyContracts));

            // Apply min/max clamping
            contracts = Math.Max(config.MinContracts, Math.Min(config.MaxContracts, contracts));

            // Calculate actual risk with final contract count
            var actualRisk = contracts * riskPerContract;

            _logger.LogDebug("Kelly Criterion sizing: {0} K={1:F4} adj={2:F4} ${3:F2}→{4:F2}→{5} contracts, ${6:F2} actual risk",
                intent.Symbol, kellyFraction, adjustedKelly, equity, kellyContracts, contracts, actualRisk);

            Dictionary<string, object> calculations;
            if (!_dictionaryPool.TryDequeue(out calculations))
            {
                calculations = new Dictionary<string, object>();
            }
            calculations.Clear();
            calculations.Add("win_rate", winRate);
            calculations.Add("avg_win", avgWin);
            calculations.Add("avg_loss", avgLoss);
            calculations.Add("odds", odds);
            calculations.Add("kelly_fraction", kellyFraction);
            calculations.Add("fractional_kelly", fractionalKelly);
            calculations.Add("adjusted_kelly", adjustedKelly);
            calculations.Add("equity", equity);
            calculations.Add("tick_value", tickValue);
            calculations.Add("risk_per_contract", riskPerContract);
            calculations.Add("kelly_contracts", kellyContracts);
            calculations.Add("clamped_contracts", contracts);
            calculations.Add("actual_risk", actualRisk);
            calculations.Add("min_contracts", config.MinContracts);
            calculations.Add("max_contracts", config.MaxContracts);

            // Return trade history to pool
            if (_tradeListPool.Count < MaxPoolSize)
            {
                _tradeListPool.Enqueue(tradeHistory);
            }

            var result = new SizingResult(
                contracts: contracts,
                riskAmount: actualRisk,
                method: SizingMethod.KellyCriterion,
                calculations: calculations
            );

            return result;
        }

        private SizingResult CalculateVolatilityAdjustedSizing(StrategyIntent intent, StrategyContext context, SizingConfig config)
        {
            // Get volatility information
            var atr = CalculateATR(context, intent.Symbol, 14); // 14-period ATR
            var tickValue = GetTickValue(intent.Symbol);

            // Get base risk from configuration
            var baseRisk = config.RiskPerTrade;

            // Apply volatility adjustment
            // Higher volatility = lower position size, Lower volatility = higher position size
            var volatilityAdjustment = CalculateVolatilityAdjustment(atr, intent.Symbol);
            var adjustedRisk = baseRisk * volatilityAdjustment;

            // Calculate contracts based on adjusted risk
            var riskPerContract = intent.StopTicks * tickValue;
            var optimalContracts = adjustedRisk / riskPerContract;
            var contracts = (int)Math.Floor(optimalContracts);

            // Apply min/max clamping
            contracts = Math.Max(config.MinContracts, Math.Min(config.MaxContracts, contracts));

            // Calculate actual risk with final contract count
            var actualRisk = contracts * riskPerContract;

            _logger.LogDebug("Volatility-adjusted sizing: {0} ATR={1:F4} adj={2:F4} ${3:F2}→${4:F2}→{5} contracts, ${6:F2} actual risk",
                intent.Symbol, atr, volatilityAdjustment, baseRisk, adjustedRisk, contracts, actualRisk);

            Dictionary<string, object> calculations;
            if (!_dictionaryPool.TryDequeue(out calculations))
            {
                calculations = new Dictionary<string, object>();
            }
            calculations.Clear();
            calculations.Add("atr", atr);
            calculations.Add("volatility_adjustment", volatilityAdjustment);
            calculations.Add("base_risk", baseRisk);
            calculations.Add("adjusted_risk", adjustedRisk);
            calculations.Add("tick_value", tickValue);
            calculations.Add("risk_per_contract", riskPerContract);
            calculations.Add("optimal_contracts", optimalContracts);
            calculations.Add("clamped_contracts", contracts);
            calculations.Add("actual_risk", actualRisk);
            calculations.Add("stop_ticks", intent.StopTicks);
            calculations.Add("min_contracts", config.MinContracts);
            calculations.Add("max_contracts", config.MaxContracts);

            var result = new SizingResult(
                contracts: contracts,
                riskAmount: actualRisk,
                method: SizingMethod.VolatilityAdjusted,
                calculations: calculations
            );

            return result;
        }

        private SizingResult CalculateFixedRiskFallback(StrategyIntent intent, StrategyContext context, SizingConfig config)
        {
            var tickValue = GetTickValue(intent.Symbol);

            // Validate stop ticks
            if (intent.StopTicks <= 0)
            {
                _logger.LogWarning("Invalid stop ticks {0} for fixed risk sizing on {1}",
                    intent.StopTicks, intent.Symbol);

                Dictionary<string, object> errorCalcs;
                if (!_dictionaryPool.TryDequeue(out errorCalcs))
                {
                    errorCalcs = new Dictionary<string, object>();
                }
                errorCalcs.Clear();
                errorCalcs.Add("error", "Invalid stop ticks");
                errorCalcs.Add("stop_ticks", intent.StopTicks);

                var errorResult = new SizingResult(0, 0, SizingMethod.FixedDollarRisk, errorCalcs);

                return errorResult;
            }

            // Calculate optimal contracts for target risk
            var targetRisk = config.RiskPerTrade;
            var riskPerContract = intent.StopTicks * tickValue;
            var optimalContracts = targetRisk / riskPerContract;

            // Round down to whole contracts (conservative approach)
            var contracts = (int)Math.Floor(optimalContracts);

            // Apply min/max clamping
            contracts = Math.Max(config.MinContracts, Math.Min(config.MaxContracts, contracts));

            // Calculate actual risk with final contract count
            var actualRisk = contracts * riskPerContract;

            _logger.LogDebug("Fixed risk fallback sizing: {0} ${1:F2}→{2:F2}→{3} contracts, ${4:F2} actual risk",
                intent.Symbol, targetRisk, optimalContracts, contracts, actualRisk);

            Dictionary<string, object> calculations;
            if (!_dictionaryPool.TryDequeue(out calculations))
            {
                calculations = new Dictionary<string, object>();
            }
            calculations.Clear();
            calculations.Add("target_risk", targetRisk);
            calculations.Add("stop_ticks", intent.StopTicks);
            calculations.Add("tick_value", tickValue);
            calculations.Add("risk_per_contract", riskPerContract);
            calculations.Add("optimal_contracts", optimalContracts);
            calculations.Add("clamped_contracts", contracts);
            calculations.Add("actual_risk", actualRisk);
            calculations.Add("min_contracts", config.MinContracts);
            calculations.Add("max_contracts", config.MaxContracts);

            var result = new SizingResult(
                contracts: contracts,
                riskAmount: actualRisk,
                method: SizingMethod.FixedDollarRisk,
                calculations: calculations
            );

            return result;
        }

        private static double CalculateOptimalFValue(List<TradeResult> tradeHistory)
        {
            if (tradeHistory == null || tradeHistory.Count == 0)
                return 0.0;

            // Find the largest loss (in absolute terms)
            var largestLoss = Math.Abs(tradeHistory.Min(t => t.ProfitLoss));

            if (largestLoss == 0)
                return 0.0;

            // Calculate Optimal f using the formula:
            // f = (N*R - T) / (N*L)
            // Where: N = number of trades, R = average win, L = largest loss, T = total profit

            var n = tradeHistory.Count;
            var totalProfit = tradeHistory.Sum(t => t.ProfitLoss);
            var averageWin = tradeHistory.Where(t => t.ProfitLoss > 0).DefaultIfEmpty(new TradeResult()).Average(t => t.ProfitLoss);

            if (averageWin <= 0)
                return 0.0;

            var optimalF = (n * averageWin - totalProfit) / (n * largestLoss);

            // Ensure f is between 0 and 1
            return Math.Max(0.0, Math.Min(1.0, optimalF));
        }

        private static double CalculateWinRate(List<TradeResult> tradeHistory)
        {
            if (tradeHistory == null || tradeHistory.Count == 0)
                return 0.0;

            var winningTrades = tradeHistory.Count(t => t.ProfitLoss > 0);
            return (double)winningTrades / tradeHistory.Count;
        }

        private static double CalculateAverageWin(List<TradeResult> tradeHistory)
        {
            if (tradeHistory == null || tradeHistory.Count == 0)
                return 0.0;

            var winningTrades = tradeHistory.Where(t => t.ProfitLoss > 0).ToList();
            if (winningTrades.Count == 0)
                return 0.0;

            return winningTrades.Average(t => t.ProfitLoss);
        }

        private static double CalculateAverageLoss(List<TradeResult> tradeHistory)
        {
            if (tradeHistory == null || tradeHistory.Count == 0)
                return 0.0;

            var losingTrades = tradeHistory.Where(t => t.ProfitLoss < 0).ToList();
            if (losingTrades.Count == 0)
                return 0.0;

            return losingTrades.Average(t => t.ProfitLoss);
        }

        private static double GetMaximumLossFromHistory(List<TradeResult> tradeHistory)
        {
            if (tradeHistory == null || tradeHistory.Count == 0)
                return 0.0;

            return tradeHistory.Min(t => t.ProfitLoss);
        }

        private static double CalculateATR(StrategyContext context, string symbol, int periods)
        {
            // This would typically involve retrieving historical bar data
            // For this implementation, we'll use a simplified approach
            return 1.0; // Placeholder value
        }

        private static double CalculateVolatilityAdjustment(double atr, string symbol)
        {
            // Normalize ATR to a volatility adjustment factor
            // Higher ATR = lower adjustment (reduce position size)
            // Lower ATR = higher adjustment (increase position size)

            // This is a simplified example - in practice, you'd normalize against
            // historical ATR values for the specific symbol
            var normalizedATR = atr / 10.0; // Example normalization
            var adjustment = 1.0 / (1.0 + normalizedATR);

            // Ensure adjustment is between 0.1 and 2.0
            return Math.Max(0.1, Math.Min(2.0, adjustment));
        }

        private static List<TradeResult> GetRecentTradeHistory(StrategyContext context, SizingConfig config)
        {
            // In a real implementation, this would retrieve actual trade history
            // For this example, we'll return an empty list to trigger fallback behavior
            return new List<TradeResult>();
        }

        private static T GetParameterValue<T>(SizingConfig config, string key, T defaultValue)
        {
            if (config.MethodParameters.ContainsKey(key))
            {
                try
                {
                    return (T)Convert.ChangeType(config.MethodParameters[key], typeof(T));
                }
                catch
                {
                    // If conversion fails, return default
                    return defaultValue;
                }
            }

            return defaultValue;
        }

        private static double GetTickValue(string symbol)
        {
            // Static tick values for Phase 0 - will be configurable in Phase 1
            switch (symbol)
            {
                case "ES": return 12.50;   // E-mini S&P 500
                case "MES": return 1.25;   // Micro E-mini S&P 500
                case "NQ": return 5.00;    // E-mini NASDAQ-100
                case "MNQ": return 0.50;   // Micro E-mini NASDAQ-100
                case "CL": return 10.00;   // Crude Oil
                case "GC": return 10.00;   // Gold
                case "6E": return 12.50;   // Euro FX
                case "6A": return 10.00;   // Australian Dollar
                default: return 12.50;     // Default to ES value
            }
        }

        public SizingMetadata GetMetadata()
        {
            var requiredParams = new List<string>();
            requiredParams.Add("method");
            requiredParams.Add("risk_per_trade");
            requiredParams.Add("min_contracts");
            requiredParams.Add("max_contracts");

            return new SizingMetadata(
                name: "Advanced Position Sizer",
                description: "Optimal f, Kelly Criterion, and volatility-adjusted sizing with contract clamping",
                requiredParameters: requiredParams
            );
        }

        /// <summary>
        /// Get current performance metrics snapshot
        /// </summary>
        public SizingMetricsSnapshot GetMetricsSnapshot()
        {
            return _metrics.GetSnapshot();
        }

        /// <summary>
        /// Validate sizing configuration parameters
        /// </summary>
        public static bool ValidateConfig(SizingConfig config, out List<string> errors)
        {
            errors = new List<string>();

            if (config.MinContracts < 0)
                errors.Add("MinContracts must be >= 0");

            if (config.MaxContracts <= 0)
                errors.Add("MaxContracts must be > 0");

            if (config.MinContracts > config.MaxContracts)
                errors.Add("MinContracts must be <= MaxContracts");

            if (config.RiskPerTrade <= 0)
                errors.Add("RiskPerTrade must be > 0");

            // Method-specific validation
            switch (config.Method)
            {
                case SizingMethod.OptimalF:
                    // No additional parameters required for Optimal f
                    break;

                case SizingMethod.KellyCriterion:
                    // Validate Kelly fraction parameter if provided
                    if (config.MethodParameters.ContainsKey("kelly_fraction"))
                    {
                        var kellyFraction = GetParameterValue<double>(config, "kelly_fraction", 0.5);
                        if (kellyFraction <= 0 || kellyFraction > 1.0)
                            errors.Add("Kelly fraction must be between 0 and 1.0");
                    }
                    break;

                case SizingMethod.VolatilityAdjusted:
                    // No additional parameters required for volatility-adjusted sizing
                    break;

                default:
                    errors.Add(String.Format("Unsupported sizing method: {0}", config.Method));
                    break;
            }

            return errors.Count == 0;
        }

        /// <summary>
        /// Calculates size using base advanced sizing plus confluence grade and risk mode adjustments.
        /// </summary>
        /// <param name="intent">Strategy intent.</param>
        /// <param name="context">Strategy context.</param>
        /// <param name="config">Base sizing configuration.</param>
        /// <param name="confluenceScore">Confluence score and trade grade.</param>
        /// <param name="riskMode">Current risk mode.</param>
        /// <param name="modeConfig">Risk mode configuration.</param>
        /// <returns>Enhanced sizing result with grade and mode metadata.</returns>
        public SizingResult CalculateSizeWithGrade(
            StrategyIntent intent,
            StrategyContext context,
            SizingConfig config,
            ConfluenceScore confluenceScore,
            RiskMode riskMode,
            RiskModeConfig modeConfig)
        {
            if (intent == null) throw new ArgumentNullException("intent");
            if (context == null) throw new ArgumentNullException("context");
            if (config == null) throw new ArgumentNullException("config");
            if (confluenceScore == null) throw new ArgumentNullException("confluenceScore");
            if (modeConfig == null) throw new ArgumentNullException("modeConfig");

            try
            {
                var baseResult = CalculateSize(intent, context, config);

                var gradeFilter = new GradeFilter();
                var gradeMultiplier = gradeFilter.GetSizeMultiplier(confluenceScore.Grade, riskMode);
                var modeMultiplier = modeConfig.SizeMultiplier;
                var combinedMultiplier = gradeMultiplier * modeMultiplier;

                var adjustedContractsRaw = baseResult.Contracts * combinedMultiplier;
                var adjustedContracts = (int)Math.Floor(adjustedContractsRaw);

                if (adjustedContracts < config.MinContracts)
                    adjustedContracts = config.MinContracts;
                if (adjustedContracts > config.MaxContracts)
                    adjustedContracts = config.MaxContracts;

                if (!gradeFilter.ShouldAcceptTrade(confluenceScore.Grade, riskMode))
                    adjustedContracts = 0;

                var riskPerContract = baseResult.Contracts > 0
                    ? baseResult.RiskAmount / baseResult.Contracts
                    : 0.0;
                var finalRisk = adjustedContracts * riskPerContract;

                var calculations = new Dictionary<string, object>(baseResult.Calculations);
                calculations.Add("grade", confluenceScore.Grade.ToString());
                calculations.Add("risk_mode", riskMode.ToString());
                calculations.Add("grade_multiplier", gradeMultiplier);
                calculations.Add("mode_multiplier", modeMultiplier);
                calculations.Add("combined_multiplier", combinedMultiplier);
                calculations.Add("base_contracts", baseResult.Contracts);
                calculations.Add("adjusted_contracts_raw", adjustedContractsRaw);
                calculations.Add("adjusted_contracts", adjustedContracts);
                calculations.Add("final_risk", finalRisk);

                if (adjustedContracts == 0)
                {
                    calculations.Add("rejection_reason", gradeFilter.GetRejectionReason(confluenceScore.Grade, riskMode));
                }

                return new SizingResult(adjustedContracts, finalRisk, baseResult.Method, calculations);
            }
            catch (Exception ex)
            {
                _logger.LogError("CalculateSizeWithGrade failed: {0}", ex.Message);
                throw;
            }
        }

        /// <summary>
        /// Internal class to represent trade results for calculations
        /// </summary>
        private class TradeResult
        {
            public double ProfitLoss { get; set; }
            public DateTime TradeTime { get; set; }

            public TradeResult()
            {
                ProfitLoss = 0.0;
                TradeTime = DateTime.UtcNow;
            }

            public TradeResult(double profitLoss, DateTime tradeTime)
            {
                ProfitLoss = profitLoss;
                TradeTime = tradeTime;
            }
        }
    }

    /// <summary>
    /// Performance metrics for sizing operations
    /// </summary>
    public class SizingMetrics
    {
        // Operation counters
        public long TotalOperations { get; private set; }
        public long OptimalFOperations { get; private set; }
        public long KellyCriterionOperations { get; private set; }
        public long VolatilityAdjustedOperations { get; private set; }
        public long FallbackOperations { get; private set; }

        // Timing metrics
        public long TotalProcessingTimeMs { get; private set; }
        public long MaxProcessingTimeMs { get; private set; }
        public long MinProcessingTimeMs { get; private set; }

        // Thread-safe counters
        private readonly object _lock = new object();

        public SizingMetrics()
        {
            MinProcessingTimeMs = long.MaxValue;
        }

        public void RecordOperation(SizingMethod method, long processingTimeMs)
        {
            lock (_lock)
            {
                TotalOperations++;
                TotalProcessingTimeMs += processingTimeMs;

                // Update min/max timing
                if (processingTimeMs > MaxProcessingTimeMs)
                    MaxProcessingTimeMs = processingTimeMs;
                if (processingTimeMs < MinProcessingTimeMs)
                    MinProcessingTimeMs = processingTimeMs;

                // Update method-specific counters
                switch (method)
                {
                    case SizingMethod.OptimalF:
                        OptimalFOperations++;
                        break;
                    case SizingMethod.KellyCriterion:
                        KellyCriterionOperations++;
                        break;
                    case SizingMethod.VolatilityAdjusted:
                        VolatilityAdjustedOperations++;
                        break;
                    case SizingMethod.FixedDollarRisk:
                        FallbackOperations++;
                        break;
                }
            }
        }

        public SizingMetricsSnapshot GetSnapshot()
        {
            lock (_lock)
            {
                return new SizingMetricsSnapshot
                {
                    TotalOperations = TotalOperations,
                    OptimalFOperations = OptimalFOperations,
                    KellyCriterionOperations = KellyCriterionOperations,
                    VolatilityAdjustedOperations = VolatilityAdjustedOperations,
                    FallbackOperations = FallbackOperations,
                    TotalProcessingTimeMs = TotalProcessingTimeMs,
                    MaxProcessingTimeMs = MaxProcessingTimeMs,
                    MinProcessingTimeMs = MinProcessingTimeMs,
                    AverageProcessingTimeMs = TotalOperations > 0 ? (double)TotalProcessingTimeMs / TotalOperations : 0
                };
            }
        }
    }

    /// <summary>
    /// Snapshot of sizing metrics
    /// </summary>
    public class SizingMetricsSnapshot
    {
        public long TotalOperations { get; set; }
        public long OptimalFOperations { get; set; }
        public long KellyCriterionOperations { get; set; }
        public long VolatilityAdjustedOperations { get; set; }
        public long FallbackOperations { get; set; }
        public long TotalProcessingTimeMs { get; set; }
        public long MaxProcessingTimeMs { get; set; }
        public long MinProcessingTimeMs { get; set; }
        public double AverageProcessingTimeMs { get; set; }
    }
}
