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

namespace NT8.Core.Analytics
{
    /// <summary>
    /// Filterable and sortable trade blotter service.
    /// </summary>
    public class TradeBlotter
    {
        private readonly ILogger _logger;
        private readonly object _lock;
        private readonly List<TradeRecord> _trades;

        public TradeBlotter(ILogger logger)
        {
            if (logger == null)
                throw new ArgumentNullException("logger");

            _logger = logger;
            _lock = new object();
            _trades = new List<TradeRecord>();
        }

        /// <summary>
        /// Replaces blotter trade set.
        /// </summary>
        public void SetTrades(List<TradeRecord> trades)
        {
            if (trades == null)
                throw new ArgumentNullException("trades");

            try
            {
                lock (_lock)
                {
                    _trades.Clear();
                    _trades.AddRange(trades.Select(Clone));
                }
            }
            catch (Exception ex)
            {
                _logger.LogError("SetTrades failed: {0}", ex.Message);
                throw;
            }
        }

        /// <summary>
        /// Appends one trade and supports real-time update flow.
        /// </summary>
        public void AddOrUpdateTrade(TradeRecord trade)
        {
            if (trade == null)
                throw new ArgumentNullException("trade");

            try
            {
                lock (_lock)
                {
                    var index = _trades.FindIndex(t => t.TradeId == trade.TradeId);
                    if (index >= 0)
                        _trades[index] = Clone(trade);
                    else
                        _trades.Add(Clone(trade));
                }
            }
            catch (Exception ex)
            {
                _logger.LogError("AddOrUpdateTrade failed: {0}", ex.Message);
                throw;
            }
        }

        /// <summary>
        /// Filters by date range.
        /// </summary>
        public List<TradeRecord> FilterByDate(DateTime start, DateTime end)
        {
            try
            {
                lock (_lock)
                {
                    return _trades
                        .Where(t => t.EntryTime >= start && t.EntryTime <= end)
                        .OrderBy(t => t.EntryTime)
                        .Select(Clone)
                        .ToList();
                }
            }
            catch (Exception ex)
            {
                _logger.LogError("FilterByDate failed: {0}", ex.Message);
                throw;
            }
        }

        /// <summary>
        /// Filters by symbol.
        /// </summary>
        public List<TradeRecord> FilterBySymbol(string symbol)
        {
            if (string.IsNullOrEmpty(symbol))
                throw new ArgumentNullException("symbol");

            try
            {
                lock (_lock)
                {
                    return _trades
                        .Where(t => string.Equals(t.Symbol, symbol, StringComparison.OrdinalIgnoreCase))
                        .OrderBy(t => t.EntryTime)
                        .Select(Clone)
                        .ToList();
                }
            }
            catch (Exception ex)
            {
                _logger.LogError("FilterBySymbol failed: {0}", ex.Message);
                throw;
            }
        }

        /// <summary>
        /// Filters by grade.
        /// </summary>
        public List<TradeRecord> FilterByGrade(TradeGrade grade)
        {
            try
            {
                lock (_lock)
                {
                    return _trades
                        .Where(t => t.Grade == grade)
                        .OrderBy(t => t.EntryTime)
                        .Select(Clone)
                        .ToList();
                }
            }
            catch (Exception ex)
            {
                _logger.LogError("FilterByGrade failed: {0}", ex.Message);
                throw;
            }
        }

        /// <summary>
        /// Filters by realized pnl range.
        /// </summary>
        public List<TradeRecord> FilterByPnL(double minPnL, double maxPnL)
        {
            try
            {
                lock (_lock)
                {
                    return _trades
                        .Where(t => t.RealizedPnL >= minPnL && t.RealizedPnL <= maxPnL)
                        .OrderBy(t => t.EntryTime)
                        .Select(Clone)
                        .ToList();
                }
            }
            catch (Exception ex)
            {
                _logger.LogError("FilterByPnL failed: {0}", ex.Message);
                throw;
            }
        }

        /// <summary>
        /// Sorts by named column.
        /// </summary>
        public List<TradeRecord> SortBy(string column, SortDirection direction)
        {
            if (string.IsNullOrEmpty(column))
                throw new ArgumentNullException("column");

            try
            {
                lock (_lock)
                {
                    IEnumerable<TradeRecord> ordered;
                    var normalized = column.Trim().ToLowerInvariant();

                    switch (normalized)
                    {
                        case "time":
                        case "entrytime":
                            ordered = direction == SortDirection.Asc
                                ? _trades.OrderBy(t => t.EntryTime)
                                : _trades.OrderByDescending(t => t.EntryTime);
                            break;
                        case "symbol":
                            ordered = direction == SortDirection.Asc
                                ? _trades.OrderBy(t => t.Symbol)
                                : _trades.OrderByDescending(t => t.Symbol);
                            break;
                        case "pnl":
                            ordered = direction == SortDirection.Asc
                                ? _trades.OrderBy(t => t.RealizedPnL)
                                : _trades.OrderByDescending(t => t.RealizedPnL);
                            break;
                        case "grade":
                            ordered = direction == SortDirection.Asc
                                ? _trades.OrderBy(t => t.Grade)
                                : _trades.OrderByDescending(t => t.Grade);
                            break;
                        case "rmultiple":
                            ordered = direction == SortDirection.Asc
                                ? _trades.OrderBy(t => t.RMultiple)
                                : _trades.OrderByDescending(t => t.RMultiple);
                            break;
                        case "duration":
                            ordered = direction == SortDirection.Asc
                                ? _trades.OrderBy(t => t.Duration)
                                : _trades.OrderByDescending(t => t.Duration);
                            break;
                        default:
                            ordered = direction == SortDirection.Asc
                                ? _trades.OrderBy(t => t.EntryTime)
                                : _trades.OrderByDescending(t => t.EntryTime);
                            break;
                    }

                    return ordered.Select(Clone).ToList();
                }
            }
            catch (Exception ex)
            {
                _logger.LogError("SortBy failed: {0}", ex.Message);
                throw;
            }
        }

        private static TradeRecord Clone(TradeRecord input)
        {
            var copy = new TradeRecord();
            copy.TradeId = input.TradeId;
            copy.Symbol = input.Symbol;
            copy.StrategyName = input.StrategyName;
            copy.EntryTime = input.EntryTime;
            copy.ExitTime = input.ExitTime;
            copy.Side = input.Side;
            copy.Quantity = input.Quantity;
            copy.EntryPrice = input.EntryPrice;
            copy.ExitPrice = input.ExitPrice;
            copy.RealizedPnL = input.RealizedPnL;
            copy.UnrealizedPnL = input.UnrealizedPnL;
            copy.Grade = input.Grade;
            copy.ConfluenceScore = input.ConfluenceScore;
            copy.RiskMode = input.RiskMode;
            copy.VolatilityRegime = input.VolatilityRegime;
            copy.TrendRegime = input.TrendRegime;
            copy.StopTicks = input.StopTicks;
            copy.TargetTicks = input.TargetTicks;
            copy.RMultiple = input.RMultiple;
            copy.Duration = input.Duration;
            copy.Metadata = new Dictionary<string, object>(input.Metadata);
            return copy;
        }
    }
}
