using System;
using System.Collections.Generic;
using cAlgo.API;
using cAlgo.API.Internals;

namespace cAlgo.Robots
{
    [Robot(TimeZone = TimeZones.UTC, AccessRights = AccessRights.None)]
    public class SimpleLossCloser : Robot
    {
        [Parameter("Max Loss Amount (EUR)", DefaultValue = 0.95, MinValue = 0.01)]
        public double MaxLossAmount { get; set; }

        [Parameter("Use Profit Protection (Rule 2)", DefaultValue = true)]
        public bool UseProfitProtection { get; set; }

        [Parameter("Use Drawdown Close (Rule 3)", DefaultValue = true)]
        public bool UseDrawdownCloseRule { get; set; }

        // Stores the peak profit (G) achieved for each position (Position.Id)
        private readonly Dictionary<int, double> _maxProfits = new Dictionary<int, double>();
        
        // Stores the max drawdown (D) (lowest negative profit) for each position
        private readonly Dictionary<int, double> _maxDrawdowns = new Dictionary<int, double>();

        protected override void OnStart()
        {
            // Subscribe to position close events to clean up the dictionaries
            Positions.Closed += OnPositionClosed;
        }

        protected override void OnTick()
        {
            // Iterate over all open positions for the current symbol
            foreach (var position in Positions)
            {
                if (position.SymbolName != SymbolName)
                {
                    continue; // Only manage positions for the cBot's symbol
                }

                // --- Update G (Max Profit) and D (Max Drawdown) ---
                _maxProfits.TryGetValue(position.Id, out double maxProfit);
                _maxDrawdowns.TryGetValue(position.Id, out double maxDrawdown);
                
                double currentProfit = position.NetProfit;

                // Update max profit (G) if current profit is higher
                if (currentProfit > maxProfit)
                {
                    maxProfit = currentProfit;
                    _maxProfits[position.Id] = maxProfit; // Store new peak
                }
                
                // Update max drawdown (D) if current profit is lower (more negative)
                if (currentProfit < maxDrawdown)
                {
                    maxDrawdown = currentProfit;
                    _maxDrawdowns[position.Id] = maxDrawdown; // Store new low
                }

                // --- Rule 1: Max Loss Close (Hard Stop) ---
                // Check if the position has a loss and if that loss exceeds the threshold
                if (currentProfit < 0 && Math.Abs(currentProfit) > MaxLossAmount)
                {
                    ClosePosition(position);
                    continue; // Position is closed, process next position
                }

                // --- Rule 2: Profit Protection (Trailing) ---
                if (UseProfitProtection)
                {
                    // Check profit protection trigger:
                    // 1. Max profit (G) must be greater than the MaxLossAmount threshold.
                    // 2. Current profit must have dropped to 50% (or less) of the max profit (G).
                    if ((maxProfit > MaxLossAmount) && (currentProfit <= (0.5 * maxProfit)))
                    {
                        ClosePosition(position);
                        continue; // Position is closed, process next position
                    }
                }
                
                // --- Rule 3: Drawdown Close (New) ---
                // If D (absolute value of max drawdown) > G (max profit) AND position is currently in profit
                if (UseDrawdownCloseRule)
                {
                    // maxDrawdown is negative (e.g., -5), maxProfit is positive (e.g., 3)
                    // We check if Abs(-5) > 3, and if current profit is positive.
                    if ((currentProfit > 0) && (Math.Abs(maxDrawdown) > maxProfit) && (Math.Min(Math.Abs(maxDrawdown), maxProfit) > 0.25 * MaxLossAmount))
                    {
                        ClosePosition(position);
                        continue; // Position is closed, process next position
                    }
                }
                
                // --- Rule 4: Set initial Stop Loss (Original Rule 2) ---
                // If position is still open and has no SL, set the emergency SL.
                if (position.StopLoss == null)
                {
                    // Check required symbol properties before calculation
                    if (Symbol.PipValue == 0 || Symbol.LotSize == 0)
                    {
                        Print("Warning: PipValue or LotSize is zero. Cannot calculate currency-based SL for Position {0}.", position.Id);
                        continue; // Skip SL calculation for this tick/position
                    }
                    
                    // Convert position volume from units to lots
                    double positionLots = Symbol.VolumeInUnitsToQuantity(position.VolumeInUnits);

                    // Calculate the value of 1 pip for this specific position's quantity
                    double positionPipValue = Symbol.PipValue * positionLots;
                    
                    if (positionPipValue == 0) continue; // Avoid division by zero

                    // Calculate the required Stop Loss distance in Pips
                    double targetLossAmount = 3 * MaxLossAmount;
                    double slPips = targetLossAmount / positionPipValue;

                    double slPrice;
                    if (position.TradeType == TradeType.Buy)
                    {
                        slPrice = position.EntryPrice - (slPips * Symbol.PipSize);
                    }
                    else // Sell
                    {
                        slPrice = position.EntryPrice + (slPips * Symbol.PipSize);
                    }
                    
                    slPrice = Math.Round(slPrice, Symbol.Digits);

                    ModifyPosition(position, slPrice, position.TakeProfit, ProtectionType.Absolute);
                }
            }
        }

        private void OnPositionClosed(PositionClosedEventArgs args)
        {
            // Remove the position from tracking when it's closed
            _maxProfits.Remove(args.Position.Id);
            _maxDrawdowns.Remove(args.Position.Id);
        }

        protected override void OnStop()
        {
            // Unsubscribe from events on cBot stop
            Positions.Closed -= OnPositionClosed;
        }
    }
}