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

namespace cAlgo
{
    [Indicator(IsOverlay = true, TimeZone = TimeZones.UTC, AccessRights = AccessRights.None)]
    public class GTSClassroomChartTutor : Indicator
    {
        [Parameter("Swing strength", DefaultValue = 2, MinValue = 1, MaxValue = 10, Group = "Learning")]
        public int SwingStrength { get; set; }

        [Parameter("Analysis lookback", DefaultValue = 120, MinValue = 30, MaxValue = 500, Group = "Learning")]
        public int Lookback { get; set; }

        [Parameter("Show structure labels", DefaultValue = true, Group = "Learning")]
        public bool ShowStructureLabels { get; set; }

        [Parameter("Show forming reminder", DefaultValue = true, Group = "Learning")]
        public bool ShowFormingReminder { get; set; }

        [Parameter("Questions every N closed candles", DefaultValue = 1, MinValue = 1, MaxValue = 20, Group = "Learning")]
        public int QuestionInterval { get; set; }

        private StackPanel _panel;
        private TextBlock _status;
        private TextBlock _question;
        private TextBlock _feedback;
        private Button[] _answers;
        private Question _active;
        private DateTime _lastProcessedOpenTime;
        private int _closedBarsSinceQuestion;

        protected override void Initialize()
        {
            BuildPanel();
        }

        public override void Calculate(int index)
        {
            if (index != Bars.Count - 1 || Bars.Count < Math.Max(20, SwingStrength * 2 + 8))
                return;

            if (ShowFormingReminder)
                _status.Text = "FORMING CANDLE — wait for close before grading";

            DateTime currentOpenTime = Bars.OpenTimes[index];
            if (_lastProcessedOpenTime == currentOpenTime)
                return;

            _lastProcessedOpenTime = currentOpenTime;
            int closedIndex = index - 1;
            _closedBarsSinceQuestion++;
            StructureSnapshot structure = AnalyseStructure(closedIndex);
            if (ShowStructureLabels)
                DrawStructure(structure);

            if (_closedBarsSinceQuestion < QuestionInterval)
                return;
            _closedBarsSinceQuestion = 0;
            _active = BuildQuestion(closedIndex, structure);
            RenderQuestion();
        }

        private void BuildPanel()
        {
            _status = Text("Waiting for the next candle close…", "#38BDF8", 11);
            _question = Text("GTS CLASSROOM CHART TUTOR", "#E8EAED", 14);
            _question.FontWeight = FontWeight.Bold;
            _feedback = Text("Educational use only. This indicator never places or modifies trades.", "#B7BEC9", 11);

            var choices = new StackPanel { Orientation = Orientation.Vertical };
            _answers = new Button[4];
            for (int i = 0; i < 4; i++)
            {
                int answerIndex = i;
                _answers[i] = new Button
                {
                    Text = "—",
                    Height = 32,
                    Margin = new Thickness(0, 3, 0, 0),
                    BackgroundColor = Color.FromHex("#1C2330"),
                    ForegroundColor = Color.FromHex("#E8EAED")
                };
                _answers[i].Click += args => Grade(answerIndex);
                choices.AddChild(_answers[i]);
            }

            _panel = new StackPanel
            {
                Orientation = Orientation.Vertical,
                Width = 330,
                Margin = 12,
                BackgroundColor = Color.FromHex("#161B22"),
                Opacity = 0.96,
                VerticalAlignment = VerticalAlignment.Top,
                HorizontalAlignment = HorizontalAlignment.Right
            };
            var title = Text("GTS CLASSROOM  ·  CHART TUTOR", "#38BDF8", 12);
            title.FontWeight = FontWeight.Bold;
            _panel.AddChild(title);
            _panel.AddChild(_status);
            _panel.AddChild(_question);
            _panel.AddChild(choices);
            _panel.AddChild(_feedback);
            Chart.AddControl(_panel);
        }

        private TextBlock Text(string value, string colour, double size)
        {
            return new TextBlock
            {
                Text = value,
                ForegroundColor = Color.FromHex(colour),
                FontSize = size,
                TextWrapping = TextWrapping.Wrap,
                Width = 300,
                Margin = new Thickness(0, 0, 0, 8)
            };
        }

        private void RenderQuestion()
        {
            _status.Text = "CONFIRMED CLOSED-CANDLE OBSERVATION";
            _question.Text = _active.Prompt;
            _feedback.Text = "Choose an answer before revealing the explanation.";
            for (int i = 0; i < 4; i++)
            {
                _answers[i].Text = _active.Options[i];
                _answers[i].IsEnabled = true;
            }
        }

        private void Grade(int selected)
        {
            if (_active == null) return;
            bool correct = selected == _active.CorrectIndex;
            _feedback.Text = (correct ? "CORRECT ✓\n" : "NOT QUITE\n") +
                "Answer: " + _active.Options[_active.CorrectIndex] + "\n" +
                _active.Explanation +
                "\n\nEducational observation only. Context and uncertainty still matter.";
            foreach (Button button in _answers) button.IsEnabled = false;
            Chart.DrawText("GTS_TUTOR_RESULT_" + _active.BarIndex, _active.ShortLabel,
                _active.BarIndex, Bars.HighPrices[_active.BarIndex] + Symbol.PipSize * 8,
                Color.FromHex("#38BDF8"));
        }

        private Question BuildQuestion(int i, StructureSnapshot s)
        {
            if (s.ChangeOfCharacter != Direction.None)
            {
                string answer = s.ChangeOfCharacter == Direction.Bullish ? "Potential bullish CHoCH" : "Potential bearish CHoCH";
                return Q(i, "What structural event has just been confirmed?", answer,
                    "Bullish BOS", "Bearish BOS", "Range continuation",
                    "Price closed beyond the prior swing against the preceding structure. This is a potential change of character under the tutor's stated swing rules.",
                    s.ChangeOfCharacter == Direction.Bullish ? "POTENTIAL CHoCH ↑" : "POTENTIAL CHoCH ↓");
            }
            if (s.BreakOfStructure != Direction.None)
            {
                string answer = s.BreakOfStructure == Direction.Bullish ? "Bullish BOS" : "Bearish BOS";
                return Q(i, "What market-structure event has just closed?", answer,
                    "Potential bullish CHoCH", "Potential bearish CHoCH", "No confirmed break",
                    "The candle closed beyond the most recent confirmed swing level. A wick through it alone would not meet this tutor's BOS rule.",
                    s.BreakOfStructure == Direction.Bullish ? "BOS ↑" : "BOS ↓");
            }
            PatternResult pattern = DetectCandlePattern(i);
            if (pattern != null)
                return Q(i, "Which completed candlestick pattern best matches the highlighted candle(s)?",
                    pattern.Name, "No recognised pattern", Opposite(pattern.Name), "Market structure break",
                    pattern.Reason + " The name describes candle geometry and context; it does not predict the next candle.",
                    pattern.Name.ToUpperInvariant());
            if (s.Retest != RetestType.None)
            {
                string answer = s.Retest == RetestType.Support ? "Support-area rejection" : "Resistance-area rejection";
                return Q(i, "What price behaviour occurred near a prior swing?", answer,
                    "Confirmed breakout", "Doji", "No nearby structure",
                    "Price tested a recent confirmed swing area and closed back away from it. The area can still fail later.",
                    s.Retest == RetestType.Support ? "SUPPORT RETEST" : "RESISTANCE RETEST");
            }

            string state = s.State == MarketState.Uptrend ? "Uptrend structure" :
                s.State == MarketState.Downtrend ? "Downtrend structure" :
                s.State == MarketState.Range ? "Range / mixed structure" : "Not enough confirmed swings";
            return Q(i, "Using the confirmed swing highs and lows, what structure is visible?",
                state, "Bullish Engulfing", "Bearish Engulfing", "Guaranteed reversal",
                s.StructureReason, state.ToUpperInvariant());
        }

        private Question Q(int bar, string prompt, string correct, string w1, string w2, string w3, string explanation, string label)
        {
            var options = new List<string> { correct, w1, w2, w3 }.Distinct().Take(4).ToList();
            while (options.Count < 4) options.Add("Not enough information");
            int shift = Math.Abs(bar) % 4;
            options = options.Skip(shift).Concat(options.Take(shift)).ToList();
            return new Question { BarIndex = bar, Prompt = prompt, Options = options.ToArray(),
                CorrectIndex = options.IndexOf(correct), Explanation = explanation, ShortLabel = label };
        }

        private StructureSnapshot AnalyseStructure(int closed)
        {
            var highs = new List<Pivot>();
            var lows = new List<Pivot>();
            int start = Math.Max(SwingStrength, closed - Lookback);
            int end = closed - SwingStrength;
            for (int i = start; i <= end; i++)
            {
                bool high = true, low = true;
                for (int j = 1; j <= SwingStrength; j++)
                {
                    high &= Bars.HighPrices[i] > Bars.HighPrices[i - j] && Bars.HighPrices[i] >= Bars.HighPrices[i + j];
                    low &= Bars.LowPrices[i] < Bars.LowPrices[i - j] && Bars.LowPrices[i] <= Bars.LowPrices[i + j];
                }
                if (high) highs.Add(new Pivot(i, Bars.HighPrices[i]));
                if (low) lows.Add(new Pivot(i, Bars.LowPrices[i]));
            }

            var result = new StructureSnapshot { Highs = highs, Lows = lows };
            if (highs.Count < 2 || lows.Count < 2)
            {
                result.StructureReason = "At least two confirmed swing highs and two confirmed swing lows are required.";
                return result;
            }

            bool hh = highs.Last().Price > highs[highs.Count - 2].Price;
            bool hl = lows.Last().Price > lows[lows.Count - 2].Price;
            bool lh = highs.Last().Price < highs[highs.Count - 2].Price;
            bool ll = lows.Last().Price < lows[lows.Count - 2].Price;
            result.State = hh && hl ? MarketState.Uptrend : lh && ll ? MarketState.Downtrend : MarketState.Range;
            result.StructureReason = hh && hl
                ? "The latest confirmed swing high and swing low are above their previous equivalents."
                : lh && ll
                    ? "The latest confirmed swing high and swing low are below their previous equivalents."
                    : "The confirmed highs and lows do not yet form a consistent rising or falling sequence.";

            Pivot priorHigh = highs.LastOrDefault(p => p.Index < closed);
            Pivot priorLow = lows.LastOrDefault(p => p.Index < closed);
            if (priorHigh != null && Bars.ClosePrices[closed] > priorHigh.Price && Bars.ClosePrices[closed - 1] <= priorHigh.Price)
                result.BreakOfStructure = Direction.Bullish;
            else if (priorLow != null && Bars.ClosePrices[closed] < priorLow.Price && Bars.ClosePrices[closed - 1] >= priorLow.Price)
                result.BreakOfStructure = Direction.Bearish;

            MarketState prior = PreviousState(highs, lows);
            if (prior == MarketState.Downtrend && result.BreakOfStructure == Direction.Bullish)
                result.ChangeOfCharacter = Direction.Bullish;
            else if (prior == MarketState.Uptrend && result.BreakOfStructure == Direction.Bearish)
                result.ChangeOfCharacter = Direction.Bearish;

            double tolerance = AverageRange(closed, 14) * 0.2;
            if (priorLow != null && Math.Abs(Bars.LowPrices[closed] - priorLow.Price) <= tolerance && Bars.ClosePrices[closed] > priorLow.Price)
                result.Retest = RetestType.Support;
            else if (priorHigh != null && Math.Abs(Bars.HighPrices[closed] - priorHigh.Price) <= tolerance && Bars.ClosePrices[closed] < priorHigh.Price)
                result.Retest = RetestType.Resistance;
            return result;
        }

        private MarketState PreviousState(List<Pivot> highs, List<Pivot> lows)
        {
            if (highs.Count < 3 || lows.Count < 3) return MarketState.Unclear;
            bool up = highs[highs.Count - 2].Price > highs[highs.Count - 3].Price &&
                      lows[lows.Count - 2].Price > lows[lows.Count - 3].Price;
            bool down = highs[highs.Count - 2].Price < highs[highs.Count - 3].Price &&
                        lows[lows.Count - 2].Price < lows[lows.Count - 3].Price;
            return up ? MarketState.Uptrend : down ? MarketState.Downtrend : MarketState.Range;
        }

        private void DrawStructure(StructureSnapshot s)
        {
            DrawLast(s.Highs, true);
            DrawLast(s.Lows, false);
        }

        private void DrawLast(List<Pivot> pivots, bool high)
        {
            if (pivots.Count < 2) return;
            Pivot previous = pivots[pivots.Count - 2], current = pivots.Last();
            string label = high ? (current.Price > previous.Price ? "HH" : "LH") : (current.Price > previous.Price ? "HL" : "LL");
            double y = high ? current.Price + Symbol.PipSize * 5 : current.Price - Symbol.PipSize * 5;
            Chart.DrawText("GTS_STRUCTURE_" + (high ? "H_" : "L_") + current.Index, label,
                current.Index, y, Color.FromHex("#B7BEC9"));
        }

        private PatternResult DetectCandlePattern(int i)
        {
            if (i < 3) return null;
            double o = Bars.OpenPrices[i], h = Bars.HighPrices[i], l = Bars.LowPrices[i], c = Bars.ClosePrices[i];
            double po = Bars.OpenPrices[i - 1], ph = Bars.HighPrices[i - 1], pl = Bars.LowPrices[i - 1], pc = Bars.ClosePrices[i - 1];
            double range = Math.Max(Symbol.PipSize, h - l), body = Math.Abs(c - o);
            double upper = h - Math.Max(o, c), lower = Math.Min(o, c) - l;
            bool rising = Bars.ClosePrices[i - 1] > Bars.ClosePrices[i - 3];
            bool falling = Bars.ClosePrices[i - 1] < Bars.ClosePrices[i - 3];

            if (body <= range * 0.10) return P("Doji", "The body is no more than 10% of the high-to-low range.");
            if (falling && lower >= Math.Max(body * 2, Symbol.PipSize) && upper <= body) return P("Hammer", "After a short decline, the lower wick is at least twice the body.");
            if (rising && upper >= Math.Max(body * 2, Symbol.PipSize) && lower <= body) return P("Shooting Star", "After a short rise, the upper wick is at least twice the body.");
            if (pc < po && c > o && o <= pc && c >= po) return P("Bullish Engulfing", "A bullish body surrounds the previous bearish body.");
            if (pc > po && c < o && o >= pc && c <= po) return P("Bearish Engulfing", "A bearish body surrounds the previous bullish body.");
            if (h < ph && l > pl) return P("Inside Bar", "The candle's range sits inside the previous range.");
            if (h > ph && l < pl) return P("Outside Bar", "The candle made both a higher high and a lower low than the previous candle.");

            double tolerance = AverageRange(i, 14) * 0.08;
            if (Math.Abs(h - ph) <= tolerance && rising) return P("Tweezer Top", "Adjacent candles tested similar highs after a short rise.");
            if (Math.Abs(l - pl) <= tolerance && falling) return P("Tweezer Bottom", "Adjacent candles tested similar lows after a short decline.");

            double o2 = Bars.OpenPrices[i - 2], c2 = Bars.ClosePrices[i - 2];
            double b1 = Math.Abs(c2 - o2), b2 = Math.Abs(pc - po);
            if (c2 < o2 && b2 < b1 * 0.5 && c > o && c > (o2 + c2) / 2) return P("Morning Star", "A bearish candle, smaller middle body, and bullish recovery formed.");
            if (c2 > o2 && b2 < b1 * 0.5 && c < o && c < (o2 + c2) / 2) return P("Evening Star", "A bullish candle, smaller middle body, and bearish decline formed.");
            if (c2 > o2 && pc > po && c > o && c2 < pc && pc < c) return P("Three White Soldiers", "Three bullish bodies closed progressively higher.");
            if (c2 < o2 && pc < po && c < o && c2 > pc && pc > c) return P("Three Black Crows", "Three bearish bodies closed progressively lower.");
            return null;
        }

        private PatternResult P(string name, string reason) { return new PatternResult { Name = name, Reason = reason }; }

        private string Opposite(string name)
        {
            if (name.Contains("Bullish")) return name.Replace("Bullish", "Bearish");
            if (name.Contains("Bearish")) return name.Replace("Bearish", "Bullish");
            var pairs = new Dictionary<string, string> {
                {"Hammer","Shooting Star"},{"Shooting Star","Hammer"},{"Morning Star","Evening Star"},{"Evening Star","Morning Star"},
                {"Tweezer Top","Tweezer Bottom"},{"Tweezer Bottom","Tweezer Top"},{"Inside Bar","Outside Bar"},{"Outside Bar","Inside Bar"}
            };
            return pairs.ContainsKey(name) ? pairs[name] : "Different candle pattern";
        }

        private double AverageRange(int end, int length)
        {
            int start = Math.Max(0, end - length + 1), count = 0;
            double sum = 0;
            for (int i = start; i <= end; i++) { sum += Bars.HighPrices[i] - Bars.LowPrices[i]; count++; }
            return count == 0 ? Symbol.PipSize : sum / count;
        }

        private sealed class Question { public int BarIndex, CorrectIndex; public string Prompt, Explanation, ShortLabel; public string[] Options; }
        private sealed class PatternResult { public string Name, Reason; }
        private sealed class Pivot { public Pivot(int index, double price) { Index = index; Price = price; } public int Index; public double Price; }
        private sealed class StructureSnapshot
        {
            public List<Pivot> Highs = new List<Pivot>(), Lows = new List<Pivot>();
            public MarketState State; public Direction BreakOfStructure, ChangeOfCharacter;
            public RetestType Retest; public string StructureReason;
        }
        private enum MarketState { Unclear, Uptrend, Downtrend, Range }
        private enum Direction { None, Bullish, Bearish }
        private enum RetestType { None, Support, Resistance }
    }
}
