using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.Extensions.Logging;

namespace NT8.Core.MarketData
{
    /// <summary>
    /// Monitors liquidity conditions for symbols and calculates liquidity scores
    /// </summary>
    public class LiquidityMonitor
    {
        private readonly ILogger _logger;
        private readonly object _lock = new object();

        // Store spread information for each symbol
        private readonly Dictionary<string, Queue<double>> _spreadHistory;
        private readonly Dictionary<string, SpreadInfo> _currentSpreads;
        private readonly Dictionary<string, LiquidityMetrics> _liquidityMetrics;

        // Default window size for rolling spread calculations
        private const int SPREAD_WINDOW_SIZE = 100;

        /// <summary>
        /// Constructor for LiquidityMonitor
        /// </summary>
        /// <param name="logger">Logger instance</param>
        public LiquidityMonitor(ILogger<LiquidityMonitor> logger)
        {
            if (logger == null)
                throw new ArgumentNullException("logger");

            _logger = logger;
            _spreadHistory = new Dictionary<string, Queue<double>>();
            _currentSpreads = new Dictionary<string, SpreadInfo>();
            _liquidityMetrics = new Dictionary<string, LiquidityMetrics>();
        }

        /// <summary>
        /// Updates the spread information for a symbol
        /// </summary>
        /// <param name="symbol">Symbol to update</param>
        /// <param name="bid">Current bid price</param>
        /// <param name="ask">Current ask price</param>
        /// <param name="volume">Current volume</param>
        public void UpdateSpread(string symbol, double bid, double ask, long volume)
        {
            if (string.IsNullOrEmpty(symbol))
                throw new ArgumentNullException("symbol");

            try
            {
                lock (_lock)
                {
                    var spreadInfo = new SpreadInfo(symbol, bid, ask, DateTime.UtcNow);

                    // Update current spreads
                    _currentSpreads[symbol] = spreadInfo;

                    // Maintain rolling window of spread history
                    if (!_spreadHistory.ContainsKey(symbol))
                    {
                        _spreadHistory[symbol] = new Queue<double>();
                    }

                    var spreadQueue = _spreadHistory[symbol];
                    spreadQueue.Enqueue(spreadInfo.Spread);

                    // Keep only the last N spreads
                    while (spreadQueue.Count > SPREAD_WINDOW_SIZE)
                    {
                        spreadQueue.Dequeue();
                    }

                    _logger.LogDebug("Updated spread for {Symbol}: {Spread:F4}", symbol, spreadInfo.Spread);
                }
            }
            catch (Exception ex)
            {
                _logger.LogError("Failed to update spread for {Symbol}: {Message}", symbol, ex.Message);
                throw;
            }
        }

        /// <summary>
        /// Gets liquidity metrics for a symbol
        /// </summary>
        /// <param name="symbol">Symbol to get metrics for</param>
        /// <returns>Liquidity metrics for the symbol</returns>
        public LiquidityMetrics GetLiquidityMetrics(string symbol)
        {
            if (string.IsNullOrEmpty(symbol))
                throw new ArgumentNullException("symbol");

            try
            {
                lock (_lock)
                {
                    if (_currentSpreads.ContainsKey(symbol))
                    {
                        var currentSpread = _currentSpreads[symbol];

                        // Calculate average spread from history
                        double averageSpread = 0;
                        if (_spreadHistory.ContainsKey(symbol) && _spreadHistory[symbol].Count > 0)
                        {
                            var spreads = _spreadHistory[symbol].ToList();
                            averageSpread = spreads.Average();
                        }

                        // For now, we'll use placeholder values for volume-based metrics
                        // In a real implementation, these would come from order book data
                        var metrics = new LiquidityMetrics(
                            symbol,
                            currentSpread.Spread,
                            averageSpread,
                            1000, // Placeholder bid volume
                            1000, // Placeholder ask volume
                            10000, // Placeholder total depth
                            10, // Placeholder order levels
                            DateTime.UtcNow
                        );

                        _liquidityMetrics[symbol] = metrics;
                        return metrics;
                    }

                    // Return default metrics if no data available
                    return new LiquidityMetrics(
                        symbol,
                        0,
                        0,
                        0,
                        0,
                        0,
                        0,
                        DateTime.UtcNow
                    );
                }
            }
            catch (Exception ex)
            {
                _logger.LogError("Failed to get liquidity metrics for {Symbol}: {Message}", symbol, ex.Message);
                throw;
            }
        }

        /// <summary>
        /// Calculates liquidity score for a symbol
        /// </summary>
        /// <param name="symbol">Symbol to calculate score for</param>
        /// <returns>Liquidity score for the symbol</returns>
        public LiquidityScore CalculateLiquidityScore(string symbol)
        {
            if (string.IsNullOrEmpty(symbol))
                throw new ArgumentNullException("symbol");

            try
            {
                var metrics = GetLiquidityMetrics(symbol);

                // Calculate score based on spread and depth
                // Lower spread and higher depth = better liquidity
                double normalizedSpread = metrics.Spread > 0 ? metrics.Spread / metrics.AverageSpread : double.MaxValue;

                // Depth score (higher is better)
                double depthScore = metrics.TotalDepth > 0 ? Math.Min(metrics.TotalDepth / 10000.0, 1.0) : 0;

                // Volume score (higher is better)
                double volumeScore = (metrics.BidVolume + metrics.AskVolume) > 0 ?
                    Math.Min((metrics.BidVolume + metrics.AskVolume) / 5000.0, 1.0) : 0;

                // Calculate overall score based on spread and depth
                if (normalizedSpread >= 2.0 || metrics.Spread <= 0)
                {
                    return LiquidityScore.Poor;
                }
                else if (normalizedSpread >= 1.5)
                {
                    return LiquidityScore.Fair;
                }
                else if (normalizedSpread >= 1.0)
                {
                    return LiquidityScore.Good;
                }
                else
                {
                    return LiquidityScore.Excellent;
                }
            }
            catch (Exception ex)
            {
                _logger.LogError("Failed to calculate liquidity score for {Symbol}: {Message}", symbol, ex.Message);
                throw;
            }
        }

        /// <summary>
        /// Determines if liquidity is acceptable for a symbol based on threshold
        /// </summary>
        /// <param name="symbol">Symbol to check</param>
        /// <param name="threshold">Minimum acceptable liquidity score</param>
        /// <returns>True if liquidity is acceptable, false otherwise</returns>
        public bool IsLiquidityAcceptable(string symbol, LiquidityScore threshold)
        {
            if (string.IsNullOrEmpty(symbol))
                throw new ArgumentNullException("symbol");

            try
            {
                var currentScore = CalculateLiquidityScore(symbol);
                return currentScore >= threshold;
            }
            catch (Exception ex)
            {
                _logger.LogError("Failed to check liquidity acceptability for {Symbol}: {Message}", symbol, ex.Message);
                throw;
            }
        }

        /// <summary>
        /// Gets the current spread for a symbol
        /// </summary>
        /// <param name="symbol">Symbol to get spread for</param>
        /// <returns>Current spread for the symbol</returns>
        public double GetCurrentSpread(string symbol)
        {
            if (string.IsNullOrEmpty(symbol))
                throw new ArgumentNullException("symbol");

            try
            {
                lock (_lock)
                {
                    if (_currentSpreads.ContainsKey(symbol))
                    {
                        return _currentSpreads[symbol].Spread;
                    }
                    return 0;
                }
            }
            catch (Exception ex)
            {
                _logger.LogError("Failed to get current spread for {Symbol}: {Message}", symbol, ex.Message);
                throw;
            }
        }

        /// <summary>
        /// Gets the average spread for a symbol based on historical data
        /// </summary>
        /// <param name="symbol">Symbol to get average spread for</param>
        /// <returns>Average spread for the symbol</returns>
        public double GetAverageSpread(string symbol)
        {
            if (string.IsNullOrEmpty(symbol))
                throw new ArgumentNullException("symbol");

            try
            {
                lock (_lock)
                {
                    if (_spreadHistory.ContainsKey(symbol) && _spreadHistory[symbol].Count > 0)
                    {
                        return _spreadHistory[symbol].Average();
                    }
                    return 0;
                }
            }
            catch (Exception ex)
            {
                _logger.LogError("Failed to get average spread for {Symbol}: {Message}", symbol, ex.Message);
                throw;
            }
        }

        /// <summary>
        /// Clears spread history for a symbol
        /// </summary>
        /// <param name="symbol">Symbol to clear history for</param>
        public void ClearSpreadHistory(string symbol)
        {
            if (string.IsNullOrEmpty(symbol))
                throw new ArgumentNullException("symbol");

            try
            {
                lock (_lock)
                {
                    if (_spreadHistory.ContainsKey(symbol))
                    {
                        _spreadHistory[symbol].Clear();
                    }
                    if (_currentSpreads.ContainsKey(symbol))
                    {
                        _currentSpreads.Remove(symbol);
                    }
                    if (_liquidityMetrics.ContainsKey(symbol))
                    {
                        _liquidityMetrics.Remove(symbol);
                    }

                    _logger.LogDebug("Cleared spread history for {Symbol}", symbol);
                }
            }
            catch (Exception ex)
            {
                _logger.LogError("Failed to clear spread history for {Symbol}: {Message}", symbol, ex.Message);
                throw;
            }
        }
    }
}
