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

namespace cAlgo.Indicators
{
    [Indicator(IsOverlay = true, TimeZone = TimeZones.UTC, AccessRights = AccessRights.None)]
    public class HmaMitigationCount : Indicator
    {
        [Parameter("HMA Period", DefaultValue = 20, MinValue = 2)]
        public int HmaPeriod { get; set; }

        [Parameter("Session Close Time (UTC)", DefaultValue = "17:30")]
        public TimeSpan SessionCloseTimeUtc { get; set; }

        [Output("HMA", LineColor = "Orange")]
        public IndicatorDataSeries HmaOutput { get; set; }

        private HullMovingAverage _hma;
        private Stack<double> _peakStack;
        private Stack<double> _troughStack;
        private int lblCnt = 0;
        private int mitigatedPeaks = 0;
        private int mitigatedTroughs = 0;
        private int dir = 0;

        private DateTime _currentSessionEndTimeUtc = DateTime.MinValue;

        protected override void Initialize()
        {
            _hma = Indicators.HullMovingAverage(Bars.ClosePrices, HmaPeriod);
            _peakStack = new Stack<double>();
            _troughStack = new Stack<double>();
        }

        public override void Calculate(int index)
        {
            HmaOutput[index] = _hma.Result[index];
            DateTime barOpenTime = Bars.OpenTimes[index]; // This is UTC

            // --- Session Initialization (run once) ---
            if (_currentSessionEndTimeUtc == DateTime.MinValue)
            {
                // Find the session close time for the *current* bar's day
                DateTime sessionCloseToday = barOpenTime.Date.Add(SessionCloseTimeUtc);
                
                // If the bar is already *after* today's close, the session ends *tomorrow*
                if (barOpenTime >= sessionCloseToday)
                {
                    _currentSessionEndTimeUtc = sessionCloseToday.AddDays(1);
                }
                // If the bar is *before* today's close, the session ends *today*
                else
                {
                    _currentSessionEndTimeUtc = sessionCloseToday;
                }
            }

            // --- Session Check ---
            // Check if the current bar has crossed the session boundary
            if (barOpenTime >= _currentSessionEndTimeUtc)
            {
                // New session starts, clear stacks
                _peakStack.Clear();
                _troughStack.Clear();
                //Chart.RemoveAllObjects(); // Optional: Clear old session text
                
                // Set the *next* session end time, looping in case of market gaps (weekends)
                while (barOpenTime >= _currentSessionEndTimeUtc)
                {
                    _currentSessionEndTimeUtc = _currentSessionEndTimeUtc.AddDays(1);
                }
            }

            // Stop logic from running on the forming bar
            if (IsLastBar)
            {
                return;
            }

            // We need at least 3 bars to identify a peak/trough at index - 1
            if (index < 2)
            {
                return;
            }

            // --- 1. Identify new peaks/troughs at index - 1 AND check mitigation ---
            // All HMA values are now based on closed bars (index, index-1, index-2).
            double prevHma = _hma.Result[index - 1];
            double prevHma2 = _hma.Result[index - 2];
            double currentHma = _hma.Result[index];

            // A peak at index-1
            bool isPeak = (prevHma > prevHma2) && (prevHma > currentHma);
            // A trough at index-1
            bool isTrough = (prevHma < prevHma2) && (prevHma < currentHma);

            if (isPeak)
            {
                // New Peak: Check if it mitigates any *previous Peaks*
                if( dir < 0 )
                {
                    mitigatedPeaks = 0;
                    lblCnt++;
                }

                // A new peak (prevHma) mitigates old peaks that are *lower*
                while (_peakStack.Count > 0 && prevHma >= _peakStack.Peek())
                {
                    _peakStack.Pop(); // Pop the mitigated (lower) peak
                    mitigatedPeaks++;
                }

                if (mitigatedPeaks > 0)
                {
                    // Draw mitigation count above the peak bar
                    // The remaining count is the peaks *higher* than the new one
                    string label = $"Mit: {mitigatedPeaks} / Rem: {_peakStack.Count}"; 
                    double yPos = Bars.HighPrices[index - 1] + Symbol.PipSize * 5;
                    Chart.DrawText($"peak_mit_{lblCnt}", label, index - 1, yPos, Color.Red);
                    dir = 1;
                }
                
                // Add the new peak to its own stack
                _peakStack.Push(prevHma);
            }

            if (isTrough)
            {
                // New Trough: Check if it mitigates any *previous Troughs*
                if( dir > 0 )
                {
                    mitigatedTroughs = 0;
                    lblCnt++;
                }

                // A new trough (prevHma) mitigates old troughs that are *higher*
                while (_troughStack.Count > 0 && prevHma <= _troughStack.Peek())
                {
                    _troughStack.Pop(); // Pop the mitigated (higher) trough
                    mitigatedTroughs++;
                }

                if (mitigatedTroughs > 0)
                {
                    // Draw mitigation count below the trough bar
                    // The remaining count is the troughs *lower* than the new one
                    string label = $"Mit: {mitigatedTroughs} / Rem: {_troughStack.Count}";
                    double yPos = Bars.LowPrices[index - 1] - Symbol.PipSize * 5;
                    Chart.DrawText($"trough_mit_{lblCnt}", label, index - 1, yPos, Color.Lime);
                    dir = -1;
                }
                
                // Add the new trough to its own stack
                _troughStack.Push(prevHma);
            }
        }
    }
}