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

namespace NT8.Core.Sizing
{
    /// <summary>
    /// Basic position sizer with fixed contracts and fixed dollar risk methods
    /// Handles contract size calculations with proper rounding and clamping
    /// </summary>
    public class BasicPositionSizer : IPositionSizer
    {
        private readonly ILogger _logger;

        public BasicPositionSizer(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");

            // Validate intent is suitable for sizing
            if (!intent.IsValid())
            {
                _logger.LogWarning("Invalid strategy intent provided for sizing: {0}", intent);
                
                var errorCalcs = new Dictionary<string, object>();
                errorCalcs.Add("error", "Invalid intent");
                
                return new SizingResult(0, 0, config.Method, errorCalcs);
            }

            switch (config.Method)
            {
                case SizingMethod.FixedContracts:
                    return CalculateFixedContracts(intent, context, config);
                case SizingMethod.FixedDollarRisk:
                    return CalculateFixedRisk(intent, context, config);
                default:
                    throw new NotSupportedException(String.Format("Sizing method {0} not supported in Phase 0", config.Method));
            }
        }

        private SizingResult CalculateFixedContracts(StrategyIntent intent, StrategyContext context, SizingConfig config)
        {
            // Get target contracts from configuration
            var targetContracts = GetParameterValue<int>(config, "contracts", 1);

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

            // Calculate actual risk amount
            var tickValue = GetTickValue(intent.Symbol);
            var riskAmount = contracts * intent.StopTicks * tickValue;

            _logger.LogDebug("Fixed contracts sizing: {0} {1}→{2} contracts, ${3:F2} risk",
                intent.Symbol, targetContracts, contracts, riskAmount);

            var calculations = new Dictionary<string, object>();
            calculations.Add("target_contracts", targetContracts);
            calculations.Add("clamped_contracts", contracts);
            calculations.Add("stop_ticks", intent.StopTicks);
            calculations.Add("tick_value", tickValue);
            calculations.Add("risk_amount", riskAmount);
            calculations.Add("min_contracts", config.MinContracts);
            calculations.Add("max_contracts", config.MaxContracts);

            return new SizingResult(
                contracts: contracts,
                riskAmount: riskAmount,
                method: SizingMethod.FixedContracts,
                calculations: calculations
            );
        }

        private SizingResult CalculateFixedRisk(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);

                var errorCalcs = new Dictionary<string, object>();
                errorCalcs.Add("error", "Invalid stop ticks");
                errorCalcs.Add("stop_ticks", intent.StopTicks);

                return new SizingResult(0, 0, SizingMethod.FixedDollarRisk, errorCalcs);
            }

            // 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 sizing: {0} ${1:F2}→{2:F2}→{3} contracts, ${4:F2} actual risk",
                intent.Symbol, targetRisk, optimalContracts, contracts, actualRisk);

            var calculations = new Dictionary<string, object>();
            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);

            return new SizingResult(
                contracts: contracts,
                riskAmount: actualRisk,
                method: SizingMethod.FixedDollarRisk,
                calculations: calculations
            );
        }

        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: "Basic Position Sizer",
                description: "Fixed contracts or fixed dollar risk sizing with contract clamping",
                requiredParameters: requiredParams
            );
        }

        /// <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.FixedContracts:
                    if (!config.MethodParameters.ContainsKey("contracts"))
                        errors.Add("FixedContracts method requires 'contracts' parameter");
                    else if (GetParameterValue<int>(config, "contracts", 0) <= 0)
                        errors.Add("Fixed contracts parameter must be > 0");
                    break;

                case SizingMethod.FixedDollarRisk:
                    // No additional parameters required for fixed dollar risk
                    break;

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

            return errors.Count == 0;
        }

        /// <summary>
        /// Get supported symbols with their tick values
        /// </summary>
        public static Dictionary<string, double> GetSupportedSymbols()
        {
            var symbols = new Dictionary<string, double>();
            symbols.Add("ES", 12.50);
            symbols.Add("MES", 1.25);
            symbols.Add("NQ", 5.00);
            symbols.Add("MNQ", 0.50);
            symbols.Add("CL", 10.00);
            symbols.Add("GC", 10.00);
            symbols.Add("6E", 12.50);
            symbols.Add("6A", 10.00);
            
            return symbols;
        }
    }
}