using System;
using System.Linq;
using cAlgo.API;
using cAlgo.API.Internals;

namespace cAlgo.Robots
{
    [Robot(TimeZone = TimeZones.UTC, AccessRights = AccessRights.None)]
    public class RoundNumberBreakout : Robot
    {
        #region Parameters

        [Parameter("Order Volume (Lots)", DefaultValue = 1.0)]
        public double VolumeInLots { get; set; }

        [Parameter("Round Number Step (n)", DefaultValue = 50, MinValue = 0.00001)]
        public double RoundNumberStep { get; set; }

        [Parameter("SL Factor", DefaultValue = 1.0)]
        public double SlFactor { get; set; }

        [Parameter("TP Factor", DefaultValue = 2.0)]
        public double TpFactor { get; set; }

        [Parameter("Label", DefaultValue = "RN_Breakout")]
        public string Label { get; set; }

        [Parameter("Max Lookback Bars", DefaultValue = 200, Group = "Optimization")]
        public int MaxLookback { get; set; }

        #endregion

        #region Fields

        private double _volumeInUnits;

        #endregion

        protected override void OnStart()
        {
            _volumeInUnits = Symbol.QuantityToVolumeInUnits(VolumeInLots);
        }

        protected override void OnBarClosed()
        {
            double closeCurrent = Bars.ClosePrices.Last(0);
            double closePrev = Bars.ClosePrices.Last(1);

            // --- BUY LOGIC ---
            double potentialLowerRn = Math.Floor(closeCurrent / RoundNumberStep) * RoundNumberStep;
            
            if (closeCurrent > potentialLowerRn && closePrev < potentialLowerRn)
            {
                // 1. Check duplicate RN
                // 2. Check virtual history
                if (!IsSameAsLastEntry(TradeType.Buy, potentialLowerRn) && 
                    WasLastVirtualTradeSuccessful(TradeType.Buy, potentialLowerRn))
                {
                    CancelPendingOrders();
                    PlaceTrade(TradeType.Buy, potentialLowerRn);
                }
            }

            // --- SELL LOGIC ---
            double potentialUpperRn = Math.Ceiling(closeCurrent / RoundNumberStep) * RoundNumberStep;
            
            if (closeCurrent < potentialUpperRn && closePrev > potentialUpperRn)
            {
                // 1. Check duplicate RN
                // 2. Check virtual history
                if (!IsSameAsLastEntry(TradeType.Sell, potentialUpperRn) && 
                    WasLastVirtualTradeSuccessful(TradeType.Sell, potentialUpperRn))
                {
                    CancelPendingOrders();
                    PlaceTrade(TradeType.Sell, potentialUpperRn);
                }
            }
        }

        private void PlaceTrade(TradeType type, double entryPrice)
        {
            double slPrice, tpPrice;
            CalculateLevels(type, entryPrice, out slPrice, out tpPrice);

            entryPrice = Math.Round(entryPrice, Symbol.Digits);
            
            PlaceLimitOrder(type, SymbolName, _volumeInUnits, entryPrice, Label, slPrice, tpPrice, ProtectionType.Absolute);
        }

        /// <summary>
        /// Checks if the last executed trade in the given direction was opened at the same RN level.
        /// </summary>
        private bool IsSameAsLastEntry(TradeType type, double currentRn)
        {
            // We look at actual History to see what we really traded last
            var lastTrade = History
                .Where(t => t.Label == Label && t.SymbolName == SymbolName && t.TradeType == type)
                .OrderByDescending(t => t.EntryTime) // Important: Order by EntryTime, not ClosingTime
                .FirstOrDefault();

            if (lastTrade == null)
                return false;

            // Use a small epsilon for double comparison (TickSize / 2)
            double epsilon = Symbol.TickSize / 2;
            return Math.Abs(lastTrade.EntryPrice - currentRn) < epsilon;
        }

        /// <summary>
        /// Simulates the outcome of the PREVIOUS signal of the same type.
        /// </summary>
        private bool WasLastVirtualTradeSuccessful(TradeType type, double currentRnLevel)
        {
            int startIndex = 2; 
            int foundSignalIndex = -1;
            double foundRnLevel = 0;

            // 1. Find the previous signal backwards
            for (int i = startIndex; i < Math.Min(Bars.Count, MaxLookback); i++)
            {
                double c = Bars.ClosePrices.Last(i);
                double cPrev = Bars.ClosePrices.Last(i + 1);

                if (type == TradeType.Buy)
                {
                    double rn = Math.Floor(c / RoundNumberStep) * RoundNumberStep;
                    if (c > rn && cPrev < rn)
                    {
                        foundSignalIndex = i;
                        foundRnLevel = rn;
                        break;
                    }
                }
                else // Sell
                {
                    double rn = Math.Ceiling(c / RoundNumberStep) * RoundNumberStep;
                    if (c < rn && cPrev > rn)
                    {
                        foundSignalIndex = i;
                        foundRnLevel = rn;
                        break;
                    }
                }
            }

            if (foundSignalIndex == -1) return true;

            // 2. Simulate forward
            double sl, tp;
            CalculateLevels(type, foundRnLevel, out sl, out tp);
            
            bool isFilled = false;

            for (int j = foundSignalIndex - 1; j >= 0; j--)
            {
                double high = Bars.HighPrices.Last(j);
                double low = Bars.LowPrices.Last(j);

                if (!isFilled)
                {
                    if (type == TradeType.Buy) { if (low <= foundRnLevel) isFilled = true; }
                    else { if (high >= foundRnLevel) isFilled = true; }
                }

                if (isFilled)
                {
                    if (type == TradeType.Buy)
                    {
                        if (low <= sl) return false; // LOSS
                        if (high >= tp) return true; // WIN
                    }
                    else
                    {
                        if (high >= sl) return false; // LOSS
                        if (low <= tp) return true; // WIN
                    }
                }
            }

            return true;
        }

        private void CalculateLevels(TradeType type, double entryPrice, out double sl, out double tp)
        {
            double riskAmount = RoundNumberStep * SlFactor;
            double rewardAmount = RoundNumberStep * TpFactor;

            if (type == TradeType.Buy)
            {
                sl = Math.Round(entryPrice - riskAmount, Symbol.Digits);
                tp = Math.Round(entryPrice + rewardAmount, Symbol.Digits);
            }
            else
            {
                sl = Math.Round(entryPrice + riskAmount, Symbol.Digits);
                tp = Math.Round(entryPrice - rewardAmount, Symbol.Digits);
            }
        }

        private void CancelPendingOrders()
        {
            var ordersToCancel = PendingOrders.Where(o => o.Label == Label && o.SymbolName == SymbolName);
            foreach (var order in ordersToCancel)
            {
                CancelPendingOrder(order);
            }
        }
    }
}