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 HmaMitigationSignals : Indicator
    {
        [Parameter("HMA Period", DefaultValue = 20, MinValue = 2)]
        public int HmaPeriod { get; set; }

        // Use the chart's TimeZone for this input
        [Parameter("Session Close Time (Chart TZ)", DefaultValue = "17:30")]
        public TimeSpan SessionCloseTime { get; set; }

        [Parameter("Enable Alerts", DefaultValue = true)]
        public bool EnableAlerts { get; set; }

        [Parameter("Alert Sound Path", DefaultValue = "")]
        public string AlertSoundPath { get; set; }

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

        private HullMovingAverage _hma;
        private Stack<double> _peakStack;
        private Stack<double> _troughStack;

        private DateTime _currentSessionEndTimeUtc = DateTime.MinValue;

        // State flags for entry setup
        private bool _buySetupActive = false;
        private bool _sellSetupActive = false;

        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)
            {
                // Convert the current UTC bar time to the chart's local time zone
                DateTime barOpenTimeChartTz = TimeZoneInfo.ConvertTimeFromUtc(barOpenTime, this.TimeZone);
                
                // Create the session close time for "today" *in the chart's timezone*
                DateTime sessionCloseChartTz = barOpenTimeChartTz.Date.Add(SessionCloseTime);
                
                // Convert this chart-timezone-based closing time back to UTC
                DateTime sessionCloseTodayUtc = TimeZoneInfo.ConvertTimeToUtc( sessionCloseChartTz, this.TimeZone);

                if (barOpenTime >= sessionCloseTodayUtc)
                {
                    // Bar is after today's close. Session end is tomorrow (chart time).
                    // Add 1 day *in chart time* (handles DST)
                    DateTime nextSessionEndChartTz = sessionCloseChartTz.AddDays(1);
                    // Convert back to UTC
                    _currentSessionEndTimeUtc = TimeZoneInfo.ConvertTime(nextSessionEndChartTz, this.TimeZone, TimeZoneInfo.Utc);
                }
                else
                {
                    // Bar is before today's close. Session end is today (chart time).
                    _currentSessionEndTimeUtc = sessionCloseTodayUtc;
                }
            }

            // --- Session Check ---
            // Check if the current bar has crossed the session boundary
            if (barOpenTime >= _currentSessionEndTimeUtc)
            {
                // New session starts, clear stacks and drawings
                _peakStack.Clear();
                _troughStack.Clear();
                //Chart.RemoveAllObjects(); // Clear old session text and icons
                
                // Reset setup flags
                _buySetupActive = false;
                _sellSetupActive = false;

                // Set the *next* session end time, looping in case of market gaps (weekends)
                while (barOpenTime >= _currentSessionEndTimeUtc)
                {
                    // Convert current UTC end time to chart time
                    DateTime currentSessionEndChartTz = TimeZoneInfo.ConvertTimeFromUtc(_currentSessionEndTimeUtc, this.TimeZone);
                    // Add 1 day *in chart time* (handles DST)
                    DateTime nextSessionEndChartTz = currentSessionEndChartTz.AddDays(1);
                    // Convert back to UTC for the next comparison
                    _currentSessionEndTimeUtc = TimeZoneInfo.ConvertTime(nextSessionEndChartTz, this.TimeZone, TimeZoneInfo.Utc);
                }
            }

            // Stop signal logic from running on the forming bar (which causes repainting)
            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 for entry ---
            // 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)
            {
                // Check if a sell setup was active *before* pushing the new peak
                if (_sellSetupActive)
                {
                    // Draw sell icon at the peak (index - 1)
                    Chart.DrawIcon($"sell_{index - 1}", ChartIconType.DownArrow, index - 1, Bars.HighPrices[index - 1] + Symbol.PipSize * 10, Color.Red);
                    _sellSetupActive = false; // Reset setup
                }
                
                _peakStack.Push(prevHma);
                // Draw count above the peak bar
                string peakLabel = $"{_peakStack.Count}";
                double yPos = Bars.HighPrices[index - 1] + Symbol.PipSize * 5;
                Chart.DrawText($"peak_{index - 1}", peakLabel, index - 1, yPos, Color.Red);
            }

            if (isTrough)
            {
                // Check if a buy setup was active *before* pushing the new trough
                if (_buySetupActive)
                {
                    // Draw buy icon at the trough (index - 1)
                    Chart.DrawIcon($"buy_{index - 1}", ChartIconType.UpArrow, index - 1, Bars.LowPrices[index - 1] - Symbol.PipSize * 10, Color.Lime);
                    _buySetupActive = false; // Reset setup
                }
                
                _troughStack.Push(prevHma);
                // Draw count below the trough bar
                string troughLabel = $"{_troughStack.Count}";
                double yPos = Bars.LowPrices[index - 1] - Symbol.PipSize * 5;
                Chart.DrawText($"trough_{index - 1}", troughLabel, index - 1, yPos, Color.Lime);
            }

            // --- 2. Check for mitigation (Setup) based on the hma value ---
            // Mitigation is checked against the closed bar 'currentHma' (at index).
            
            // Check peak mitigation (testing highs)
            bool peakMitigationTriggeredSetup = false;
            while (_peakStack.Count > 0 && currentHma >= _peakStack.Peek())
            {
                _peakStack.Pop();
                if (_peakStack.Count <= 1)
                {
                    peakMitigationTriggeredSetup = true;
                }
            }
            if (peakMitigationTriggeredSetup)
            {
                // Alert only on the first bar the setup becomes active
                if (EnableAlerts && !_sellSetupActive && !string.IsNullOrEmpty(AlertSoundPath))
                {
                    Notifications.PlaySound(AlertSoundPath);
                }
                _sellSetupActive = true; // Activate sell setup
            }

            // Check trough mitigation (testing lows)
            bool troughMitigationTriggeredSetup = false;
            while (_troughStack.Count > 0 && currentHma <= _troughStack.Peek())
            {
                _troughStack.Pop();
                if (_troughStack.Count <= 1)
                {
                    troughMitigationTriggeredSetup = true;
                }
            }
            if (troughMitigationTriggeredSetup)
            {
                // Alert only on the first bar the setup becomes active
                if (EnableAlerts && !_buySetupActive && !string.IsNullOrEmpty(AlertSoundPath))
                {
                    Notifications.PlaySound(AlertSoundPath);
                }
                _buySetupActive = true; // Activate buy setup
            }
        }
    }
}