using Microsoft.UI.Text;
using Microsoft.UI.Xaml;
using Microsoft.UI.Xaml.Controls;
using Microsoft.UI.Xaml.Media;
using Microsoft.UI.Xaml.Shapes;
using Windows.Foundation;

namespace YMhut.Box.WinUI.Controls;

public sealed class MetricChartControl : UserControl
{
    private const int MaxSamples = 90;

    private readonly TextBlock _valueText;
    private readonly TextBlock _detailText;
    private readonly ProgressBar _bar;
    private readonly Canvas _canvas = new() { MinHeight = 142, Height = 152 };
    private readonly Polyline _line = new()
    {
        Stroke = ModernUi.Accent,
        StrokeThickness = 2.2
    };
    private readonly Queue<double> _samples = new();

    public MetricChartControl(string title, string subtitle, string glyph)
    {
        _valueText = ModernUi.Text("--", 24, FontWeights.SemiBold, maxLines: 1);
        _valueText.TextAlignment = TextAlignment.Right;
        _detailText = ModernUi.Text(subtitle, 12, foreground: ModernUi.TextSecondary, maxLines: 2);
        _bar = new ProgressBar
        {
            Minimum = 0,
            Maximum = 100,
            Height = 5,
            Foreground = ModernUi.Accent,
            Background = ModernUi.SurfaceAlt
        };

        _canvas.SizeChanged += (_, _) => Redraw();

        var header = new Grid { ColumnSpacing = 12 };
        header.ColumnDefinitions.Add(new ColumnDefinition { Width = GridLength.Auto });
        header.ColumnDefinitions.Add(new ColumnDefinition());
        header.ColumnDefinitions.Add(new ColumnDefinition { Width = GridLength.Auto });
        header.Children.Add(ModernUi.IconTile(glyph, 38, ModernUi.SurfaceAlt, ModernUi.Accent, 17));

        var titleStack = new StackPanel
        {
            Spacing = 2,
            Children =
            {
                ModernUi.Text(title, 17, FontWeights.SemiBold, maxLines: 1),
                _detailText
            }
        };
        Grid.SetColumn(titleStack, 1);
        header.Children.Add(titleStack);
        Grid.SetColumn(_valueText, 2);
        header.Children.Add(_valueText);

        Content = ModernUi.Card(new StackPanel
        {
            Spacing = 12,
            Children =
            {
                header,
                _bar,
                _canvas
            }
        }, new Thickness(16), radius: 8);
    }

    public void Update(double? percent, string valueText, string detailText)
    {
        _valueText.Text = valueText;
        _detailText.Text = detailText;
        if (percent is double value)
        {
            var clamped = Math.Clamp(value, 0, 100);
            _bar.IsIndeterminate = false;
            _bar.Value = clamped;
            _samples.Enqueue(clamped);
            while (_samples.Count > MaxSamples)
            {
                _samples.Dequeue();
            }
        }
        else
        {
            _bar.Value = 0;
        }

        Redraw();
    }

    private void Redraw()
    {
        var width = _canvas.ActualWidth > 0 ? _canvas.ActualWidth : 320;
        var height = _canvas.ActualHeight > 0 ? _canvas.ActualHeight : _canvas.Height;
        if (double.IsNaN(height) || height <= 0)
        {
            height = 152;
        }

        _canvas.Children.Clear();
        _canvas.Children.Add(new Border
        {
            Width = width,
            Height = height,
            Background = ChartBackground(),
            CornerRadius = new CornerRadius(8)
        });

        var left = 34d;
        var right = 10d;
        var top = 10d;
        var bottom = 20d;
        var plotWidth = Math.Max(12, width - left - right);
        var plotHeight = Math.Max(12, height - top - bottom);
        DrawGrid(left, top, plotWidth, plotHeight);

        var values = _samples.ToArray();
        var points = new PointCollection();
        if (values.Length == 0 || width <= 0 || height <= 0)
        {
            _line.Points = points;
            _canvas.Children.Add(_line);
            return;
        }

        for (var index = 0; index < values.Length; index++)
        {
            var x = left + (values.Length == 1 ? plotWidth : index * plotWidth / (values.Length - 1));
            var y = top + (100 - values[index]) / 100 * plotHeight;
            points.Add(new Point(x, Math.Clamp(y, top, top + plotHeight)));
        }

        _line.Points = points;
        _line.Stroke = ModernUi.Accent;
        _canvas.Children.Add(_line);
    }

    private void DrawGrid(double left, double top, double width, double height)
    {
        var gridBrush = GridBrush();
        var labelBrush = LabelBrush();
        foreach (var value in new[] { 100, 75, 50, 25, 0 })
        {
            var y = top + (100 - value) / 100d * height;
            var line = new Line
            {
                X1 = left,
                X2 = left + width,
                Y1 = y,
                Y2 = y,
                Stroke = gridBrush,
                StrokeThickness = 1,
                Opacity = value is 0 or 100 ? 0.42 : 0.28
            };
            _canvas.Children.Add(line);

            var label = new TextBlock
            {
                Text = value.ToString(),
                FontSize = 10.5,
                Foreground = labelBrush,
                Opacity = 0.78
            };
            Canvas.SetLeft(label, 6);
            Canvas.SetTop(label, Math.Clamp(y - 8, 2, top + height - 12));
            _canvas.Children.Add(label);
        }

        for (var index = 0; index <= 5; index++)
        {
            var x = left + index / 5d * width;
            _canvas.Children.Add(new Line
            {
                X1 = x,
                X2 = x,
                Y1 = top,
                Y2 = top + height,
                Stroke = gridBrush,
                StrokeThickness = 1,
                Opacity = index is 0 or 5 ? 0.32 : 0.18
            });
        }
    }

    private static SolidColorBrush ChartBackground()
        => IsDarkTheme() ? ModernUi.Brush("#121820") : ModernUi.Brush("#202833");

    private static SolidColorBrush GridBrush()
        => IsDarkTheme() ? ModernUi.Brush("#7EA6C7") : ModernUi.Brush("#9FB4C8");

    private static SolidColorBrush LabelBrush()
        => IsDarkTheme() ? ModernUi.Brush("#C9D8E6") : ModernUi.Brush("#D7E4F0");

    private static bool IsDarkTheme()
    {
        var color = ModernUi.AppBackground.Color;
        return color.R + color.G + color.B < 384;
    }
}
