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

namespace NT8.Core.Risk
{
    /// <summary>
    /// Basic risk manager implementing Tier 1 risk controls
    /// Thread-safe implementation using locks for state consistency
    /// </summary>
    public class BasicRiskManager : IRiskManager
    {
        private readonly ILogger _logger;
        private readonly object _lock = new object();

        // Risk state - protected by _lock
        private double _dailyPnL;
        private double _maxDrawdown;
        private bool _tradingHalted;
        private DateTime _lastUpdate = DateTime.UtcNow;
        private readonly Dictionary<string, double> _symbolExposure = new Dictionary<string, double>();

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

        public RiskDecision ValidateOrder(StrategyIntent intent, StrategyContext context, RiskConfig config)
        {
            if (intent == null) throw new ArgumentNullException("intent");
            if (context == null) throw new ArgumentNullException("context");
            if (config == null) throw new ArgumentNullException("config");

            lock (_lock)
            {
                // Check if trading is halted
                if (_tradingHalted)
                {
                    _logger.LogWarning("Order rejected - trading halted by risk manager");

                    var haltedMetrics = new Dictionary<string, object>();
                    haltedMetrics.Add("halted", true);
                    haltedMetrics.Add("daily_pnl", _dailyPnL);

                    return new RiskDecision(
                        allow: false,
                        rejectReason: "Trading halted by risk manager",
                        modifiedIntent: null,
                        riskLevel: RiskLevel.Critical,
                        riskMetrics: haltedMetrics
                    );
                }

                // Tier 1: Daily loss cap
                if (_dailyPnL <= -config.DailyLossLimit)
                {
                    _tradingHalted = true;
                    _logger.LogCritical("Daily loss limit breached: {0:C} <= {1:C}", _dailyPnL, -config.DailyLossLimit);

                    var limitMetrics = new Dictionary<string, object>();
                    limitMetrics.Add("daily_pnl", _dailyPnL);
                    limitMetrics.Add("limit", config.DailyLossLimit);

                    return new RiskDecision(
                        allow: false,
                        rejectReason: String.Format("Daily loss limit breached: {0:C}", _dailyPnL),
                        modifiedIntent: null,
                        riskLevel: RiskLevel.Critical,
                        riskMetrics: limitMetrics
                    );
                }

                // Tier 1: Per-trade risk limit
                var tradeRisk = CalculateTradeRisk(intent, context);
                if (tradeRisk > config.MaxTradeRisk)
                {
                    _logger.LogWarning("Trade risk too high: {0:C} > {1:C}", tradeRisk, config.MaxTradeRisk);

                    var riskMetrics = new Dictionary<string, object>();
                    riskMetrics.Add("trade_risk", tradeRisk);
                    riskMetrics.Add("limit", config.MaxTradeRisk);

                    return new RiskDecision(
                        allow: false,
                        rejectReason: String.Format("Trade risk too high: {0:C}", tradeRisk),
                        modifiedIntent: null,
                        riskLevel: RiskLevel.High,
                        riskMetrics: riskMetrics
                    );
                }

                // Tier 1: Position limits
                var currentPositions = GetOpenPositionCount();
                if (currentPositions >= config.MaxOpenPositions && context.CurrentPosition.Quantity == 0)
                {
                    _logger.LogWarning("Max open positions exceeded: {0} >= {1}", currentPositions, config.MaxOpenPositions);

                    var positionMetrics = new Dictionary<string, object>();
                    positionMetrics.Add("open_positions", currentPositions);
                    positionMetrics.Add("limit", config.MaxOpenPositions);

                    return new RiskDecision(
                        allow: false,
                        rejectReason: String.Format("Max open positions exceeded: {0}", currentPositions),
                        modifiedIntent: null,
                        riskLevel: RiskLevel.Medium,
                        riskMetrics: positionMetrics
                    );
                }

                // All checks passed - determine risk level
                var riskLevel = DetermineRiskLevel(config);

                _logger.LogDebug("Order approved: {0} {1} risk=${2:F2} level={3}", intent.Symbol, intent.Side, tradeRisk, riskLevel);

                var successMetrics = new Dictionary<string, object>();
                successMetrics.Add("trade_risk", tradeRisk);
                successMetrics.Add("daily_pnl", _dailyPnL);
                successMetrics.Add("max_drawdown", _maxDrawdown);
                successMetrics.Add("open_positions", currentPositions);

                return new RiskDecision(
                    allow: true,
                    rejectReason: null,
                    modifiedIntent: null,
                    riskLevel: riskLevel,
                    riskMetrics: successMetrics
                );
            }
        }

        private static double CalculateTradeRisk(StrategyIntent intent, StrategyContext context)
        {
            // Get tick value for symbol - this will be enhanced in later phases
            var tickValue = GetTickValue(intent.Symbol);
            return intent.StopTicks * tickValue;
        }

        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;
                case "MES": return 1.25;
                case "NQ": return 5.00;
                case "MNQ": return 0.50;
                case "CL": return 10.00;
                case "GC": return 10.00;
                default: return 12.50; // Default to ES
            }
        }

        private int GetOpenPositionCount()
        {
            // For Phase 0, return simplified count
            // Will be enhanced with actual position tracking in Phase 1
            return _symbolExposure.Count(kvp => Math.Abs(kvp.Value) > 0.01);
        }

        private RiskLevel DetermineRiskLevel(RiskConfig config)
        {
            var lossPercent = Math.Abs(_dailyPnL) / config.DailyLossLimit;

            if (lossPercent >= 0.8) return RiskLevel.High;
            if (lossPercent >= 0.5) return RiskLevel.Medium;
            return RiskLevel.Low;
        }

        public void OnFill(OrderFill fill)
        {
            if (fill == null) throw new ArgumentNullException("fill");

            lock (_lock)
            {
                _lastUpdate = DateTime.UtcNow;

                // Update symbol exposure
                var fillValue = fill.Quantity * fill.FillPrice;
                if (_symbolExposure.ContainsKey(fill.Symbol))
                {
                    _symbolExposure[fill.Symbol] += fillValue;
                }
                else
                {
                    _symbolExposure[fill.Symbol] = fillValue;
                }

                _logger.LogDebug("Fill processed: {0} {1} @ {2:F2}, Exposure: {3:C}",
                    fill.Symbol, fill.Quantity, fill.FillPrice, _symbolExposure[fill.Symbol]);
            }
        }

        public void OnPnLUpdate(double netPnL, double dayPnL)
        {
            lock (_lock)
            {
                var oldDailyPnL = _dailyPnL;
                _dailyPnL = dayPnL;
                _maxDrawdown = Math.Min(_maxDrawdown, dayPnL);
                _lastUpdate = DateTime.UtcNow;

                if (Math.Abs(dayPnL - oldDailyPnL) > 0.01)
                {
                    _logger.LogDebug("P&L Update: Daily={0:C}, Max DD={1:C}", dayPnL, _maxDrawdown);
                }

                // Check for emergency conditions
                CheckEmergencyConditions(dayPnL);
            }
        }

        private void CheckEmergencyConditions(double dayPnL)
        {
            // Emergency halt if daily loss exceeds 90% of limit
            // Using a default limit of 1000 as this method doesn't have access to config
            // In Phase 1, this should be improved to use the actual config value
            if (dayPnL <= -(1000 * 0.9) && !_tradingHalted)
            {
                _tradingHalted = true;
                _logger.LogCritical("Emergency halt triggered at 90% of daily loss limit: {0:C}", dayPnL);
            }
        }

        public async Task<bool> EmergencyFlatten(string reason)
        {
            if (String.IsNullOrEmpty(reason)) throw new ArgumentException("Reason required", "reason");

            lock (_lock)
            {
                _tradingHalted = true;
                _logger.LogCritical("Emergency flatten triggered: {0}", reason);
            }

            // In Phase 0, this is a placeholder
            // Phase 1 will implement actual position flattening via OMS
            await Task.Delay(100);

            _logger.LogInformation("Emergency flatten completed");
            return true;
        }

        public RiskStatus GetRiskStatus()
        {
            lock (_lock)
            {
                var alerts = new List<string>();

                if (_tradingHalted)
                    alerts.Add("Trading halted");

                if (_dailyPnL <= -500) // Half of typical daily limit
                    alerts.Add(String.Format("Significant daily loss: {0:C}", _dailyPnL));

                if (_maxDrawdown <= -1000)
                    alerts.Add(String.Format("Large drawdown: {0:C}", _maxDrawdown));

                return new RiskStatus(
                    tradingEnabled: !_tradingHalted,
                    dailyPnL: _dailyPnL,
                    dailyLossLimit: 1000, // Will come from config in Phase 1
                    maxDrawdown: _maxDrawdown,
                    openPositions: GetOpenPositionCount(),
                    lastUpdate: _lastUpdate,
                    activeAlerts: alerts
                );
            }
        }

        /// <summary>
        /// Reset daily state - typically called at start of new trading day
        /// </summary>
        public void ResetDaily()
        {
            lock (_lock)
            {
                _dailyPnL = 0;
                _maxDrawdown = 0;
                _tradingHalted = false;
                _symbolExposure.Clear();
                _lastUpdate = DateTime.UtcNow;

                _logger.LogInformation("Daily risk state reset");
            }
        }
    }
}
