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

namespace cAlgo.Robots
{
    [Robot(TimeZone = TimeZones.UTC, AccessRights = AccessRights.None)]
    public class HmaStructureBot : Robot
    {
        // --- Parameters ---
        [Parameter("HMA Period", DefaultValue = 25)]
        public int HmaPeriod { get; set; }

        [Parameter("Risk % per Trade", DefaultValue = 1.0, MinValue = 0.01)]
        public double RiskPercent { get; set; }

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

        // Expiration removed

        // --- Indicators ---
        private HullMovingAverage _hma;

        // --- State Management ---
        private DateTime _lastSetupTime;

        // --- Data Structures ---
        private enum ExtremumType { High, Low }

        private struct Extremum
        {
            public ExtremumType Type;
            public double Value;
            public int Index; // Index relative to logic
            public DateTime Time; // Unique identifier for the extremum
        }

        protected override void OnStart()
        {
            _hma = Indicators.HullMovingAverage(Bars.ClosePrices, HmaPeriod);
        }

        protected override void OnBarClosed()
        {
            // 1. Manage existing pending orders
            ManageExistingOrders();

            // 2. Find Extrema
            var extrema = GetExtrema(200, 5);

            if (extrema.Count < 5)
                return;

            // 3. Check for duplicate execution on the same setup
            // The setup is defined by the latest extremum (extrema[0])
            if (extrema[0].Time == _lastSetupTime)
                return;

            bool orderPlaced = false;

            // --- Short Logic ---
            // Last extremum must be a Trough (T0)
            if (extrema[0].Type == ExtremumType.Low)
            {
                var t0 = extrema[0].Value;
                var h0 = extrema[1].Value; 
                var t1 = extrema[2].Value;
                var h1 = extrema[3].Value;
                var t2 = extrema[4].Value;

                // Condition: T1 < T2 and H0 < T2
                if (t1 < t2 && h0 < t2)
                {
                    double entryPrice = 0.5 * (t2 + h0);
                    double stopLossPrice = h1;
                    double takeProfitPrice = Math.Max(t0, t1);

                    PlaceStructureOrder(TradeType.Sell, entryPrice, stopLossPrice, takeProfitPrice);
                    orderPlaced = true;
                }
            }
            // --- Long Logic ---
            // Last extremum must be a Peak (H0)
            else if (extrema[0].Type == ExtremumType.High)
            {
                var h0 = extrema[0].Value;
                var t0 = extrema[1].Value; 
                var h1 = extrema[2].Value;
                var t1 = extrema[3].Value;
                var h2 = extrema[4].Value;

                // Condition: H1 > H2 and T0 > H2
                if (h1 > h2 && t0 > h2)
                {
                    double entryPrice = 0.5 * (h2 + t0);
                    double stopLossPrice = t1;
                    double takeProfitPrice = Math.Min(h0, h1);

                    PlaceStructureOrder(TradeType.Buy, entryPrice, stopLossPrice, takeProfitPrice);
                    orderPlaced = true;
                }
            }

            // 4. Mark Setup as handled if we placed an order
            if (orderPlaced)
            {
                _lastSetupTime = extrema[0].Time;
            }
        }

        private void PlaceStructureOrder(TradeType type, double entryPrice, double slPrice, double tpPrice)
        {
            // Calculate Risk
            double riskAmount = Account.Balance * (RiskPercent / 100.0);
            
            // Note: VolumeForFixedRisk still requires SL in Pips calculation
            double slDistancePips = Math.Abs(entryPrice - slPrice) / Symbol.PipSize;

            if (slDistancePips <= 0) return;

            double volume = Symbol.VolumeForFixedRisk(riskAmount, slDistancePips);
            volume = Symbol.NormalizeVolumeInUnits(volume, RoundingMode.Down);

            // Place Limit Order using Absolute Prices
            // Expiry is null (GTC - Good Till Cancelled)
            PlaceLimitOrder(type, SymbolName, volume, entryPrice, OrderLabel, slPrice, tpPrice, ProtectionType.Absolute, null);
        }

        private void ManageExistingOrders()
        {
            foreach (var order in PendingOrders.ToArray())
            {
                if (order.Label != OrderLabel || order.SymbolName != SymbolName)
                    continue;

                var barLow = Bars.LowPrices.Last(0);
                var barHigh = Bars.HighPrices.Last(0);
                bool isCancelled = false;

                // --- Check SL Hit ---
                if (order.StopLoss.HasValue)
                {
                    if (order.TradeType == TradeType.Buy)
                    {
                        if (barLow <= order.StopLoss.Value)
                        {
                            order.Cancel();
                            isCancelled = true;
                        }
                    }
                    else // Sell
                    {
                        if (barHigh >= order.StopLoss.Value)
                        {
                            order.Cancel();
                            isCancelled = true;
                        }
                    }
                }

                if (isCancelled) continue;

                // --- Check TP Extension ---
                if (order.TakeProfit.HasValue)
                {
                    if (order.TradeType == TradeType.Sell)
                    {
                        // If price went lower than TP, move TP to Low
                        if (barLow < order.TakeProfit.Value)
                        {
                            ModifyPendingOrder(order, order.TargetPrice, order.StopLoss, barLow, order.ProtectionType);
                        }
                    }
                    else // Buy
                    {
                        // If price went higher than TP, move TP to High
                        if (barHigh > order.TakeProfit.Value)
                        {
                            ModifyPendingOrder(order, order.TargetPrice, order.StopLoss, barHigh, order.ProtectionType);
                        }
                    }
                }
            }
        }

        private List<Extremum> GetExtrema(int searchDepth, int minCount)
        {
            var list = new List<Extremum>();
            var hma = _hma.Result;

            // i starts at 1 to compare with confirmed i+1 and i-1 (where i-1 is closer to closed bar Last(0))
            for (int i = 1; i < searchDepth && i < hma.Count - 1; i++)
            {
                double current = hma.Last(i);
                double prev = hma.Last(i + 1); // Older
                double next = hma.Last(i - 1); // Newer

                if (current > prev && current > next)
                {
                    list.Add(new Extremum { 
                        Type = ExtremumType.High, 
                        Value = current, 
                        Index = i,
                        Time = Bars.OpenTimes.Last(i) 
                    });
                }
                else if (current < prev && current < next)
                {
                    list.Add(new Extremum { 
                        Type = ExtremumType.Low, 
                        Value = current, 
                        Index = i,
                        Time = Bars.OpenTimes.Last(i) 
                    });
                }

                if (list.Count >= minCount)
                    break;
            }
            return list;
        }
    }
}