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

namespace NT8.Core.Execution
{
    /// <summary>
    /// Tracks execution quality for orders and maintains statistics
    /// </summary>
    public class ExecutionQualityTracker
    {
        private readonly ILogger _logger;
        private readonly object _lock = new object();

        // Store execution metrics for each order
        private readonly Dictionary<string, ExecutionMetrics> _executionMetrics;

        // Store execution history by symbol
        private readonly Dictionary<string, Queue<ExecutionMetrics>> _symbolExecutionHistory;

        // Rolling window size for statistics
        private const int ROLLING_WINDOW_SIZE = 100;

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

            _logger = logger;
            _executionMetrics = new Dictionary<string, ExecutionMetrics>();
            _symbolExecutionHistory = new Dictionary<string, Queue<ExecutionMetrics>>();
        }

        /// <summary>
        /// Records an execution for tracking
        /// </summary>
        /// <param name="orderId">Order ID</param>
        /// <param name="intendedPrice">Intended price when order was placed</param>
        /// <param name="fillPrice">Actual fill price</param>
        /// <param name="fillTime">Time of fill</param>
        /// <param name="submitTime">Time of submission</param>
        /// <param name="intentTime">Time of intent formation</param>
        public void RecordExecution(
            string orderId,
            decimal intendedPrice,
            decimal fillPrice,
            DateTime fillTime,
            DateTime submitTime,
            DateTime intentTime)
        {
            if (string.IsNullOrEmpty(orderId))
                throw new ArgumentNullException("orderId");

            try
            {
                var slippage = fillPrice - intendedPrice;
                var slippageType = slippage > 0 ? SlippageType.Positive :
                                  slippage < 0 ? SlippageType.Negative : SlippageType.Zero;

                var submitLatency = submitTime - intentTime;
                var fillLatency = fillTime - intentTime;

                var quality = CalculateExecutionQuality(slippage, submitLatency, fillLatency);

                var metrics = new ExecutionMetrics(
                    orderId,
                    intentTime,
                    submitTime,
                    fillTime,
                    intendedPrice,
                    fillPrice,
                    slippage,
                    slippageType,
                    submitLatency,
                    fillLatency,
                    quality);

                lock (_lock)
                {
                    _executionMetrics[orderId] = metrics;

                    // Add to symbol history
                    var symbol = ExtractSymbolFromOrderId(orderId);
                    if (!_symbolExecutionHistory.ContainsKey(symbol))
                    {
                        _symbolExecutionHistory[symbol] = new Queue<ExecutionMetrics>();
                    }

                    var symbolHistory = _symbolExecutionHistory[symbol];
                    symbolHistory.Enqueue(metrics);

                    // Keep only the last N executions
                    while (symbolHistory.Count > ROLLING_WINDOW_SIZE)
                    {
                        symbolHistory.Dequeue();
                    }
                }

                _logger.LogDebug("Recorded execution for {OrderId}: Slippage={Slippage:F4}, Quality={Quality}",
                    orderId, slippage, quality);
            }
            catch (Exception ex)
            {
                _logger.LogError("Failed to record execution for {OrderId}: {Message}", orderId, ex.Message);
                throw;
            }
        }

        /// <summary>
        /// Gets execution metrics for a specific order
        /// </summary>
        /// <param name="orderId">Order ID to get metrics for</param>
        /// <returns>Execution metrics for the order</returns>
        public ExecutionMetrics GetExecutionMetrics(string orderId)
        {
            if (string.IsNullOrEmpty(orderId))
                throw new ArgumentNullException("orderId");

            try
            {
                lock (_lock)
                {
                    ExecutionMetrics metrics;
                    _executionMetrics.TryGetValue(orderId, out metrics);
                    return metrics;
                }
            }
            catch (Exception ex)
            {
                _logger.LogError("Failed to get execution metrics for {OrderId}: {Message}", orderId, ex.Message);
                throw;
            }
        }

        /// <summary>
        /// Gets execution statistics for a symbol
        /// </summary>
        /// <param name="symbol">Symbol to get statistics for</param>
        /// <returns>Execution statistics for the symbol</returns>
        public ExecutionStatistics GetSymbolStatistics(string symbol)
        {
            if (string.IsNullOrEmpty(symbol))
                throw new ArgumentNullException("symbol");

            try
            {
                lock (_lock)
                {
                    if (_symbolExecutionHistory.ContainsKey(symbol))
                    {
                        var history = _symbolExecutionHistory[symbol].ToList();

                        if (history.Count == 0)
                        {
                            return new ExecutionStatistics(
                                symbol,
                                0,
                                TimeSpan.Zero,
                                TimeSpan.Zero,
                                0,
                                0,
                                ExecutionQuality.Poor
                            );
                        }

                        var avgSlippage = history.Average(x => (double)x.Slippage);
                        var avgSubmitLatency = TimeSpan.FromMilliseconds(history.Average(x => x.SubmitLatency.TotalMilliseconds));
                        var avgFillLatency = TimeSpan.FromMilliseconds(history.Average(x => x.FillLatency.TotalMilliseconds));
                        var avgQuality = history.GroupBy(x => x.Quality)
                                              .OrderByDescending(g => g.Count())
                                              .First().Key;

                        var positiveSlippageCount = history.Count(x => x.Slippage > 0);
                        var negativeSlippageCount = history.Count(x => x.Slippage < 0);
                        var zeroSlippageCount = history.Count(x => x.Slippage == 0);

                        return new ExecutionStatistics(
                            symbol,
                            avgSlippage,
                            avgSubmitLatency,
                            avgFillLatency,
                            positiveSlippageCount,
                            negativeSlippageCount,
                            avgQuality
                        );
                    }

                    return new ExecutionStatistics(
                        symbol,
                        0,
                        TimeSpan.Zero,
                        TimeSpan.Zero,
                        0,
                        0,
                        ExecutionQuality.Poor
                    );
                }
            }
            catch (Exception ex)
            {
                _logger.LogError("Failed to get execution statistics for {Symbol}: {Message}", symbol, ex.Message);
                throw;
            }
        }

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

            try
            {
                var stats = GetSymbolStatistics(symbol);
                return stats.AverageSlippage;
            }
            catch (Exception ex)
            {
                _logger.LogError("Failed to get average slippage for {Symbol}: {Message}", symbol, ex.Message);
                throw;
            }
        }

        /// <summary>
        /// Checks if execution quality for a symbol is acceptable
        /// </summary>
        /// <param name="symbol">Symbol to check</param>
        /// <param name="threshold">Minimum acceptable quality</param>
        /// <returns>True if execution quality is acceptable, false otherwise</returns>
        public bool IsExecutionQualityAcceptable(string symbol, ExecutionQuality threshold = ExecutionQuality.Fair)
        {
            if (string.IsNullOrEmpty(symbol))
                throw new ArgumentNullException("symbol");

            try
            {
                var stats = GetSymbolStatistics(symbol);
                return stats.AverageQuality >= threshold;
            }
            catch (Exception ex)
            {
                _logger.LogError("Failed to check execution quality for {Symbol}: {Message}", symbol, ex.Message);
                throw;
            }
        }

        /// <summary>
        /// Calculates execution quality based on slippage and latencies
        /// </summary>
        /// <param name="slippage">Price slippage</param>
        /// <param name="submitLatency">Submission latency</param>
        /// <param name="fillLatency">Fill latency</param>
        /// <returns>Calculated execution quality</returns>
        private ExecutionQuality CalculateExecutionQuality(decimal slippage, TimeSpan submitLatency, TimeSpan fillLatency)
        {
            // Determine quality based on slippage and latencies
            // Positive slippage is good, negative is bad
            // Lower latencies are better

            // If we have positive slippage (better than expected), quality is higher
            if (slippage > 0)
            {
                // Low latency is excellent, high latency is good
                if (fillLatency.TotalMilliseconds < 100) // Less than 100ms
                    return ExecutionQuality.Excellent;
                else
                    return ExecutionQuality.Good;
            }
            else if (slippage == 0)
            {
                // No slippage, check latencies
                if (fillLatency.TotalMilliseconds < 100)
                    return ExecutionQuality.Good;
                else
                    return ExecutionQuality.Fair;
            }
            else // slippage < 0
            {
                // Negative slippage, check severity
                if (Math.Abs((double)slippage) < 0.01) // Small negative slippage
                {
                    if (fillLatency.TotalMilliseconds < 100)
                        return ExecutionQuality.Fair;
                    else
                        return ExecutionQuality.Poor;
                }
                else // Significant negative slippage
                {
                    return ExecutionQuality.Poor;
                }
            }
        }

        /// <summary>
        /// Extracts symbol from order ID (assumes format SYMBOL-XXXX)
        /// </summary>
        /// <param name="orderId">Order ID to extract symbol from</param>
        /// <returns>Extracted symbol</returns>
        private string ExtractSymbolFromOrderId(string orderId)
        {
            if (string.IsNullOrEmpty(orderId))
                return "UNKNOWN";

            // Split by hyphen and take first part as symbol
            var parts = orderId.Split('-');
            return parts.Length > 0 ? parts[0] : "UNKNOWN";
        }

        /// <summary>
        /// Gets total number of executions tracked
        /// </summary>
        /// <returns>Total execution count</returns>
        public int GetTotalExecutionCount()
        {
            try
            {
                lock (_lock)
                {
                    return _executionMetrics.Count;
                }
            }
            catch (Exception ex)
            {
                _logger.LogError("Failed to get total execution count: {Message}", ex.Message);
                throw;
            }
        }

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

            try
            {
                lock (_lock)
                {
                    if (_symbolExecutionHistory.ContainsKey(symbol))
                    {
                        _symbolExecutionHistory[symbol].Clear();
                        _logger.LogDebug("Cleared execution history for {Symbol}", symbol);
                    }
                }
            }
            catch (Exception ex)
            {
                _logger.LogError("Failed to clear execution history for {Symbol}: {Message}", symbol, ex.Message);
                throw;
            }
        }
    }

    /// <summary>
    /// Execution statistics for a symbol
    /// </summary>
    public class ExecutionStatistics
    {
        /// <summary>
        /// Symbol these statistics are for
        /// </summary>
        public string Symbol { get; set; }

        /// <summary>
        /// Average slippage
        /// </summary>
        public double AverageSlippage { get; set; }

        /// <summary>
        /// Average submission latency
        /// </summary>
        public TimeSpan AverageSubmitLatency { get; set; }

        /// <summary>
        /// Average fill latency
        /// </summary>
        public TimeSpan AverageFillLatency { get; set; }

        /// <summary>
        /// Count of executions with positive slippage
        /// </summary>
        public int PositiveSlippageCount { get; set; }

        /// <summary>
        /// Count of executions with negative slippage
        /// </summary>
        public int NegativeSlippageCount { get; set; }

        /// <summary>
        /// Average execution quality
        /// </summary>
        public ExecutionQuality AverageQuality { get; set; }

        /// <summary>
        /// Constructor for ExecutionStatistics
        /// </summary>
        /// <param name="symbol">Symbol for statistics</param>
        /// <param name="avgSlippage">Average slippage</param>
        /// <param name="avgSubmitLatency">Average submission latency</param>
        /// <param name="avgFillLatency">Average fill latency</param>
        /// <param name="posSlippageCount">Positive slippage count</param>
        /// <param name="negSlippageCount">Negative slippage count</param>
        /// <param name="avgQuality">Average quality</param>
        public ExecutionStatistics(
            string symbol,
            double avgSlippage,
            TimeSpan avgSubmitLatency,
            TimeSpan avgFillLatency,
            int posSlippageCount,
            int negSlippageCount,
            ExecutionQuality avgQuality)
        {
            if (string.IsNullOrEmpty(symbol))
                throw new ArgumentNullException("symbol");

            Symbol = symbol;
            AverageSlippage = avgSlippage;
            AverageSubmitLatency = avgSubmitLatency;
            AverageFillLatency = avgFillLatency;
            PositiveSlippageCount = posSlippageCount;
            NegativeSlippageCount = negSlippageCount;
            AverageQuality = avgQuality;
        }
    }
}
