﻿// 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.Threading.Tasks;
using Microsoft.CodeAnalysis.CodeRefactorings;
using Microsoft.CodeAnalysis.CSharp.ReplaceDocCommentTextWithTag;
using Microsoft.CodeAnalysis.Editor.CSharp.UnitTests.CodeRefactorings;
using Microsoft.CodeAnalysis.Test.Utilities;
using Roslyn.Test.Utilities;
using Xunit;

namespace Microsoft.CodeAnalysis.Editor.CSharp.UnitTests.ReplaceDocCommentTextWithTag
{
    [Trait(Traits.Feature, Traits.Features.CodeActionsReplaceDocCommentTextWithTag)]
    public class ReplaceDocCommentTextWithTagTests : AbstractCSharpCodeActionTest
    {
        protected override CodeRefactoringProvider CreateCodeRefactoringProvider(Workspace workspace, TestParameters parameters)
            => new CSharpReplaceDocCommentTextWithTagCodeRefactoringProvider();

        [Fact]
        public async Task TestStartOfKeyword()
        {
            await TestInRegularAndScriptAsync(
                """
                /// Testing keyword [||]null.
                class C<TKey>
                {
                }
                """,

                """
                /// Testing keyword <see langword="null"/>.
                class C<TKey>
                {
                }
                """);
        }

        [Fact]
        public async Task TestEndOfKeyword()
        {
            await TestInRegularAndScriptAsync(
                """
                /// Testing keyword abstract[||].
                class C<TKey>
                {
                }
                """,

                """
                /// Testing keyword <see langword="abstract"/>.
                class C<TKey>
                {
                }
                """);
        }

        [Fact]
        public async Task TestEndOfKeyword_NewLineFollowing()
        {
            await TestInRegularAndScriptAsync(
                """
                /// Testing keyword static[||]
                class C<TKey>
                {
                }
                """,

                """
                /// Testing keyword <see langword="static"/>
                class C<TKey>
                {
                }
                """);
        }

        [Fact]
        public async Task TestSelectedKeyword()
        {
            await TestInRegularAndScriptAsync(
                """
                /// Testing keyword [|abstract|].
                class C<TKey>
                {
                }
                """,

                """
                /// Testing keyword <see langword="abstract"/>.
                class C<TKey>
                {
                }
                """);
        }

        [Fact]
        public async Task TestInsideKeyword()
        {
            await TestInRegularAndScriptAsync(
                """
                /// Testing keyword asy[||]nc.
                class C<TKey>
                {
                }
                """,

                """
                /// Testing keyword <see langword="async"/>.
                class C<TKey>
                {
                }
                """);
        }

        [Fact]
        public async Task TestNotInsideKeywordIfNonEmptySpan()
        {
            await TestMissingAsync(
                """
                /// TKey must implement the System.IDisposable int[|erf|]ace
                class C<TKey>
                {
                }
                """);
        }

        [Fact]
        public async Task TestStartOfFullyQualifiedTypeName_Start()
        {
            await TestInRegularAndScriptAsync(
                """
                /// TKey must implement the [||]System.IDisposable interface.
                class C<TKey>
                {
                }
                """,

                """
                /// TKey must implement the <see cref="System.IDisposable"/> interface.
                class C<TKey>
                {
                }
                """);
        }

        [Fact]
        public async Task TestStartOfFullyQualifiedTypeName_Mid1()
        {
            await TestInRegularAndScriptAsync(
                """
                /// TKey must implement the System[||].IDisposable interface.
                class C<TKey>
                {
                }
                """,

                """
                /// TKey must implement the <see cref="System.IDisposable"/> interface.
                class C<TKey>
                {
                }
                """);
        }

        [Fact]
        public async Task TestStartOfFullyQualifiedTypeName_Mid2()
        {
            await TestInRegularAndScriptAsync(
                """
                /// TKey must implement the System.[||]IDisposable interface.
                class C<TKey>
                {
                }
                """,

                """
                /// TKey must implement the <see cref="System.IDisposable"/> interface.
                class C<TKey>
                {
                }
                """);
        }

        [Fact]
        public async Task TestStartOfFullyQualifiedTypeName_End()
        {
            await TestInRegularAndScriptAsync(
                """
                /// TKey must implement the System.IDisposable[||] interface.
                class C<TKey>
                {
                }
                """,

                """
                /// TKey must implement the <see cref="System.IDisposable"/> interface.
                class C<TKey>
                {
                }
                """);
        }

        [Fact]
        public async Task TestStartOfFullyQualifiedTypeName_Selected()
        {
            await TestInRegularAndScriptAsync(
                """
                /// TKey must implement the [|System.IDisposable|] interface.
                class C<TKey>
                {
                }
                """,

                """
                /// TKey must implement the <see cref="System.IDisposable"/> interface.
                class C<TKey>
                {
                }
                """);
        }

        [Fact]
        public async Task TestTypeParameterReference()
        {
            await TestInRegularAndScriptAsync(
                """
                /// [||]TKey must implement the System.IDisposable interface.
                class C<TKey>
                {
                }
                """,

                """
                /// <typeparamref name="TKey"/> must implement the System.IDisposable interface.
                class C<TKey>
                {
                }
                """);
        }

        [Fact]
        public async Task TestTypeParameterReference_EmptyClassBody()
        {
            await TestInRegularAndScriptAsync(
                """
                /// [||]TKey must implement the System.IDisposable interface.
                class C<TKey>{}
                """,

                """
                /// <typeparamref name="TKey"/> must implement the System.IDisposable interface.
                class C<TKey>{}
                """);
        }

        [Fact]
        public async Task TestCanSeeInnerMethod()
        {
            await TestInRegularAndScriptAsync(
                """
                /// Use WriteLine[||] as a Console.WriteLine replacement
                class C
                {
                    void WriteLine<TKey>(TKey value) { }
                }
                """,

                """
                /// Use <see cref="WriteLine"/> as a Console.WriteLine replacement
                class C
                {
                    void WriteLine<TKey>(TKey value) { }
                }
                """);
        }

        [Fact]
        public async Task TestNotOnMispelledName()
        {
            await TestMissingAsync(
                """
                /// Use WriteLine1[||] as a Console.WriteLine replacement
                class C
                {
                    void WriteLine<TKey>(TKey value) { }
                }
                """);
        }

        [Fact]
        public async Task TestMethodTypeParameterSymbol()
        {
            await TestInRegularAndScriptAsync(
                """
                class C
                {
                    /// value has type TKey[||] so we don't box primitives.
                    void WriteLine<TKey>(TKey value) { }
                }
                """,

                """
                class C
                {
                    /// value has type <typeparamref name="TKey"/> so we don't box primitives.
                    void WriteLine<TKey>(TKey value) { }
                }
                """);
        }

        [Fact]
        public async Task TestMethodTypeParameterSymbol_EmptyBody()
        {
            await TestInRegularAndScriptAsync(
                """
                class C
                {
                    /// value has type TKey[||] so we don't box primitives.
                    void WriteLine<TKey>(TKey value){}
                }
                """,

                """
                class C
                {
                    /// value has type <typeparamref name="TKey"/> so we don't box primitives.
                    void WriteLine<TKey>(TKey value){}
                }
                """);
        }

        [Fact]
        public async Task TestMethodTypeParameterSymbol_ExpressionBody()
        {
            await TestInRegularAndScriptAsync(
                """
                class C
                {
                    /// value has type TKey[||] so we don't box primitives.
                    object WriteLine<TKey>(TKey value) => null;
                }
                """,

                """
                class C
                {
                    /// value has type <typeparamref name="TKey"/> so we don't box primitives.
                    object WriteLine<TKey>(TKey value) => null;
                }
                """);
        }

        [Fact]
        public async Task TestMethodTypeParameter_SemicolonBody()
        {
            await TestInRegularAndScriptAsync(
                """
                class C
                {
                    /// value has type TKey[||] so we don't box primitives.
                    void WriteLine<TKey>(TKey value);
                }
                """,

                """
                class C
                {
                    /// value has type <typeparamref name="TKey"/> so we don't box primitives.
                    void WriteLine<TKey>(TKey value);
                }
                """);
        }

        [Fact]
        public async Task TestMethodParameterSymbol()
        {
            await TestInRegularAndScriptAsync(
                """
                class C
                {
                    /// value[||] has type TKey so we don't box primitives.
                    void WriteLine<TKey>(TKey value) { }
                }
                """,

                """
                class C
                {
                    /// <paramref name="value"/> has type TKey so we don't box primitives.
                    void WriteLine<TKey>(TKey value) { }
                }
                """);
        }

        [Fact]
        public async Task TestMethodParameterSymbol_EmptyBody()
        {
            await TestInRegularAndScriptAsync(
                """
                class C
                {
                    /// value[||] has type TKey so we don't box primitives.
                    void WriteLine<TKey>(TKey value){}
                }
                """,

                """
                class C
                {
                    /// <paramref name="value"/> has type TKey so we don't box primitives.
                    void WriteLine<TKey>(TKey value){}
                }
                """);
        }

        [Fact]
        public async Task TestMethodParameterSymbol_ExpressionBody()
        {
            await TestInRegularAndScriptAsync(
                """
                class C
                {
                    /// value[||] has type TKey so we don't box primitives.
                    object WriteLine<TKey>(TKey value) => null;
                }
                """,

                """
                class C
                {
                    /// <paramref name="value"/> has type TKey so we don't box primitives.
                    object WriteLine<TKey>(TKey value) => null;
                }
                """);
        }

        [Fact]
        public async Task TestMethodParameterSymbol_SemicolonBody()
        {
            await TestInRegularAndScriptAsync(
                """
                class C
                {
                    /// value[||] has type TKey so we don't box primitives.
                    void WriteLine<TKey>(TKey value);
                }
                """,

                """
                class C
                {
                    /// <paramref name="value"/> has type TKey so we don't box primitives.
                    void WriteLine<TKey>(TKey value);
                }
                """);
        }

        [Fact]
        [WorkItem("https://github.com/dotnet/roslyn/issues/22278")]
        [WorkItem("https://github.com/dotnet/roslyn/issues/31208")]
        public async Task TestApplicableKeyword()
        {
            await TestInRegularAndScript1Async(
                """
                /// Testing keyword interfa[||]ce.
                class C<TKey>
                {
                }
                """,
                """
                /// Testing keyword <see langword="interface"/>.
                class C<TKey>
                {
                }
                """);
        }

        [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/22278")]
        public async Task TestInXMLAttribute()
        {
            await TestMissingAsync(
                """
                /// Testing keyword inside <see langword ="nu[||]ll"/>
                class C
                {
                    void WriteLine<TKey>(TKey value) { }
                }
                """);
        }

        [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/22278")]
        public async Task TestInXMLAttribute2()
        {
            await TestMissingAsync(
                """
                /// Testing keyword inside <see langword ="nu[||]ll"
                class C
                {
                    void WriteLine<TKey>(TKey value) { }
                }
                """);
        }

        [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/38370")]
        public async Task TestBaseKeyword()
        {
            await TestInRegularAndScriptAsync(
                """
                /// Testing keyword [||]base.
                class C<TKey>
                {
                }
                """,

                """
                /// Testing keyword <see langword="base"/>.
                class C<TKey>
                {
                }
                """);
        }

        [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/38370")]
        public async Task TestThisKeyword()
        {
            await TestInRegularAndScriptAsync(
                """
                /// Testing keyword [||]this.
                class C<TKey>
                {
                }
                """,

                """
                /// Testing keyword <see langword="this"/>.
                class C<TKey>
                {
                }
                """);
        }

        [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/31208")]
        public async Task TestArbitraryKeyword()
        {
            await TestInRegularAndScriptAsync(
                """
                /// Testing keyword [||]delegate.
                class C<TKey>
                {
                }
                """,

                """
                /// Testing keyword <see langword="delegate"/>.
                class C<TKey>
                {
                }
                """);
        }

        [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/31208")]
        public async Task TestContextualKeyword()
        {
            await TestInRegularAndScriptAsync(
                """
                /// Testing keyword [||]yield.
                class C<TKey>
                {
                }
                """,

                """
                /// Testing keyword <see langword="yield"/>.
                class C<TKey>
                {
                }
                """);
        }
    }
}
