using cAlgo.API;
using cAlgo.API.Indicators;
using System;

namespace cAlgo.Robots
{
    [Robot(TimeZone = TimeZones.UTC, AccessRights = AccessRights.None)]
    public class HmaSmaCrossoverBot : Robot
    {
        [Parameter("HMA A Period (Fast)", DefaultValue = 20, Group = "Periods")]
        public int HmaAPeriod { get; set; }

        [Parameter("HMA B Period (Slow)", DefaultValue = 250, Group = "Periods")]
        public int HmaBPeriod { get; set; }

        [Parameter("SMA C Period (Base)", DefaultValue = 200, Group = "Periods")]
        public int SmaCPeriod { get; set; }

        [Parameter("Lot Size", DefaultValue = 0.1, Group = "Volume")]
        public double LotSize { get; set; }

        [Parameter("Max Positions", DefaultValue = 4, Group = "Volume")]
        public int MaxPositions { get; set; }

        [Parameter("Stop Loss (Pips)", DefaultValue = 50, Group = "Protection")]
        public double StopLossPips { get; set; }

        // Label for position management
        private const string BotLabel = "HmaSmaCrossoverBot";

        private HullMovingAverage _hmaA;
        private HullMovingAverage _hmaB;
        private SimpleMovingAverage _smaC;
        private double _volumeInUnits;
        private TimeZoneInfo _germanZone; // Added for time filter

        protected override void OnStart()
        {
            _hmaA = Indicators.HullMovingAverage(Bars.ClosePrices, HmaAPeriod);
            _hmaB = Indicators.HullMovingAverage(Bars.ClosePrices, HmaBPeriod);
            _smaC = Indicators.SimpleMovingAverage(Bars.ClosePrices, SmaCPeriod);

            // Convert LotSize to normalized VolumeInUnits
            double preciseVolume = Symbol.QuantityToVolumeInUnits(LotSize);
            _volumeInUnits = Symbol.NormalizeVolumeInUnits(preciseVolume, RoundingMode.ToNearest);

            if (_volumeInUnits < Symbol.VolumeInUnitsMin)
            {
                _volumeInUnits = Symbol.VolumeInUnitsMin;
                Print($"Warning: Requested volume {preciseVolume} is below minimum. Using minimum: {_volumeInUnits} units.");
            }

            // Initialize German TimeZone
            try
            {
                // "W. Europe Standard Time" is the ID for Berlin, Amsterdam, Rome (CET/CEST)
                _germanZone = TimeZoneInfo.FindSystemTimeZoneById("W. Europe Standard Time");
            }
            catch (TimeZoneNotFoundException)
            {
                Print("Error: TimeZone 'W. Europe Standard Time' not found. Using Server TimeZone as fallback for time filter (might be incorrect).");
                _germanZone = TimeZone; // Fallback to server zone (which is UTC here)
            }
            catch (Exception ex)
            {
                Print($"Error initializing TimeZone: {ex.Message}. Using Server TimeZone as fallback.");
                _germanZone = TimeZone;
            }
        }

        protected override void OnBar()
        {
            // Ensure indicators have enough data + 3 bars for peak/trough logic
            int requiredBars = Math.Max(HmaAPeriod, Math.Max(HmaBPeriod, SmaCPeriod)) + 3;
            if (Bars.Count < requiredBars)
            {
                return;
            }

            // Check exit conditions first
            HandleExits();

            // Check entry conditions
            HandleEntries();
        }

        // Checks and executes exit logic based on rules
        private void HandleExits()
        {
            // Get current and previous values (based on closed bars)
            // Last(0) = current closed bar, Last(1) = previous bar
            double hmaB_curr = _hmaB.Result.Last(0);
            double smaC_curr = _smaC.Result.Last(0);
            double hmaB_prev = _hmaB.Result.Last(1);

            double low_curr = Bars.LowPrices.Last(0);
            double low_prev = Bars.LowPrices.Last(1);
            double high_curr = Bars.HighPrices.Last(0);
            double high_prev = Bars.HighPrices.Last(1);

            // --- Long Exit Conditions ---
            // 1. B < C (at current close)
            // 2. Low crosses below B (Currentbar.Low < Current.B AND PreviousBar.Low >= Previous.B)
            bool longExitSignal = (hmaB_curr < smaC_curr) ||
                                  ((low_curr < hmaB_curr) && (low_prev >= hmaB_prev));

            // --- Short Exit Conditions ---
            // 1. B > C (at current close)
            // 2. High crosses above B (Currentbar.High > Current.B AND PreviousBar.High <= Previous.B)
            bool shortExitSignal = (hmaB_curr > smaC_curr) ||
                                   ((high_curr > hmaB_curr) && (high_prev <= hmaB_prev));

            // Find all positions managed by this bot for the current symbol
            var botPositions = GetPositions(null);
            
            foreach (var position in botPositions)
            {
                if (position.TradeType == TradeType.Buy && longExitSignal)
                {
                    ClosePosition(position);
                }
                else if (position.TradeType == TradeType.Sell && shortExitSignal)
                {
                    ClosePosition(position);
                }
            }
        }

        // Checks and executes entry logic based on indicator signals
        private void HandleEntries()
        {
            // --- Time Filter ---
            // Use TimeInUtc for a reliable UTC baseline (as Robot TimeZone is UTC)
            DateTime germanTime = TimeZoneInfo.ConvertTimeFromUtc(TimeInUtc, _germanZone);
            
            // Only allow new trades between 7:00 (inclusive) and 17:00 (exclusive) German time
            if (germanTime.Hour < 7 || germanTime.Hour >= 17)
            {
                return; // Outside trading hours
            }
            // --- End Time Filter ---

            // Get values for signal bar (i-1) and confirmation bar (i)
            // Last(0) = i (confirmation bar, just closed)
            // Last(1) = i-1 (signal bar)
            // Last(2) = i-2 (previous bar)
            
            double hmaA_confirm = _hmaA.Result.Last(0);
            double hmaA_signal = _hmaA.Result.Last(1);
            double hmaA_prev = _hmaA.Result.Last(2);

            // Get filter values at the signal bar (i-1)
            double hmaB_signal = _hmaB.Result.Last(1);
            double smaC_signal = _smaC.Result.Last(1);
            
            // Peak/Trough detection at i-1 (Last(1)), confirmed by i (Last(0))
            bool isPeak = (hmaA_prev < hmaA_signal) && (hmaA_signal > hmaA_confirm);
            bool isTrough = (hmaA_prev > hmaA_signal) && (hmaA_signal < hmaA_confirm);

            // --- Sell Signal Condition (at i-1) ---
            // 1. B < C
            // 2. A > C
            // 3. A forms a peak
            if ((hmaB_signal < smaC_signal) && (hmaA_signal > smaC_signal) && isPeak)
            {
                if (GetPositions(TradeType.Sell).Length < MaxPositions)
                {
                    ExecuteMarketOrder(TradeType.Sell, SymbolName, _volumeInUnits, BotLabel, StopLossPips, null);
                }
            }

            // --- Buy Signal Condition (at i-1) ---
            // 1. B > C
            // 2. A < C
            // 3. A forms a trough
            if ((hmaB_signal > smaC_signal) && (hmaA_signal < smaC_signal) && isTrough)
            {
                if (GetPositions(TradeType.Buy).Length < MaxPositions)
                {
                    ExecuteMarketOrder(TradeType.Buy, SymbolName, _volumeInUnits, BotLabel, StopLossPips, null);
                }
            }
        }
        
        // Helper to get positions only for the current symbol and managed by this bot
        private Position[] GetPositions(TradeType? tradeType)
        {
            if (tradeType == null)
            {
                // Get all Buy or Sell positions for this bot/symbol
                return Positions.FindAll(BotLabel, SymbolName);
            }
            
            // Get specific TradeType positions for this bot/symbol
            return Positions.FindAll(BotLabel, SymbolName, tradeType.Value);
        }
    }
}