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

namespace NT8.Core.Intelligence
{
    /// <summary>
    /// Detects volatility regimes from current and normal ATR values and tracks transitions.
    /// </summary>
    public class VolatilityRegimeDetector
    {
        private readonly ILogger _logger;
        private readonly object _lock = new object();
        private readonly Dictionary<string, VolatilityRegime> _currentRegimes;
        private readonly Dictionary<string, DateTime> _lastTransitionTimes;
        private readonly Dictionary<string, List<RegimeTransition>> _transitionHistory;
        private readonly int _maxHistoryPerSymbol;

        /// <summary>
        /// Creates a new volatility regime detector.
        /// </summary>
        /// <param name="logger">Logger instance.</param>
        /// <param name="maxHistoryPerSymbol">Maximum transitions to keep per symbol.</param>
        /// <exception cref="ArgumentNullException">Logger is null.</exception>
        /// <exception cref="ArgumentException">History size is not positive.</exception>
        public VolatilityRegimeDetector(ILogger logger, int maxHistoryPerSymbol)
        {
            if (logger == null)
                throw new ArgumentNullException("logger");
            if (maxHistoryPerSymbol <= 0)
                throw new ArgumentException("maxHistoryPerSymbol must be greater than zero", "maxHistoryPerSymbol");

            _logger = logger;
            _maxHistoryPerSymbol = maxHistoryPerSymbol;
            _currentRegimes = new Dictionary<string, VolatilityRegime>();
            _lastTransitionTimes = new Dictionary<string, DateTime>();
            _transitionHistory = new Dictionary<string, List<RegimeTransition>>();
        }

        /// <summary>
        /// Detects the current volatility regime for a symbol.
        /// </summary>
        /// <param name="symbol">Instrument symbol.</param>
        /// <param name="currentAtr">Current ATR value.</param>
        /// <param name="normalAtr">Normal ATR baseline value.</param>
        /// <returns>Detected volatility regime.</returns>
        public VolatilityRegime DetectRegime(string symbol, double currentAtr, double normalAtr)
        {
            if (string.IsNullOrEmpty(symbol))
                throw new ArgumentNullException("symbol");
            if (currentAtr < 0.0)
                throw new ArgumentException("currentAtr must be non-negative", "currentAtr");
            if (normalAtr <= 0.0)
                throw new ArgumentException("normalAtr must be greater than zero", "normalAtr");

            try
            {
                var score = CalculateVolatilityScore(currentAtr, normalAtr);
                var regime = ClassifyRegime(score);

                lock (_lock)
                {
                    VolatilityRegime previous;
                    var hasPrevious = _currentRegimes.TryGetValue(symbol, out previous);

                    if (hasPrevious && IsRegimeTransition(regime, previous))
                    {
                        AddTransition(symbol, previous, regime, "ATR ratio threshold crossed");
                    }
                    else if (!hasPrevious)
                    {
                        _lastTransitionTimes[symbol] = DateTime.UtcNow;
                    }

                    _currentRegimes[symbol] = regime;
                }

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

        /// <summary>
        /// Returns true when current and previous regimes differ.
        /// </summary>
        /// <param name="current">Current regime.</param>
        /// <param name="previous">Previous regime.</param>
        /// <returns>True when transition occurred.</returns>
        public bool IsRegimeTransition(VolatilityRegime current, VolatilityRegime previous)
        {
            return current != previous;
        }

        /// <summary>
        /// Calculates volatility score as current ATR divided by normal ATR.
        /// </summary>
        /// <param name="currentAtr">Current ATR value.</param>
        /// <param name="normalAtr">Normal ATR baseline value.</param>
        /// <returns>Volatility score ratio.</returns>
        public double CalculateVolatilityScore(double currentAtr, double normalAtr)
        {
            if (currentAtr < 0.0)
                throw new ArgumentException("currentAtr must be non-negative", "currentAtr");
            if (normalAtr <= 0.0)
                throw new ArgumentException("normalAtr must be greater than zero", "normalAtr");

            return currentAtr / normalAtr;
        }

        /// <summary>
        /// Updates internal regime history for a symbol with an externally provided regime.
        /// </summary>
        /// <param name="symbol">Instrument symbol.</param>
        /// <param name="regime">Detected regime.</param>
        public void UpdateRegimeHistory(string symbol, VolatilityRegime regime)
        {
            if (string.IsNullOrEmpty(symbol))
                throw new ArgumentNullException("symbol");

            try
            {
                lock (_lock)
                {
                    VolatilityRegime previous;
                    var hasPrevious = _currentRegimes.TryGetValue(symbol, out previous);

                    if (hasPrevious && IsRegimeTransition(regime, previous))
                    {
                        AddTransition(symbol, previous, regime, "External regime update");
                    }
                    else if (!hasPrevious)
                    {
                        _lastTransitionTimes[symbol] = DateTime.UtcNow;
                    }

                    _currentRegimes[symbol] = regime;
                }
            }
            catch (Exception ex)
            {
                _logger.LogError("UpdateRegimeHistory failed for {0}: {1}", symbol, ex.Message);
                throw;
            }
        }

        /// <summary>
        /// Gets the current volatility regime for a symbol.
        /// </summary>
        /// <param name="symbol">Instrument symbol.</param>
        /// <returns>Current regime or Normal when unknown.</returns>
        public VolatilityRegime GetCurrentRegime(string symbol)
        {
            if (string.IsNullOrEmpty(symbol))
                throw new ArgumentNullException("symbol");

            lock (_lock)
            {
                VolatilityRegime regime;
                if (_currentRegimes.TryGetValue(symbol, out regime))
                    return regime;

                return VolatilityRegime.Normal;
            }
        }

        /// <summary>
        /// Returns duration spent in the current regime for a symbol.
        /// </summary>
        /// <param name="symbol">Instrument symbol.</param>
        /// <returns>Time elapsed since last transition, or zero if unknown.</returns>
        public TimeSpan GetRegimeDuration(string symbol)
        {
            if (string.IsNullOrEmpty(symbol))
                throw new ArgumentNullException("symbol");

            lock (_lock)
            {
                DateTime transitionTime;
                if (_lastTransitionTimes.TryGetValue(symbol, out transitionTime))
                    return DateTime.UtcNow - transitionTime;

                return TimeSpan.Zero;
            }
        }

        /// <summary>
        /// Gets recent transition events for a symbol.
        /// </summary>
        /// <param name="symbol">Instrument symbol.</param>
        /// <returns>Transition list in chronological order.</returns>
        public List<RegimeTransition> GetTransitions(string symbol)
        {
            if (string.IsNullOrEmpty(symbol))
                throw new ArgumentNullException("symbol");

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

                return new List<RegimeTransition>(_transitionHistory[symbol]);
            }
        }

        private VolatilityRegime ClassifyRegime(double score)
        {
            if (score < 0.6)
                return VolatilityRegime.Low;
            if (score < 0.8)
                return VolatilityRegime.BelowNormal;
            if (score <= 1.2)
                return VolatilityRegime.Normal;
            if (score <= 1.5)
                return VolatilityRegime.Elevated;
            if (score <= 2.0)
                return VolatilityRegime.High;

            return VolatilityRegime.Extreme;
        }

        private void AddTransition(string symbol, VolatilityRegime previous, VolatilityRegime current, string reason)
        {
            if (!_transitionHistory.ContainsKey(symbol))
                _transitionHistory.Add(symbol, new List<RegimeTransition>());

            var transition = new RegimeTransition(
                symbol,
                previous,
                current,
                TrendRegime.Range,
                TrendRegime.Range,
                DateTime.UtcNow,
                reason);

            _transitionHistory[symbol].Add(transition);

            while (_transitionHistory[symbol].Count > _maxHistoryPerSymbol)
            {
                _transitionHistory[symbol].RemoveAt(0);
            }

            _lastTransitionTimes[symbol] = transition.TransitionTime;

            _logger.LogInformation("Volatility regime transition for {0}: {1} -> {2}", symbol, previous, current);
        }
    }
}
