using System.Text.RegularExpressions;
using Microsoft.UI.Text;
using Microsoft.UI.Xaml;
using Microsoft.UI.Xaml.Controls;
using Microsoft.UI.Xaml.Documents;
using Microsoft.UI.Xaml.Media;
using Windows.UI.Text;
using YMhut.Box.Core.Tools;
using YMhut.Box.WinUI.Services;

namespace YMhut.Box.WinUI.Views.Tools;

public abstract partial class AdaptiveToolPage
{
    private void RenderMarkdownResult(ToolMarkdownDocument markdown)
    {
        foreach (var element in BuildMarkdownElements(markdown.MarkdownText))
        {
            _resultCards.Children.Add(element);
        }
    }

    private static IEnumerable<UIElement> BuildMarkdownElements(string markdown)
    {
        var lines = markdown.Replace("\r\n", "\n").Split('\n');
        var index = 0;
        while (index < lines.Length)
        {
            var line = lines[index];
            if (string.IsNullOrWhiteSpace(line))
            {
                index++;
                continue;
            }

            if (line.StartsWith("```", StringComparison.Ordinal))
            {
                var language = line[3..].Trim();
                var codeLines = new List<string>();
                index++;
                while (index < lines.Length && !lines[index].StartsWith("```", StringComparison.Ordinal))
                {
                    codeLines.Add(lines[index++]);
                }
                if (index < lines.Length)
                {
                    index++;
                }

                yield return BuildMarkdownCodeBlock(language, string.Join(Environment.NewLine, codeLines));
                continue;
            }

            if (IsMarkdownTableAt(lines, index))
            {
                var tableLines = new List<string>();
                while (index < lines.Length && lines[index].TrimStart().StartsWith('|'))
                {
                    tableLines.Add(lines[index++]);
                }

                yield return BuildMarkdownTable(tableLines);
                continue;
            }

            if (line.StartsWith("#", StringComparison.Ordinal))
            {
                var level = line.TakeWhile(ch => ch == '#').Count();
                var text = line[level..].Trim();
                yield return BuildMarkdownHeading(text, level);
                index++;
                continue;
            }

            if (IsListLine(line))
            {
                var items = new List<string>();
                while (index < lines.Length && IsListLine(lines[index]))
                {
                    items.Add(Regex.Replace(lines[index].Trim(), @"^(\d+[\.\)]|[-*+])\s+", string.Empty));
                    index++;
                }

                yield return BuildMarkdownList(items);
                continue;
            }

            if (line.StartsWith("> ", StringComparison.Ordinal))
            {
                yield return ModernUi.Card(InlineText(line[2..], 13, ModernUi.TextSecondary), new Thickness(12), radius: 8, background: ModernUi.SurfaceAlt);
                index++;
                continue;
            }

            yield return InlineText(line.Trim(), 14, ModernUi.TextPrimary);
            index++;
        }
    }

    private static bool IsMarkdownTableAt(IReadOnlyList<string> lines, int index)
    {
        if (index + 1 >= lines.Count)
        {
            return false;
        }

        return lines[index].TrimStart().StartsWith('|') &&
            Regex.IsMatch(lines[index + 1], @"^\s*\|?\s*:?-{3,}:?\s*(\|\s*:?-{3,}:?\s*)+\|?\s*$");
    }

    private static bool IsListLine(string line)
    {
        return Regex.IsMatch(line.TrimStart(), @"^(\d+[\.\)]|[-*+])\s+");
    }

    private static TextBlock BuildMarkdownHeading(string text, int level)
    {
        var size = level <= 2 ? 18 : 14;
        return InlineText(text, size, level <= 2 ? ModernUi.TextPrimary : ModernUi.TextSecondary, FontWeights.SemiBold);
    }

    private static Border BuildMarkdownCodeBlock(string language, string code)
    {
        var panel = new StackPanel { Spacing = 8 };
        if (!string.IsNullOrWhiteSpace(language))
        {
            panel.Children.Add(ModernUi.SmallBadge(language.ToUpperInvariant(), ModernUi.TextSecondary, ModernUi.Surface));
        }

        panel.Children.Add(new TextBox
        {
            Text = code,
            IsReadOnly = true,
            AcceptsReturn = true,
            TextWrapping = TextWrapping.NoWrap,
            FontFamily = new FontFamily("Cascadia Mono"),
            MinHeight = Math.Min(320, Math.Max(96, code.Split('\n').Length * 22)),
            MaxHeight = 420
        });
        return ModernUi.Card(panel, new Thickness(12), radius: 8, background: ModernUi.SurfaceAlt);
    }

    private static Border BuildMarkdownList(IReadOnlyList<string> items)
    {
        var panel = new StackPanel { Spacing = 8 };
        foreach (var item in items.Take(120))
        {
            var row = new Grid { ColumnSpacing = 10 };
            row.ColumnDefinitions.Add(new ColumnDefinition { Width = GridLength.Auto });
            row.ColumnDefinitions.Add(new ColumnDefinition());
            row.Children.Add(ModernUi.SmallBadge("•", ModernUi.TextSecondary, ModernUi.Surface));
            var text = InlineText(item, 13, ModernUi.TextPrimary);
            Grid.SetColumn(text, 1);
            row.Children.Add(text);
            panel.Children.Add(row);
        }

        return ModernUi.Card(panel, new Thickness(12), radius: 8, background: ModernUi.SurfaceAlt);
    }

    private static Border BuildMarkdownTable(IReadOnlyList<string> lines)
    {
        var rows = lines
            .Where(line => !Regex.IsMatch(line, @"^\s*\|?\s*:?-{3,}:?\s*(\|\s*:?-{3,}:?\s*)+\|?\s*$"))
            .Select(ParseMarkdownTableRow)
            .Where(row => row.Count > 0)
            .Take(80)
            .ToList();

        if (rows.Count == 0)
        {
            return ModernUi.Card(ModernUi.Text(AppLocalizer.T("空表格", "Empty table"), 13, foreground: ModernUi.TextSecondary), new Thickness(12), radius: 8, background: ModernUi.SurfaceAlt);
        }

        var columnCount = Math.Min(8, rows.Max(row => row.Count));
        var grid = new Grid { RowSpacing = 1, ColumnSpacing = 1, MinWidth = Math.Min(900, Math.Max(360, columnCount * 130)) };
        for (var column = 0; column < columnCount; column++)
        {
            grid.ColumnDefinitions.Add(new ColumnDefinition { Width = new GridLength(1, GridUnitType.Star), MinWidth = 120 });
        }

        for (var rowIndex = 0; rowIndex < rows.Count; rowIndex++)
        {
            grid.RowDefinitions.Add(new RowDefinition { Height = GridLength.Auto });
            for (var column = 0; column < columnCount; column++)
            {
                var text = rows[rowIndex].ElementAtOrDefault(column) ?? string.Empty;
                var cell = new Border
                {
                    Padding = new Thickness(10, 7, 10, 7),
                    Background = rowIndex == 0 ? ModernUi.AccentSoft : ModernUi.Surface,
                    Child = InlineText(text, 12, ModernUi.TextPrimary, rowIndex == 0 ? FontWeights.SemiBold : FontWeights.Normal)
                };
                Grid.SetRow(cell, rowIndex);
                Grid.SetColumn(cell, column);
                grid.Children.Add(cell);
            }
        }

        return ModernUi.Card(new ScrollViewer
        {
            HorizontalScrollBarVisibility = ScrollBarVisibility.Auto,
            VerticalScrollBarVisibility = ScrollBarVisibility.Disabled,
            Content = grid
        }, new Thickness(12), radius: 8, background: ModernUi.SurfaceAlt);
    }

    private static IReadOnlyList<string> ParseMarkdownTableRow(string line)
    {
        return line.Trim().Trim('|').Split('|', StringSplitOptions.TrimEntries).Select(UnescapeMarkdown).ToList();
    }

    private static TextBlock InlineText(string text, double size, Brush foreground, FontWeight? weight = null)
    {
        var block = new TextBlock
        {
            FontSize = size,
            Foreground = foreground,
            FontWeight = weight ?? FontWeights.Normal,
            TextWrapping = TextWrapping.Wrap
        };

        foreach (var inline in ParseInlineMarkdown(text))
        {
            block.Inlines.Add(inline);
        }

        return block;
    }

    private static IEnumerable<Inline> ParseInlineMarkdown(string text)
    {
        var pattern = new Regex(@"(\*\*(?<bold>.+?)\*\*)|(`(?<code>.+?)`)|(\[(?<linkText>[^\]]+)\]\((?<url>https?://[^)]+)\))", RegexOptions.IgnoreCase);
        var cursor = 0;
        foreach (Match match in pattern.Matches(text))
        {
            if (match.Index > cursor)
            {
                yield return new Run { Text = UnescapeMarkdown(text[cursor..match.Index]) };
            }

            if (match.Groups["bold"].Success)
            {
                yield return new Run { Text = UnescapeMarkdown(match.Groups["bold"].Value), FontWeight = FontWeights.SemiBold };
            }
            else if (match.Groups["code"].Success)
            {
                yield return new Run
                {
                    Text = match.Groups["code"].Value,
                    FontFamily = new FontFamily("Cascadia Mono"),
                    Foreground = ModernUi.Accent
                };
            }
            else if (match.Groups["linkText"].Success)
            {
                var url = match.Groups["url"].Value;
                var link = new Hyperlink();
                link.Inlines.Add(new Run { Text = UnescapeMarkdown(match.Groups["linkText"].Value) });
                link.Click += async (_, _) =>
                {
                    await AppServices.GetRequiredService<IToolLinkNavigationService>().OpenAsync(url);
                };
                yield return link;
            }

            cursor = match.Index + match.Length;
        }

        if (cursor < text.Length)
        {
            yield return new Run { Text = UnescapeMarkdown(text[cursor..]) };
        }
    }

    private static string UnescapeMarkdown(string value)
    {
        return value.Replace("\\|", "|", StringComparison.Ordinal);
    }
}
