// File: VolatilityAdjustedSizer.cs
using System;
using NT8.Core.Logging;

namespace NT8.Core.Sizing
{
    /// <summary>
    /// Implements a position sizer that adjusts contract quantity based on market volatility.
    /// Uses Average True Range (ATR) or Standard Deviation to scale positions inversely to volatility.
    /// </summary>
    public class VolatilityAdjustedSizer
    {
        private readonly ILogger _logger;
        private readonly object _lock;

        /// <summary>
        /// Minimum volatility factor to prevent extreme position sizes
        /// </summary>
        public const double MIN_VOLATILITY_FACTOR = 0.1;

        /// <summary>
        /// Maximum volatility factor to prevent extreme position sizes
        /// </summary>
        public const double MAX_VOLATILITY_FACTOR = 10.0;

        /// <summary>
        /// Constructor
        /// </summary>
        /// <param name="logger">Logger instance</param>
        /// <exception cref="ArgumentNullException">Logger is null</exception>
        public VolatilityAdjustedSizer(ILogger logger)
        {
            if (logger == null)
                throw new ArgumentNullException("logger");

            _logger = logger;
            _lock = new object();

            _logger.LogDebug("VolatilityAdjustedSizer initialized");
        }

        /// <summary>
        /// Calculates contract quantity adjusted by volatility.
        /// Scales position size inversely to volatility - higher volatility = smaller position.
        /// </summary>
        /// <param name="baseContracts">Base number of contracts before adjustment</param>
        /// <param name="volatilityMetrics">Current volatility metrics</param>
        /// <param name="targetVolatility">Target or normal volatility level to normalize against</param>
        /// <param name="method">Volatility method to use (ATR or StdDev)</param>
        /// <param name="constraints">Contract constraints</param>
        /// <returns>Adjusted contract quantity</returns>
        /// <exception cref="ArgumentNullException">Required parameters are null</exception>
        /// <exception cref="ArgumentException">Invalid input parameters</exception>
        public int CalculateAdjustedSize(
            int baseContracts,
            VolatilityMetrics volatilityMetrics,
            double targetVolatility,
            VolatilityRegime method,
            ContractConstraints constraints)
        {
            if (volatilityMetrics == null)
                throw new ArgumentNullException("volatilityMetrics");
            if (constraints == null)
                throw new ArgumentNullException("constraints");

            if (baseContracts <= 0)
                throw new ArgumentException("Base contracts must be positive");
            if (targetVolatility <= 0)
                throw new ArgumentException("Target volatility must be positive");

            try
            {
                // Get current volatility based on method
                double currentVolatility = GetVolatility(volatilityMetrics, method);

                _logger.LogDebug(
                    "Calculating volatility adjusted size: base={0}, current={1:F4}, target={2:F4}, method={3}",
                    baseContracts, currentVolatility, targetVolatility, method
                );

                // Calculate volatility factor (inverse relationship)
                // Factor > 1 means lower position size, Factor < 1 means higher position size
                double volatilityFactor = currentVolatility / targetVolatility;

                // Clamp factor to reasonable bounds
                volatilityFactor = Math.Max(MIN_VOLATILITY_FACTOR, Math.Min(MAX_VOLATILITY_FACTOR, volatilityFactor));

                // Adjust contracts inversely to volatility
                double rawContracts = baseContracts / volatilityFactor;

                // Apply constraints
                int finalContracts = constraints.ApplyConstraints(rawContracts);

                _logger.LogInformation(
                    "Volatility adjusted size: Base {0} -> Raw {1:F2} (factor {2:F2}) -> Final {3}",
                    baseContracts, rawContracts, volatilityFactor, finalContracts
                );

                return finalContracts;
            }
            catch (ArgumentException)
            {
                throw;
            }
            catch (Exception ex)
            {
                _logger.LogError("Volatility adjusted sizing failed: {0}", ex.Message);
                throw;
            }
        }

        /// <summary>
        /// Calculates regime-based position size scaling.
        /// Reduces size in high volatility regimes, increases in low volatility.
        /// </summary>
        /// <param name="baseContracts">Base number of contracts</param>
        /// <param name="regime">Current volatility regime</param>
        /// <param name="constraints">Contract constraints</param>
        /// <returns>Adjusted contract quantity</returns>
        /// <exception cref="ArgumentNullException">Constraints is null</exception>
        /// <exception cref="ArgumentException">Invalid base contracts</exception>
        public int CalculateRegimeBasedSize(
            int baseContracts,
            VolatilityRegime regime,
            ContractConstraints constraints)
        {
            if (constraints == null)
                throw new ArgumentNullException("constraints");
            if (baseContracts <= 0)
                throw new ArgumentException("Base contracts must be positive");

            try
            {
                // Regime-based scaling factors
                double scaleFactor;
                switch (regime)
                {
                    case VolatilityRegime.VeryLow:
                        scaleFactor = 1.4; // Increase position by 40%
                        break;
                    case VolatilityRegime.Low:
                        scaleFactor = 1.2; // Increase position by 20%
                        break;
                    case VolatilityRegime.Normal:
                        scaleFactor = 1.0; // No adjustment
                        break;
                    case VolatilityRegime.High:
                        scaleFactor = 0.75; // Reduce position by 25%
                        break;
                    case VolatilityRegime.VeryHigh:
                        scaleFactor = 0.5; // Reduce position by 50%
                        break;
                    case VolatilityRegime.Extreme:
                        scaleFactor = 0.25; // Reduce position by 75%
                        break;
                    default:
                        scaleFactor = 1.0; // Default to no adjustment
                        _logger.LogWarning("Unknown volatility regime {0}, using no adjustment", regime);
                        break;
                }

                double rawContracts = baseContracts * scaleFactor;
                int finalContracts = constraints.ApplyConstraints(rawContracts);

                _logger.LogInformation(
                    "Regime-based size: Base {0} -> Raw {1:F2} (regime {2}, factor {3:F2}) -> Final {4}",
                    baseContracts, rawContracts, regime, scaleFactor, finalContracts
                );

                return finalContracts;
            }
            catch (ArgumentException)
            {
                throw;
            }
            catch (Exception ex)
            {
                _logger.LogError("Regime-based sizing failed: {0}", ex.Message);
                throw;
            }
        }

        /// <summary>
        /// Gets the appropriate volatility value based on method
        /// </summary>
        private double GetVolatility(VolatilityMetrics metrics, VolatilityRegime method)
        {
            // For now, use ATR as default volatility measure
            // In future, could expand to use different metrics based on method parameter
            if (metrics.ATR > 0)
                return metrics.ATR;
            if (metrics.StandardDeviation > 0)
                return metrics.StandardDeviation;
            if (metrics.HistoricalVolatility > 0)
                return metrics.HistoricalVolatility;

            _logger.LogWarning("No valid volatility metric found, using default value 1.0");
            return 1.0;
        }
    }
}
