﻿// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.

using System.Collections.Immutable;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis.PooledObjects;
using Microsoft.CodeAnalysis.Shared.Extensions;
using Microsoft.CodeAnalysis.Simplification;
using Microsoft.CodeAnalysis.Text;

namespace Microsoft.CodeAnalysis.LanguageServer.Handler.InlineCompletions;

internal partial class XmlSnippetParser
{
    internal class ParsedXmlSnippet
    {
        public ImmutableArray<SnippetPart> Parts { get; }
        public string DefaultText { get; }

        public ParsedXmlSnippet(ImmutableArray<SnippetPart> parts)
        {
            Parts = parts;

            using var _ = PooledStringBuilder.GetInstance(out var defaultSnippetBuilder);
            foreach (var part in parts)
            {
                var textToAdd = part.DefaultText;
                defaultSnippetBuilder.Append(textToAdd);
            }

            DefaultText = defaultSnippetBuilder.ToString();
        }
    }

    internal abstract record SnippetPart(string DefaultText);

    internal record SnippetFieldPart(string FieldName, string DefaultText, int? EditIndex) : SnippetPart(DefaultText);

    internal record SnippetFunctionPart(string FieldName, string DefaultText, int? EditIndex, string FunctionName, string? FunctionParam)
        : SnippetFieldPart(FieldName, DefaultText, EditIndex)
    {
        public async Task<SnippetFunctionPart> WithSnippetFunctionResultAsync(Document documentWithSnippet, TextSpan fieldSpan, SimplifierOptions simplifierOptions, CancellationToken cancellationToken)
        {
            var snippetFunctionService = documentWithSnippet.Project.GetRequiredLanguageService<SnippetFunctionService>();
            switch (FunctionName)
            {
                case "SimpleTypeName":
                    if (FunctionParam == null)
                    {
                        return this;
                    }

                    var simplifiedTypeName = await SnippetFunctionService.GetSimplifiedTypeNameAsync(documentWithSnippet, fieldSpan, FunctionParam, simplifierOptions, cancellationToken).ConfigureAwait(false);
                    if (simplifiedTypeName == null)
                    {
                        return this;
                    }

                    return this with { DefaultText = simplifiedTypeName };
                case "ClassName":
                    var className = await snippetFunctionService.GetContainingClassNameAsync(documentWithSnippet, fieldSpan.Start, cancellationToken).ConfigureAwait(false);
                    if (className == null)
                    {
                        return this;
                    }

                    return this with { DefaultText = className };
                case "GenerateSwitchCases":
                    // Generate switch cases requires a multi-step snippet interaction, where the snippet is inserted first then the
                    // client calls back to on commit so that we can generate the cases for the specified switch value.
                    // This is not yet supported via LSP.
                    return this;
                default:
                    return this;
            }
        }
    }

    /// <summary>
    /// To indicate cursor location we put in a multi-line comment so that we can
    /// find it after formatting.
    /// </summary>
    internal record SnippetCursorPart() : SnippetPart("/*$0*/");

    internal record SnippetStringPart(string Text) : SnippetPart(Text);
}
