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

namespace NT8.Core.Execution
{
    /// <summary>
    /// Manages trailing stops for positions with various trailing methods
    /// </summary>
    public class TrailingStopManager
    {
        private readonly ILogger _logger;
        private readonly object _lock = new object();

        // Store trailing stop information for each order
        private readonly Dictionary<string, TrailingStopInfo> _trailingStops;

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

            _logger = logger;
            _trailingStops = new Dictionary<string, TrailingStopInfo>();
        }

        /// <summary>
        /// Starts trailing a stop for an order/position
        /// </summary>
        /// <param name="orderId">Order ID</param>
        /// <param name="position">Position information</param>
        /// <param name="config">Trailing stop configuration</param>
        public void StartTrailing(string orderId, OMS.OrderStatus position, TrailingStopConfig config)
        {
            if (string.IsNullOrEmpty(orderId))
                throw new ArgumentNullException("orderId");
            if (position == null)
                throw new ArgumentNullException("position");
            if (config == null)
                throw new ArgumentNullException("config");

            try
            {
                lock (_lock)
                {
                    var trailingStop = new TrailingStopInfo
                    {
                        OrderId = orderId,
                        Position = position,
                        Config = config,
                        IsActive = true,
                        LastTrackedPrice = position.AverageFillPrice,
                        LastCalculatedStop = CalculateInitialStop(position, config),
                        StartTime = DateTime.UtcNow
                    };

                    _trailingStops[orderId] = trailingStop;

                    _logger.LogDebug("Started trailing stop for {OrderId}, initial stop at {StopPrice}",
                        orderId, trailingStop.LastCalculatedStop);
                }
            }
            catch (Exception ex)
            {
                _logger.LogError("Failed to start trailing stop for {OrderId}: {Message}", orderId, ex.Message);
                throw;
            }
        }

        /// <summary>
        /// Updates the trailing stop based on current market price
        /// </summary>
        /// <param name="orderId">Order ID</param>
        /// <param name="currentPrice">Current market price</param>
        /// <returns>New stop price if updated, null if not updated</returns>
        public decimal? UpdateTrailingStop(string orderId, decimal currentPrice)
        {
            if (string.IsNullOrEmpty(orderId))
                throw new ArgumentNullException("orderId");

            try
            {
                lock (_lock)
                {
                    if (!_trailingStops.ContainsKey(orderId))
                    {
                        _logger.LogWarning("No trailing stop found for {OrderId}", orderId);
                        return null;
                    }

                    var trailingStop = _trailingStops[orderId];
                    if (!trailingStop.IsActive)
                    {
                        return null;
                    }

                    var newStopPrice = CalculateNewStopPrice(trailingStop.Config.Type, trailingStop.Position, currentPrice, trailingStop.Config);

                    // Only update if the stop has improved (moved in favorable direction)
                    var shouldUpdate = false;
                    decimal updatedStop = trailingStop.LastCalculatedStop;

                    if (trailingStop.Position.Side == OMS.OrderSide.Buy)
                    {
                        // For long positions, update if new stop is higher than previous
                        if (newStopPrice > trailingStop.LastCalculatedStop)
                        {
                            shouldUpdate = true;
                            updatedStop = newStopPrice;
                        }
                    }
                    else // Sell/Short
                    {
                        // For short positions, update if new stop is lower than previous
                        if (newStopPrice < trailingStop.LastCalculatedStop)
                        {
                            shouldUpdate = true;
                            updatedStop = newStopPrice;
                        }
                    }

                    if (shouldUpdate)
                    {
                        trailingStop.LastCalculatedStop = updatedStop;
                        trailingStop.LastTrackedPrice = currentPrice;
                        trailingStop.LastUpdateTime = DateTime.UtcNow;

                        _logger.LogDebug("Updated trailing stop for {OrderId} to {StopPrice}", orderId, updatedStop);
                        return updatedStop;
                    }

                    return null;
                }
            }
            catch (Exception ex)
            {
                _logger.LogError("Failed to update trailing stop for {OrderId}: {Message}", orderId, ex.Message);
                throw;
            }
        }

        /// <summary>
        /// Calculates the new stop price based on trailing stop type
        /// </summary>
        /// <param name="type">Type of trailing stop</param>
        /// <param name="position">Position information</param>
        /// <param name="marketPrice">Current market price</param>
        /// <param name="config">Trailing stop configuration</param>
        /// <returns>Calculated stop price</returns>
        public decimal CalculateNewStopPrice(StopType type, OMS.OrderStatus position, decimal marketPrice, TrailingStopConfig config = null)
        {
            if (position == null)
                throw new ArgumentNullException("position");

            try
            {
                if (config == null)
                {
                    config = new TrailingStopConfig(StopType.FixedTrailing, 8, 2m, true);
                }

                switch (type)
                {
                    case StopType.FixedTrailing:
                        {
                            // Tick size is fixed here as a temporary default (ES/NQ standard).
                            // TODO: provide symbol-specific tick size via configuration.
                            var tickSize = 0.25m;
                            var trailingTicks = config.TrailingAmountTicks > 0 ? config.TrailingAmountTicks : 8;
                            var distance = trailingTicks * tickSize;

                            return position.Side == OMS.OrderSide.Buy
                                ? marketPrice - distance
                                : marketPrice + distance;
                        }

                    case StopType.ATRTrailing:
                        {
                            // ATR is approximated here until live ATR is provided in config/context.
                            var atrMultiplier = config.AtrMultiplier > 0 ? config.AtrMultiplier : 2.0m;
                            var estimatedAtr = position.AverageFillPrice * 0.005m;
                            var distance = atrMultiplier * estimatedAtr;

                            return position.Side == OMS.OrderSide.Buy
                                ? marketPrice - distance
                                : marketPrice + distance;
                        }

                    case StopType.Chandelier:
                        {
                            // Chandelier approximation uses the same ATR proxy until bar history is wired in.
                            var chanMultiplier = config.AtrMultiplier > 0 ? config.AtrMultiplier : 3.0m;
                            var estimatedAtr = position.AverageFillPrice * 0.005m;
                            var distance = chanMultiplier * estimatedAtr;

                            return position.Side == OMS.OrderSide.Buy
                                ? marketPrice - distance
                                : marketPrice + distance;
                        }

                    case StopType.PercentageTrailing:
                        // Percentage trailing: trail by percentage of current price
                        var pctTrail = 0.02m;
                        return position.Side == OMS.OrderSide.Buy ?
                            marketPrice * (1 - pctTrail) :
                            marketPrice * (1 + pctTrail);

                    default:
                        // Fixed trailing as fallback
                        var tickSizeFallback = 0.25m;
                        var ticks = config.TrailingAmountTicks > 0 ? config.TrailingAmountTicks : 8;
                        return position.Side == OMS.OrderSide.Buy ?
                            marketPrice - (ticks * tickSizeFallback) :
                            marketPrice + (ticks * tickSizeFallback);
                }
            }
            catch (Exception ex)
            {
                _logger.LogError("Failed to calculate new stop price: {Message}", ex.Message);
                throw;
            }
        }

        /// <summary>
        /// Checks if a position should be moved to breakeven
        /// </summary>
        /// <param name="position">Position to check</param>
        /// <param name="currentPrice">Current market price</param>
        /// <param name="config">Auto-breakeven configuration</param>
        /// <returns>True if should move to breakeven, false otherwise</returns>
        public bool ShouldMoveToBreakeven(OMS.OrderStatus position, decimal currentPrice, AutoBreakevenConfig config)
        {
            if (position == null)
                throw new ArgumentNullException("position");
            if (config == null)
                throw new ArgumentNullException("config");

            try
            {
                if (!config.Enabled)
                    return false;

                // Calculate profit in ticks
                var profitPerContract = position.Side == OMS.OrderSide.Buy ?
                    currentPrice - position.AverageFillPrice :
                    position.AverageFillPrice - currentPrice;

                var tickSize = 0.25m; // Should be configurable per symbol
                var profitInTicks = (int)(profitPerContract / tickSize);

                return profitInTicks >= config.TicksToBreakeven;
            }
            catch (Exception ex)
            {
                _logger.LogError("Failed to check breakeven condition: {Message}", ex.Message);
                throw;
            }
        }

        /// <summary>
        /// Gets the current trailing stop price for an order
        /// </summary>
        /// <param name="orderId">Order ID to get stop for</param>
        /// <returns>Current trailing stop price</returns>
        public decimal? GetCurrentStopPrice(string orderId)
        {
            if (string.IsNullOrEmpty(orderId))
                throw new ArgumentNullException("orderId");

            try
            {
                lock (_lock)
                {
                    if (_trailingStops.ContainsKey(orderId))
                    {
                        return _trailingStops[orderId].LastCalculatedStop;
                    }
                    return null;
                }
            }
            catch (Exception ex)
            {
                _logger.LogError("Failed to get current stop price for {OrderId}: {Message}", orderId, ex.Message);
                throw;
            }
        }

        /// <summary>
        /// Deactivates trailing for an order
        /// </summary>
        /// <param name="orderId">Order ID to deactivate</param>
        public void DeactivateTrailing(string orderId)
        {
            if (string.IsNullOrEmpty(orderId))
                throw new ArgumentNullException("orderId");

            try
            {
                lock (_lock)
                {
                    if (_trailingStops.ContainsKey(orderId))
                    {
                        _trailingStops[orderId].IsActive = false;
                        _logger.LogDebug("Deactivated trailing stop for {OrderId}", orderId);
                    }
                }
            }
            catch (Exception ex)
            {
                _logger.LogError("Failed to deactivate trailing for {OrderId}: {Message}", orderId, ex.Message);
                throw;
            }
        }

        /// <summary>
        /// Removes trailing stop tracking for an order
        /// </summary>
        /// <param name="orderId">Order ID to remove</param>
        public void RemoveTrailing(string orderId)
        {
            if (string.IsNullOrEmpty(orderId))
                throw new ArgumentNullException("orderId");

            try
            {
                lock (_lock)
                {
                    if (_trailingStops.ContainsKey(orderId))
                    {
                        _trailingStops.Remove(orderId);
                        _logger.LogDebug("Removed trailing stop tracking for {OrderId}", orderId);
                    }
                }
            }
            catch (Exception ex)
            {
                _logger.LogError("Failed to remove trailing for {OrderId}: {Message}", orderId, ex.Message);
                throw;
            }
        }

        /// <summary>
        /// Calculates initial stop price based on configuration
        /// </summary>
        /// <param name="position">Position information</param>
        /// <param name="config">Trailing stop configuration</param>
        /// <returns>Initial stop price</returns>
        private decimal CalculateInitialStop(OMS.OrderStatus position, TrailingStopConfig config)
        {
            if (position == null || config == null)
                return 0;

            var tickSize = 0.25m; // Should be configurable per symbol
            var initialDistance = config.TrailingAmountTicks * tickSize;

            return position.Side == OMS.OrderSide.Buy ?
                position.AverageFillPrice - initialDistance :
                position.AverageFillPrice + initialDistance;
        }
    }

    /// <summary>
    /// Information about a trailing stop
    /// </summary>
    internal class TrailingStopInfo
    {
        /// <summary>
        /// Order ID this trailing stop is for
        /// </summary>
        public string OrderId { get; set; }

        /// <summary>
        /// Position information
        /// </summary>
        public OMS.OrderStatus Position { get; set; }

        /// <summary>
        /// Trailing stop configuration
        /// </summary>
        public TrailingStopConfig Config { get; set; }

        /// <summary>
        /// Whether trailing is currently active
        /// </summary>
        public bool IsActive { get; set; }

        /// <summary>
        /// Last price that was tracked
        /// </summary>
        public decimal LastTrackedPrice { get; set; }

        /// <summary>
        /// Last calculated stop price
        /// </summary>
        public decimal LastCalculatedStop { get; set; }

        /// <summary>
        /// When trailing was started
        /// </summary>
        public DateTime StartTime { get; set; }

        /// <summary>
        /// When stop was last updated
        /// </summary>
        public DateTime LastUpdateTime { get; set; }
    }
}
