using System;
using System.Collections.Generic;
using NT8.Core.Logging;

namespace NT8.Core.Intelligence
{
    /// <summary>
    /// Manages current risk mode with automatic transitions and optional manual override.
    /// Thread-safe for all shared state operations.
    /// </summary>
    public class RiskModeManager
    {
        private readonly ILogger _logger;
        private readonly object _lock = new object();
        private readonly Dictionary<RiskMode, RiskModeConfig> _configs;
        private RiskModeState _state;

        /// <summary>
        /// Creates a new risk mode manager with default mode configurations.
        /// </summary>
        /// <param name="logger">Logger instance.</param>
        public RiskModeManager(ILogger logger)
        {
            if (logger == null)
                throw new ArgumentNullException("logger");

            _logger = logger;
            _configs = new Dictionary<RiskMode, RiskModeConfig>();

            InitializeDefaultConfigs();

            _state = new RiskModeState(
                RiskMode.PCP,
                RiskMode.PCP,
                DateTime.UtcNow,
                "Initialization",
                false,
                TimeSpan.Zero,
                new Dictionary<string, object>());
        }

        /// <summary>
        /// Updates risk mode from current performance data.
        /// </summary>
        /// <param name="dailyPnL">Current daily PnL.</param>
        /// <param name="winStreak">Current win streak.</param>
        /// <param name="lossStreak">Current loss streak.</param>
        public void UpdateRiskMode(double dailyPnL, int winStreak, int lossStreak)
        {
            if (winStreak < 0)
                throw new ArgumentException("winStreak must be non-negative", "winStreak");
            if (lossStreak < 0)
                throw new ArgumentException("lossStreak must be non-negative", "lossStreak");

            try
            {
                var metrics = new PerformanceMetrics(
                    dailyPnL,
                    winStreak,
                    lossStreak,
                    CalculateSyntheticWinRate(winStreak, lossStreak),
                    0.7,
                    VolatilityRegime.Normal);

                lock (_lock)
                {
                    if (_state.IsManualOverride)
                    {
                        UpdateModeDuration();
                        return;
                    }

                    var current = _state.CurrentMode;
                    var next = DetermineTargetMode(current, metrics);

                    if (next != current)
                    {
                        TransitionMode(next, "Automatic transition by performance metrics");
                    }
                    else
                    {
                        UpdateModeDuration();
                    }
                }
            }
            catch (Exception ex)
            {
                _logger.LogError("UpdateRiskMode failed: {0}", ex.Message);
                throw;
            }
        }

        /// <summary>
        /// Gets current active risk mode.
        /// </summary>
        /// <returns>Current risk mode.</returns>
        public RiskMode GetCurrentMode()
        {
            lock (_lock)
            {
                return _state.CurrentMode;
            }
        }

        /// <summary>
        /// Gets config for the specified mode.
        /// </summary>
        /// <param name="mode">Risk mode.</param>
        /// <returns>Mode configuration.</returns>
        public RiskModeConfig GetModeConfig(RiskMode mode)
        {
            lock (_lock)
            {
                if (!_configs.ContainsKey(mode))
                    throw new ArgumentException("Mode configuration not found", "mode");

                return _configs[mode];
            }
        }

        /// <summary>
        /// Evaluates whether mode should transition based on provided metrics.
        /// </summary>
        /// <param name="current">Current mode.</param>
        /// <param name="metrics">Performance metrics snapshot.</param>
        /// <returns>True when transition should occur.</returns>
        public bool ShouldTransitionMode(RiskMode current, PerformanceMetrics metrics)
        {
            if (metrics == null)
                throw new ArgumentNullException("metrics");

            var target = DetermineTargetMode(current, metrics);
            return target != current;
        }

        /// <summary>
        /// Forces a manual mode override.
        /// </summary>
        /// <param name="mode">Target mode.</param>
        /// <param name="reason">Override reason.</param>
        public void OverrideMode(RiskMode mode, string reason)
        {
            if (string.IsNullOrEmpty(reason))
                throw new ArgumentNullException("reason");

            try
            {
                lock (_lock)
                {
                    var previous = _state.CurrentMode;
                    _state = new RiskModeState(
                        mode,
                        previous,
                        DateTime.UtcNow,
                        reason,
                        true,
                        TimeSpan.Zero,
                        _state.Metadata);
                }

                _logger.LogWarning("Risk mode manually overridden to {0}. Reason: {1}", mode, reason);
            }
            catch (Exception ex)
            {
                _logger.LogError("OverrideMode failed: {0}", ex.Message);
                throw;
            }
        }

        /// <summary>
        /// Clears manual override and resets mode to default PCP.
        /// </summary>
        public void ResetToDefault()
        {
            try
            {
                lock (_lock)
                {
                    var previous = _state.CurrentMode;
                    _state = new RiskModeState(
                        RiskMode.PCP,
                        previous,
                        DateTime.UtcNow,
                        "Reset to default",
                        false,
                        TimeSpan.Zero,
                        _state.Metadata);
                }

                _logger.LogInformation("Risk mode reset to default PCP");
            }
            catch (Exception ex)
            {
                _logger.LogError("ResetToDefault failed: {0}", ex.Message);
                throw;
            }
        }

        /// <summary>
        /// Returns current risk mode state snapshot.
        /// </summary>
        /// <returns>Risk mode state.</returns>
        public RiskModeState GetState()
        {
            lock (_lock)
            {
                return _state;
            }
        }

        private void InitializeDefaultConfigs()
        {
            _configs.Add(
                RiskMode.ECP,
                new RiskModeConfig(
                    RiskMode.ECP,
                    1.5,
                    TradeGrade.B,
                    1500.0,
                    4,
                    true,
                    new Dictionary<string, object>()));

            _configs.Add(
                RiskMode.PCP,
                new RiskModeConfig(
                    RiskMode.PCP,
                    1.0,
                    TradeGrade.B,
                    1000.0,
                    3,
                    false,
                    new Dictionary<string, object>()));

            _configs.Add(
                RiskMode.DCP,
                new RiskModeConfig(
                    RiskMode.DCP,
                    0.5,
                    TradeGrade.A,
                    600.0,
                    2,
                    false,
                    new Dictionary<string, object>()));

            _configs.Add(
                RiskMode.HR,
                new RiskModeConfig(
                    RiskMode.HR,
                    0.0,
                    TradeGrade.APlus,
                    0.0,
                    0,
                    false,
                    new Dictionary<string, object>()));
        }

        private RiskMode DetermineTargetMode(RiskMode current, PerformanceMetrics metrics)
        {
            if (metrics.LossStreak >= 3)
                return RiskMode.HR;

            if (metrics.VolatilityRegime == VolatilityRegime.Extreme)
                return RiskMode.HR;

            if (metrics.DailyPnL >= 500.0 && metrics.RecentWinRate >= 0.80 && metrics.LossStreak == 0)
                return RiskMode.ECP;

            if (metrics.DailyPnL <= -200.0 || metrics.RecentWinRate < 0.50)
                return RiskMode.DCP;

            if (current == RiskMode.DCP && metrics.WinStreak >= 2 && metrics.RecentExecutionQuality >= 0.70)
                return RiskMode.PCP;

            if (current == RiskMode.HR)
            {
                if (metrics.DailyPnL >= -100.0 && metrics.LossStreak <= 1)
                    return RiskMode.DCP;

                return RiskMode.HR;
            }

            return RiskMode.PCP;
        }

        private void TransitionMode(RiskMode nextMode, string reason)
        {
            var previous = _state.CurrentMode;
            _state = new RiskModeState(
                nextMode,
                previous,
                DateTime.UtcNow,
                reason,
                false,
                TimeSpan.Zero,
                _state.Metadata);

            _logger.LogInformation("Risk mode transition: {0} -> {1}. Reason: {2}", previous, nextMode, reason);
        }

        private void UpdateModeDuration()
        {
            _state.ModeDuration = DateTime.UtcNow - _state.LastTransitionAtUtc;
        }

        private static double CalculateSyntheticWinRate(int winStreak, int lossStreak)
        {
            var denominator = winStreak + lossStreak;
            if (denominator <= 0)
                return 0.5;

            var ratio = (double)winStreak / denominator;
            if (ratio < 0.0)
                return 0.0;
            if (ratio > 1.0)
                return 1.0;
            return ratio;
        }
    }
}
