using Microsoft.CodeAnalysis;
using System.Collections.Immutable;
using System.Text.RegularExpressions;
using System.Text;

namespace RobotNet.WebApp.Scripts.Monaco;

public enum MarkdownFormat
{
    Default,
    Italicize,
    FirstLineAsCSharp,
    FirstLineDefaultRestCSharp,
    AllTextAsCSharp
}

public static class MarkdownHelpers
{
    private static readonly Regex EscapeRegex = new("([\\\\`\\*_\\{\\}\\[\\]\\(\\)#+\\-\\.!])", RegexOptions.Compiled);

    private const string ContainerStart = "ContainerStart";

    private const string ContainerEnd = "ContainerEnd";

    public static string Escape(string markdown) => string.IsNullOrEmpty(markdown) ? string.Empty : EscapeRegex.Replace(markdown, "\\$1");

    public static void TaggedTextToMarkdown(ImmutableArray<TaggedText> taggedParts, StringBuilder stringBuilder, string newLine, MarkdownFormat markdownFormat, out bool endedWithLineBreak)
    {
        bool isInCodeBlock = false;
        bool brokeLine = true;
        bool afterFirstLine = false;
        if (markdownFormat == MarkdownFormat.Italicize)
        {
            stringBuilder.Append('_');
        }

        int num = 0;
        while (num < taggedParts.Length)
        {
            TaggedText taggedText = taggedParts[num];
            bool flag;
            if (brokeLine && markdownFormat != MarkdownFormat.Italicize)
            {
                brokeLine = false;
                if (!afterFirstLine)
                {
                    if (markdownFormat != MarkdownFormat.FirstLineAsCSharp)
                    {
                        goto IL_00a2;
                    }

                    flag = true;
                }
                else
                {
                    if (markdownFormat != MarkdownFormat.FirstLineDefaultRestCSharp)
                    {
                        goto IL_00a2;
                    }

                    flag = true;
                }

                goto IL_00bf;
            }

            goto IL_0279;
        IL_00a2:
            flag = markdownFormat == MarkdownFormat.AllTextAsCSharp;
            goto IL_00bf;
        IL_0279:
            switch (taggedText.Tag)
            {
                case "Text":
                    if (!isInCodeBlock)
                    {
                        addText(taggedText.Text);
                        break;
                    }

                    endBlock();
                    addText(taggedText.Text);
                    break;
                case "Space":
                    if (isInCodeBlock)
                    {
                        if (indexIsTag(num + 1, ["Text"]))
                        {
                            endBlock();
                        }

                        addText(taggedText.Text);
                        break;
                    }

                    goto case "Punctuation";
                case "Punctuation":
                    addText(taggedText.Text);
                    break;
                case ContainerStart:
                    addNewline();
                    addText(taggedText.Text);
                    break;
                case ContainerEnd:
                    addNewline();
                    break;
                case "LineBreak":
                    if (stringBuilder.Length != 0 && !indexIsTag(num + 1, [ContainerStart, ContainerEnd]) && num + 1 != taggedParts.Length)
                    {
                        addNewline();
                    }

                    break;
                default:
                    if (!isInCodeBlock)
                    {
                        isInCodeBlock = true;
                        stringBuilder.Append('`');
                    }

                    stringBuilder.Append(taggedText.Text);
                    brokeLine = false;
                    break;
            }

            num++;
            continue;
        IL_00bf:
            bool flag2 = flag;
            if (!flag2)
            {
                for (int j = num; j < taggedParts.Length; flag2 = true, j++)
                {
                    switch (taggedParts[j].Tag)
                    {
                        case "Text":
                            flag2 = false;
                            break;
                        default:
                            continue;
                        case ContainerStart:
                        case ContainerEnd:
                        case "LineBreak":
                            break;
                    }

                    break;
                }
            }
            else
            {
                flag2 = !indexIsTag(num,
                [
                        ContainerStart,
                        ContainerEnd,
                        "LineBreak"
                ]);
            }

            if (flag2)
            {
                afterFirstLine = true;
                stringBuilder.Append("```csharp");
                stringBuilder.Append(newLine);
                while (true)
                {
                    if (num < taggedParts.Length)
                    {
                        taggedText = taggedParts[num];
                        if (taggedText.Tag == ContainerStart || taggedText.Tag == ContainerEnd || taggedText.Tag == "LineBreak")
                        {
                            stringBuilder.Append(newLine);
                            if (markdownFormat != MarkdownFormat.AllTextAsCSharp && markdownFormat != MarkdownFormat.FirstLineDefaultRestCSharp)
                            {
                                break;
                            }
                        }
                        else
                        {
                            stringBuilder.Append(taggedText.Text);
                        }

                        num++;
                        continue;
                    }

                    stringBuilder.Append(newLine);
                    stringBuilder.Append("```");
                    endedWithLineBreak = false;
                    return;
                }

                stringBuilder.Append("```");
            }

            goto IL_0279;
        }

        if (isInCodeBlock)
        {
            endBlock();
        }

        if (!brokeLine && markdownFormat == MarkdownFormat.Italicize)
        {
            stringBuilder.Append('_');
        }

        endedWithLineBreak = brokeLine;
        void addNewline()
        {
            if (isInCodeBlock)
            {
                endBlock();
            }

            if (markdownFormat == MarkdownFormat.Italicize)
            {
                stringBuilder.Append('_');
            }

            stringBuilder.Append(newLine);
            stringBuilder.Append(newLine);
            brokeLine = true;
            if (markdownFormat == MarkdownFormat.Italicize)
            {
                stringBuilder.Append('_');
            }
        }

        void addText(string text)
        {
            brokeLine = false;
            afterFirstLine = true;
            if (!isInCodeBlock)
            {
                text = Escape(text);
            }

            stringBuilder.Append(text);
        }

        void endBlock()
        {
            stringBuilder.Append('`');
            isInCodeBlock = false;
        }

        bool indexIsTag(int i, string[] tags)
        {
            if (i < taggedParts.Length)
            {
                return tags.Contains(taggedParts[i].Tag);
            }

            return false;
        }
    }
}

