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

namespace NT8.Core.Intelligence
{
    /// <summary>
    /// Coordinates volatility and trend regime detection and stores per-symbol regime state.
    /// Thread-safe access to shared regime state and transition history.
    /// </summary>
    public class RegimeManager
    {
        private readonly ILogger _logger;
        private readonly VolatilityRegimeDetector _volatilityDetector;
        private readonly TrendRegimeDetector _trendDetector;
        private readonly object _lock = new object();

        private readonly Dictionary<string, RegimeState> _currentStates;
        private readonly Dictionary<string, List<BarData>> _barHistory;
        private readonly Dictionary<string, List<RegimeTransition>> _transitions;
        private readonly int _maxBarsPerSymbol;
        private readonly int _maxTransitionsPerSymbol;

        /// <summary>
        /// Creates a new regime manager.
        /// </summary>
        /// <param name="logger">Logger instance.</param>
        /// <param name="volatilityDetector">Volatility regime detector.</param>
        /// <param name="trendDetector">Trend regime detector.</param>
        /// <param name="maxBarsPerSymbol">Maximum bars to keep per symbol.</param>
        /// <param name="maxTransitionsPerSymbol">Maximum transitions to keep per symbol.</param>
        /// <exception cref="ArgumentNullException">Any required dependency is null.</exception>
        /// <exception cref="ArgumentException">Any max size is not positive.</exception>
        public RegimeManager(
            ILogger logger,
            VolatilityRegimeDetector volatilityDetector,
            TrendRegimeDetector trendDetector,
            int maxBarsPerSymbol,
            int maxTransitionsPerSymbol)
        {
            if (logger == null)
                throw new ArgumentNullException("logger");
            if (volatilityDetector == null)
                throw new ArgumentNullException("volatilityDetector");
            if (trendDetector == null)
                throw new ArgumentNullException("trendDetector");
            if (maxBarsPerSymbol <= 0)
                throw new ArgumentException("maxBarsPerSymbol must be greater than zero", "maxBarsPerSymbol");
            if (maxTransitionsPerSymbol <= 0)
                throw new ArgumentException("maxTransitionsPerSymbol must be greater than zero", "maxTransitionsPerSymbol");

            _logger = logger;
            _volatilityDetector = volatilityDetector;
            _trendDetector = trendDetector;
            _maxBarsPerSymbol = maxBarsPerSymbol;
            _maxTransitionsPerSymbol = maxTransitionsPerSymbol;

            _currentStates = new Dictionary<string, RegimeState>();
            _barHistory = new Dictionary<string, List<BarData>>();
            _transitions = new Dictionary<string, List<RegimeTransition>>();
        }

        /// <summary>
        /// Updates regime state for a symbol using latest market information.
        /// </summary>
        /// <param name="symbol">Instrument symbol.</param>
        /// <param name="bar">Latest bar.</param>
        /// <param name="avwap">Current AVWAP value.</param>
        /// <param name="atr">Current ATR value.</param>
        /// <param name="normalAtr">Normal ATR baseline value.</param>
        public void UpdateRegime(string symbol, BarData bar, double avwap, double atr, double normalAtr)
        {
            if (string.IsNullOrEmpty(symbol))
                throw new ArgumentNullException("symbol");
            if (bar == null)
                throw new ArgumentNullException("bar");
            if (normalAtr <= 0.0)
                throw new ArgumentException("normalAtr must be greater than zero", "normalAtr");
            if (atr < 0.0)
                throw new ArgumentException("atr must be non-negative", "atr");

            try
            {
                RegimeState previousState = null;
                VolatilityRegime volatilityRegime;
                TrendRegime trendRegime;
                double volatilityScore;
                double trendStrength;
                TimeSpan duration;
                DateTime updateTime = DateTime.UtcNow;

                lock (_lock)
                {
                    EnsureCollections(symbol);
                    AppendBar(symbol, bar);

                    if (_currentStates.ContainsKey(symbol))
                        previousState = _currentStates[symbol];

                    volatilityRegime = _volatilityDetector.DetectRegime(symbol, atr, normalAtr);
                    volatilityScore = _volatilityDetector.CalculateVolatilityScore(atr, normalAtr);

                    if (_barHistory[symbol].Count >= 5)
                    {
                        trendRegime = _trendDetector.DetectTrend(symbol, _barHistory[symbol], avwap);
                        trendStrength = _trendDetector.CalculateTrendStrength(_barHistory[symbol], avwap);
                    }
                    else
                    {
                        trendRegime = TrendRegime.Range;
                        trendStrength = 0.0;
                    }

                    duration = CalculateDuration(previousState, updateTime, volatilityRegime, trendRegime);

                    var indicators = new Dictionary<string, object>();
                    indicators.Add("atr", atr);
                    indicators.Add("normal_atr", normalAtr);
                    indicators.Add("volatility_score", volatilityScore);
                    indicators.Add("avwap", avwap);
                    indicators.Add("trend_strength", trendStrength);
                    indicators.Add("bar_count", _barHistory[symbol].Count);

                    var newState = new RegimeState(
                        symbol,
                        volatilityRegime,
                        trendRegime,
                        volatilityScore,
                        trendStrength,
                        updateTime,
                        duration,
                        indicators);

                    _currentStates[symbol] = newState;

                    if (HasTransition(previousState, newState))
                        AddTransition(symbol, previousState, newState, "Regime changed by detector update");
                }

                _logger.LogDebug(
                    "Regime updated for {0}: Vol={1}, Trend={2}, VolScore={3:F3}, TrendStrength={4:F3}",
                    symbol,
                    volatilityRegime,
                    trendRegime,
                    volatilityScore,
                    trendStrength);
            }
            catch (Exception ex)
            {
                _logger.LogError("UpdateRegime failed for {0}: {1}", symbol, ex.Message);
                throw;
            }
        }

        /// <summary>
        /// Gets current regime state for a symbol.
        /// </summary>
        /// <param name="symbol">Instrument symbol.</param>
        /// <returns>Current state, or null if unavailable.</returns>
        public RegimeState GetCurrentRegime(string symbol)
        {
            if (string.IsNullOrEmpty(symbol))
                throw new ArgumentNullException("symbol");

            lock (_lock)
            {
                if (!_currentStates.ContainsKey(symbol))
                    return null;

                return _currentStates[symbol];
            }
        }

        /// <summary>
        /// Returns whether strategy behavior should be adjusted for current regime.
        /// </summary>
        /// <param name="symbol">Instrument symbol.</param>
        /// <param name="intent">Current strategy intent.</param>
        /// <returns>True when strategy should adjust execution/sizing behavior.</returns>
        public bool ShouldAdjustStrategy(string symbol, StrategyIntent intent)
        {
            if (string.IsNullOrEmpty(symbol))
                throw new ArgumentNullException("symbol");
            if (intent == null)
                throw new ArgumentNullException("intent");

            try
            {
                RegimeState state;
                lock (_lock)
                {
                    if (!_currentStates.ContainsKey(symbol))
                        return false;

                    state = _currentStates[symbol];
                }

                if (state.VolatilityRegime == VolatilityRegime.Extreme)
                    return true;

                if (state.VolatilityRegime == VolatilityRegime.High)
                    return true;

                if (state.TrendRegime == TrendRegime.Range)
                    return true;

                if (state.TrendRegime == TrendRegime.StrongDown && intent.Side == OrderSide.Buy)
                    return true;

                if (state.TrendRegime == TrendRegime.StrongUp && intent.Side == OrderSide.Sell)
                    return true;

                return false;
            }
            catch (Exception ex)
            {
                _logger.LogError("ShouldAdjustStrategy failed for {0}: {1}", symbol, ex.Message);
                throw;
            }
        }

        /// <summary>
        /// Gets transitions for a symbol within a recent time period.
        /// </summary>
        /// <param name="symbol">Instrument symbol.</param>
        /// <param name="period">Lookback period.</param>
        /// <returns>Matching transition events.</returns>
        public List<RegimeTransition> GetRecentTransitions(string symbol, TimeSpan period)
        {
            if (string.IsNullOrEmpty(symbol))
                throw new ArgumentNullException("symbol");
            if (period < TimeSpan.Zero)
                throw new ArgumentException("period must be non-negative", "period");

            try
            {
                lock (_lock)
                {
                    if (!_transitions.ContainsKey(symbol))
                        return new List<RegimeTransition>();

                    var cutoff = DateTime.UtcNow - period;
                    var result = new List<RegimeTransition>();

                    var list = _transitions[symbol];
                    for (var i = 0; i < list.Count; i++)
                    {
                        if (list[i].TransitionTime >= cutoff)
                            result.Add(list[i]);
                    }

                    return result;
                }
            }
            catch (Exception ex)
            {
                _logger.LogError("GetRecentTransitions failed for {0}: {1}", symbol, ex.Message);
                throw;
            }
        }

        private void EnsureCollections(string symbol)
        {
            if (!_barHistory.ContainsKey(symbol))
                _barHistory.Add(symbol, new List<BarData>());

            if (!_transitions.ContainsKey(symbol))
                _transitions.Add(symbol, new List<RegimeTransition>());
        }

        private void AppendBar(string symbol, BarData bar)
        {
            _barHistory[symbol].Add(bar);
            while (_barHistory[symbol].Count > _maxBarsPerSymbol)
            {
                _barHistory[symbol].RemoveAt(0);
            }
        }

        private static bool HasTransition(RegimeState previousState, RegimeState newState)
        {
            if (previousState == null)
                return false;

            return previousState.VolatilityRegime != newState.VolatilityRegime ||
                   previousState.TrendRegime != newState.TrendRegime;
        }

        private static TimeSpan CalculateDuration(
            RegimeState previousState,
            DateTime updateTime,
            VolatilityRegime volatilityRegime,
            TrendRegime trendRegime)
        {
            if (previousState == null)
                return TimeSpan.Zero;

            if (previousState.VolatilityRegime == volatilityRegime && previousState.TrendRegime == trendRegime)
                return updateTime - previousState.LastUpdate + previousState.RegimeDuration;

            return TimeSpan.Zero;
        }

        private void AddTransition(string symbol, RegimeState previousState, RegimeState newState, string reason)
        {
            var previousVol = previousState != null ? previousState.VolatilityRegime : newState.VolatilityRegime;
            var previousTrend = previousState != null ? previousState.TrendRegime : newState.TrendRegime;

            var transition = new RegimeTransition(
                symbol,
                previousVol,
                newState.VolatilityRegime,
                previousTrend,
                newState.TrendRegime,
                newState.LastUpdate,
                reason);

            _transitions[symbol].Add(transition);
            while (_transitions[symbol].Count > _maxTransitionsPerSymbol)
            {
                _transitions[symbol].RemoveAt(0);
            }

            _logger.LogInformation(
                "Regime transition for {0}: Vol {1}->{2}, Trend {3}->{4}",
                symbol,
                previousVol,
                newState.VolatilityRegime,
                previousTrend,
                newState.TrendRegime);
        }
    }
}
