// 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.

#nullable disable

using System.Threading.Tasks;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.Editor.UnitTests.GoToAdjacentMember;
using Microsoft.CodeAnalysis.Test.Utilities;
using Roslyn.Test.Utilities;
using Xunit;

namespace Microsoft.CodeAnalysis.Editor.CSharp.UnitTests.GoToAdjacentMember
{
    [Trait(Traits.Feature, Traits.Features.GoToAdjacentMember)]
    public class CSharpGoToAdjacentMemberTests : AbstractGoToAdjacentMemberTests
    {
        protected override string LanguageName => LanguageNames.CSharp;
        protected override ParseOptions DefaultParseOptions => CSharpParseOptions.Default;

        [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/4311")]
        public async Task EmptyFile()
        {
            var code = @"$$";
            Assert.Null(await GetTargetPositionAsync(code, next: true));
        }

        [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/4311")]
        public async Task ClassWithNoMembers()
        {
            var code = """
                class C
                {
                $$
                }
                """;
            Assert.Null(await GetTargetPositionAsync(code, next: true));
        }

        [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/4311")]
        public async Task BeforeClassWithMember()
        {
            var code = """
                $$
                class C
                {
                    [||]void M() { }
                }
                """;

            await AssertNavigatedAsync(code, next: true);
        }

        [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/4311")]
        public async Task AfterClassWithMember()
        {
            var code = """
                class C
                {
                    [||]void M() { }
                }

                $$
                """;

            await AssertNavigatedAsync(code, next: true);
        }

        [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/4311")]
        public async Task BetweenClasses()
        {
            var code = """
                class C1
                {
                    void M() { }
                }

                $$

                class C2
                {
                    [||]void M() { }
                }
                """;

            await AssertNavigatedAsync(code, next: true);
        }

        [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/4311")]
        public async Task BetweenClassesPrevious()
        {
            var code = """
                class C1
                {
                    [||]void M() { }
                }

                $$

                class C2
                {
                    void M() { }
                }
                """;

            await AssertNavigatedAsync(code, next: false);
        }

        [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/4311")]
        public async Task FromFirstMemberToSecond()
        {
            var code = """
                class C
                {
                    $$void M1() { }
                    [||]void M2() { }
                }
                """;

            await AssertNavigatedAsync(code, next: true);
        }

        [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/4311")]
        public async Task FromSecondToFirst()
        {
            var code = """
                class C
                {
                    [||]void M1() { }
                    $$void M2() { }
                }
                """;

            await AssertNavigatedAsync(code, next: false);
        }

        [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/4311")]
        public async Task NextWraps()
        {
            var code = """
                class C
                {
                    [||]void M1() { }
                    $$void M2() { }
                }
                """;

            await AssertNavigatedAsync(code, next: true);
        }

        [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/4311")]
        public async Task PreviousWraps()
        {
            var code = """
                class C
                {
                    $$void M1() { }
                    [||]void M2() { }
                }
                """;

            await AssertNavigatedAsync(code, next: false);
        }

        [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/4311")]
        public async Task DescendsIntoNestedType()
        {
            var code = """
                class C
                {
                    $$void M1() { }

                    class N
                    {
                        [||]void M2() { }
                    }
                }
                """;

            await AssertNavigatedAsync(code, next: true);
        }

        [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/4311")]
        public async Task StopsAtConstructor()
        {
            var code = """
                class C
                {
                    $$void M1() { }
                    [||]public C() { }
                }
                """;
            await AssertNavigatedAsync(code, next: true);
        }

        [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/4311")]
        public async Task StopsAtDestructor()
        {
            var code = """
                class C
                {
                    $$void M1() { }
                    [||]~C() { }
                }
                """;
            await AssertNavigatedAsync(code, next: true);
        }

        [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/4311")]
        public async Task StopsAtOperator()
        {
            var code = """
                class C
                {
                    $$void M1() { }
                    [||]static C operator+(C left, C right) { throw new System.NotImplementedException(); }
                }
                """;
            await AssertNavigatedAsync(code, next: true);
        }
        [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/4311")]
        public async Task StopsAtField()
        {
            var code = """
                class C
                {
                    $$void M1() { }
                    [||]int F;
                }
                """;
            await AssertNavigatedAsync(code, next: true);
        }

        [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/4311")]
        public async Task StopsAtFieldlikeEvent()
        {
            var code = """
                class C
                {
                    $$void M1() { }
                    [||]event System.EventHandler E;
                }
                """;
            await AssertNavigatedAsync(code, next: true);
        }

        [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/4311")]
        public async Task StopsAtAutoProperty()
        {
            var code = """
                class C
                {
                    $$void M1() { }
                    [||]int P { get; set ; }
                }
                """;
            await AssertNavigatedAsync(code, next: true);
        }

        [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/4311")]
        public async Task StopsAtPropertyWithAccessors()
        {
            var code = """
                class C
                {
                    $$void M1() { }

                    [||]int P
                    {
                        get { return 42; }
                        set { }
                    }
                }
                """;

            await AssertNavigatedAsync(code, next: true);
        }

        [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/4311")]
        public async Task SkipsPropertyAccessors()
        {
            var code = """
                class C
                {
                    void M1() { }

                    $$int P
                    {
                        get { return 42; }
                        set { }
                    }

                    [||]void M2() { }
                }
                """;

            await AssertNavigatedAsync(code, next: true);
        }

        [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/4311")]
        public async Task FromInsideAccessor()
        {
            var code = """
                class C
                {
                    void M1() { }

                    int P
                    {
                        get { return $$42; }
                        set { }
                    }

                    [||]void M2() { }
                }
                """;

            await AssertNavigatedAsync(code, next: true);
        }

        [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/4311")]
        public async Task StopsAtIndexerWithAccessors()
        {
            var code = """
                class C
                {
                    $$void M1() { }

                    [||]int this[int i]
                    {
                        get { return 42; }
                        set { }
                    }
                }
                """;

            await AssertNavigatedAsync(code, next: true);
        }

        [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/4311")]
        public async Task SkipsIndexerAccessors()
        {
            var code = """
                class C
                {
                    void M1() { }

                    $$int this[int i]
                    {
                        get { return 42; }
                        set { }
                    }

                    [||]void M2() { }
                }
                """;

            await AssertNavigatedAsync(code, next: true);
        }

        [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/4311")]
        public async Task StopsAtEventWithAddRemove()
        {
            var code = """
                class C
                {
                    $$void M1() { }

                    [||]event EventHandler E
                    {
                        add { }
                        remove { }
                    }
                }
                """;

            await AssertNavigatedAsync(code, next: true);
        }

        [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/4311")]
        public async Task SkipsEventAddRemove()
        {
            var code = """
                class C
                {
                    void M1() { }

                    $$event EventHandler E
                    {
                        add { }
                        remove { }
                    }

                    [||]void M2() { }
                }
                """;

            await AssertNavigatedAsync(code, next: true);
        }

        [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/4311")]
        public async Task FromInsideMethod()
        {
            var code = """
                class C
                {
                    void M1()
                    {
                        $$System.Console.WriteLine();
                    }

                    [||]void M2() { }
                }
                """;

            await AssertNavigatedAsync(code, next: true);
        }

        [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/4311")]
        public async Task NextFromBetweenMethods()
        {
            var code = """
                class C
                {
                    void M1() { }

                    $$

                    [||]void M2() { }
                }
                """;

            await AssertNavigatedAsync(code, next: true);
        }

        [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/4311")]
        public async Task PreviousFromBetweenMethods()
        {
            var code = """
                class C
                {
                    [||]void M1() { }

                    $$

                    void M2() { }
                }
                """;

            await AssertNavigatedAsync(code, next: false);
        }

        [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/4311")]
        public async Task NextFromBetweenMethodsInTrailingTrivia()
        {
            var code = """
                class C
                {
                    void M1()
                    {
                    } $$

                    [||]void M2() { }
                }
                """;

            await AssertNavigatedAsync(code, next: true);
        }

        [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/4311")]
        public async Task PreviousFromBetweenMethodsInTrailingTrivia()
        {
            var code = """
                class C
                {
                    [||]void M1()
                    {
                    } $$

                    void M2() { }
                }
                """;

            await AssertNavigatedAsync(code, next: false);
        }

        [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/4311")]
        public async Task StopsAtExpressionBodiedMember()
        {
            var code = """
                class C
                {
                    int M1() => $$42;

                    [||]int M2() => 42;
                }
                """;

            await AssertNavigatedAsync(code, next: true);
        }

        [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/4311")]
        [WorkItem("https://github.com/dotnet/roslyn/issues/10588")]
        public async Task PreviousFromInsideCurrent()
        {
            var code = """
                class C
                {
                    [||]void M1()
                    {
                        Console.WriteLine($$);
                    }

                    void M2()
                    {
                    }
                }
                """;

            await AssertNavigatedAsync(code, next: false);
        }

        [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/4311")]
        public async Task NextInScript()
        {
            var code = """
                $$void M1() { }

                [||]void M2() { }
                """;

            await AssertNavigatedAsync(code, next: true, sourceCodeKind: SourceCodeKind.Script);
        }

        [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/4311")]
        public async Task PrevInScript()
        {
            var code = """
                [||]void M1() { }

                $$void M2() { }
                """;

            await AssertNavigatedAsync(code, next: false, sourceCodeKind: SourceCodeKind.Script);
        }
    }
}
