using System.Text.RegularExpressions;
using Avalonia;
using Avalonia.Controls;
using Avalonia.Controls.Documents;
using Avalonia.Layout;
using Avalonia.Markup.Xaml;
using Avalonia.Media;

namespace ClaudeDo.Ui.Views.Controls;

public partial class MarkdownView : UserControl
{
    public static readonly StyledProperty<string?> MarkdownProperty =
        AvaloniaProperty.Register<MarkdownView, string?>(nameof(Markdown));

    public string? Markdown
    {
        get => GetValue(MarkdownProperty);
        set => SetValue(MarkdownProperty, value);
    }

    private StackPanel? _host;

    public MarkdownView()
    {
        InitializeComponent();
        _host = this.FindControl<StackPanel>("Host");
    }

    private void InitializeComponent() => AvaloniaXamlLoader.Load(this);

    protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs change)
    {
        base.OnPropertyChanged(change);
        if (change.Property == MarkdownProperty)
            Render(change.GetNewValue<string?>());
    }

    private static readonly Regex NumberedItem = new(@"^\s*(\d+)\.\s+(.*)$", RegexOptions.Compiled);

    private void Render(string? md)
    {
        _host ??= this.FindControl<StackPanel>("Host");
        if (_host is null) return;
        _host.Children.Clear();

        if (string.IsNullOrWhiteSpace(md))
            return;

        var lines = md.Replace("\r\n", "\n").Split('\n');
        int i = 0;
        while (i < lines.Length)
        {
            var line = lines[i];
            if (string.IsNullOrWhiteSpace(line)) { i++; continue; }

            if (line.StartsWith("### "))
            {
                _host.Children.Add(Heading(line[4..], 13));
                i++;
                continue;
            }
            if (line.StartsWith("## "))
            {
                _host.Children.Add(Heading(line[3..], 15));
                i++;
                continue;
            }
            if (line.StartsWith("# "))
            {
                _host.Children.Add(Heading(line[2..], 17));
                i++;
                continue;
            }

            if (line.StartsWith("- ") || line.StartsWith("* "))
            {
                while (i < lines.Length && (lines[i].StartsWith("- ") || lines[i].StartsWith("* ")))
                {
                    _host.Children.Add(Bullet(lines[i][2..]));
                    i++;
                }
                continue;
            }

            var num = NumberedItem.Match(line);
            if (num.Success)
            {
                while (i < lines.Length && NumberedItem.IsMatch(lines[i]))
                {
                    var m = NumberedItem.Match(lines[i]);
                    _host.Children.Add(Numbered(m.Groups[1].Value, m.Groups[2].Value));
                    i++;
                }
                continue;
            }

            var paragraph = new System.Text.StringBuilder();
            while (i < lines.Length
                   && !string.IsNullOrWhiteSpace(lines[i])
                   && !IsBlockStart(lines[i]))
            {
                if (paragraph.Length > 0) paragraph.Append(' ');
                paragraph.Append(lines[i].Trim());
                i++;
            }
            _host.Children.Add(Paragraph(paragraph.ToString()));
        }
    }

    private static bool IsBlockStart(string line) =>
        line.StartsWith("# ") || line.StartsWith("## ") || line.StartsWith("### ")
        || line.StartsWith("- ") || line.StartsWith("* ")
        || NumberedItem.IsMatch(line);

    private static Control Heading(string text, double size)
    {
        var tb = new SelectableTextBlock
        {
            FontSize = size,
            FontWeight = FontWeight.SemiBold,
            TextWrapping = TextWrapping.Wrap,
            Margin = new Thickness(0, 4, 0, 2),
        };
        AppendInlines(tb.Inlines!, text);
        return tb;
    }

    private static Control Paragraph(string text)
    {
        var tb = new SelectableTextBlock { TextWrapping = TextWrapping.Wrap };
        AppendInlines(tb.Inlines!, text);
        return tb;
    }

    private static Control Bullet(string text)
    {
        var grid = new Grid { ColumnDefinitions = new ColumnDefinitions("14,*") };
        var dot = new TextBlock
        {
            Text = "•",
            VerticalAlignment = VerticalAlignment.Top,
            Margin = new Thickness(0, 0, 4, 0),
        };
        Grid.SetColumn(dot, 0);
        grid.Children.Add(dot);

        var tb = new SelectableTextBlock { TextWrapping = TextWrapping.Wrap };
        AppendInlines(tb.Inlines!, text);
        Grid.SetColumn(tb, 1);
        grid.Children.Add(tb);
        return grid;
    }

    private static Control Numbered(string num, string text)
    {
        var grid = new Grid { ColumnDefinitions = new ColumnDefinitions("20,*") };
        var lbl = new TextBlock
        {
            Text = $"{num}.",
            VerticalAlignment = VerticalAlignment.Top,
            Margin = new Thickness(0, 0, 4, 0),
        };
        Grid.SetColumn(lbl, 0);
        grid.Children.Add(lbl);

        var tb = new SelectableTextBlock { TextWrapping = TextWrapping.Wrap };
        AppendInlines(tb.Inlines!, text);
        Grid.SetColumn(tb, 1);
        grid.Children.Add(tb);
        return grid;
    }

    private static void AppendInlines(InlineCollection inlines, string text)
    {
        int i = 0;
        var plain = new System.Text.StringBuilder();

        void FlushPlain()
        {
            if (plain.Length == 0) return;
            inlines.Add(new Run(plain.ToString()));
            plain.Clear();
        }

        while (i < text.Length)
        {
            if (i + 1 < text.Length && text[i] == '*' && text[i + 1] == '*')
            {
                int close = text.IndexOf("**", i + 2, System.StringComparison.Ordinal);
                if (close > i + 2)
                {
                    FlushPlain();
                    inlines.Add(new Run(text[(i + 2)..close]) { FontWeight = FontWeight.Bold });
                    i = close + 2;
                    continue;
                }
            }
            if (text[i] == '*')
            {
                int close = text.IndexOf('*', i + 1);
                if (close > i + 1)
                {
                    FlushPlain();
                    inlines.Add(new Run(text[(i + 1)..close]) { FontStyle = FontStyle.Italic });
                    i = close + 1;
                    continue;
                }
            }
            if (text[i] == '`')
            {
                int close = text.IndexOf('`', i + 1);
                if (close > i + 1)
                {
                    FlushPlain();
                    inlines.Add(new Run(text[(i + 1)..close])
                    {
                        FontFamily = new FontFamily("Consolas,Menlo,monospace"),
                        Background = new SolidColorBrush(Color.FromArgb(60, 127, 127, 127)),
                    });
                    i = close + 1;
                    continue;
                }
            }

            plain.Append(text[i]);
            i++;
        }
        FlushPlain();
    }
}
