﻿// 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.Editor.UnitTests.CodeLens;
using Microsoft.CodeAnalysis.Test.Utilities;
using Roslyn.Test.Utilities;
using Xunit;

namespace Microsoft.CodeAnalysis.Editor.CSharp.UnitTests.CodeLens
{
    [Trait(Traits.Feature, Traits.Features.CodeLens)]
    public sealed class CSharpCodeLensTests : AbstractCodeLensTest
    {
        [Fact]
        public async Task TestCount()
        {
            const string input = @"<Workspace>
    <Project Language=""C#"" CommonReferences=""true"" AssemblyName=""Proj1"">
        <Document FilePath=""CurrentDocument.cs""><![CDATA[
public class A
{
    {|0: public void B()
    {
        C();
    }|}

    {|2: public void C()
    {
        D();
    }|}

    {|1: public void D()
    {
        C();
    }|}
}
]]>
        </Document>
    </Project>
</Workspace>";
            await RunCountTest(input);
        }

        [Fact]
        public async Task TestCapping()
        {
            const string input = @"<Workspace>
    <Project Language=""C#"" CommonReferences=""true"" AssemblyName=""Proj1"">
        <Document FilePath=""CurrentDocument.cs""><![CDATA[
public class A
{
    {|0: public void B()
    {
        C();
    }|}

    {|capped1: public void C()
    {
        D();
    }|}

    {|1: public void D()
    {
        C();
    }|}
}
]]>
        </Document>
    </Project>
</Workspace>";

            await RunCountTest(input, 1);
        }

        [Fact]
        public async Task TestLinkedFiles()
        {
            const string input = @"<Workspace>
    <Project Language=""C#"" CommonReferences=""true"" AssemblyName=""Proj1"">
        <Document FilePath=""CurrentDocument.cs""><![CDATA[
public class A
{
    {|0: public void B()
    {
        C();
    }|}

    {|3: public void C()
    {
        D();
    }|}

    {|3: public void D()
    {
        C();
    }|}
}
]]>
        </Document>
    </Project>
    <Project Language=""C#"" CommonReferences=""true"" AssemblyName=""Proj2"">
        <Document IsLinkFile=""true"" LinkAssemblyName=""Proj1"" LinkFilePath=""CurrentDocument.cs""/>
        <Document FilePath=""AdditionalDocument.cs""><![CDATA[
class E
{
    void F()
    {
        A.C();
        A.D();
        A.D();
    }
}
]]>
        </Document>
    </Project>
</Workspace>";

            await RunReferenceTest(input);
        }

        [Fact]
        public async Task TestDisplay()
        {
            const string input = @"<Workspace>
    <Project Language=""C#"" CommonReferences=""true"" AssemblyName=""Proj1"">
        <Document FilePath=""CurrentDocument.cs""><![CDATA[
public class A
{
    {|0: public void B()
    {
        C();
    }|}

    {|2: public void C()
    {
        D();
    }|}

    {|1: public void D()
    {
        C();
    }|}
}
]]>
        </Document>
    </Project>
</Workspace>";

            await RunReferenceTest(input);
        }

        [Fact]
        public async Task TestMethodReferences()
        {
            const string input = @"<Workspace>
    <Project Language=""C#"" CommonReferences=""true"" AssemblyName=""Proj1"">
        <Document FilePath=""CurrentDocument.cs""><![CDATA[
public class A
{
    {|0: public void B()
    {
        C();
    }|}

    {|2: public void C()
    {
        D();
    }|}

    {|1: public void D()
    {
        C();
    }|}
}
]]>
        </Document>
    </Project>
</Workspace>";
            await RunMethodReferenceTest(input);
        }

        [Fact]
        public async Task TestMethodReferencesWithDocstrings()
        {
            const string input = @"<Workspace>
    <Project Language=""C#"" CommonReferences=""true"" AssemblyName=""Proj1"">
        <Document FilePath=""CurrentDocument.cs""><![CDATA[
public class A
{
    /// <summary>
    ///     <see cref=""A.C""/>
    /// </summary>
    {|0: public void B()
    {
        C();
    }|}

    {|2: public void C()
    {
        D();
    }|}

    {|1: public void D()
    {
        C();
    }|}
}
]]>
        </Document>
    </Project>
</Workspace>";
            await RunMethodReferenceTest(input);
        }

        [Theory]
        [InlineData("class")]
        [InlineData("record class")]
        [InlineData("record struct")]
        public async Task TestFullyQualifiedName(string typeKind)
        {
            var input = $@"<Workspace>
    <Project Language=""C#"" CommonReferences=""true"" AssemblyName=""Proj1"">
        <Document FilePath=""CurrentDocument.cs""><![CDATA[
public {typeKind} A
{{
    {{|A.C: public void C()
    {{
        C();
    }}|}}

    public {typeKind} B
    {{
        {{|A+B.C: public void C()
        {{
            C();
        }}|}}

        public {typeKind} D
        {{
            {{|A+B+D.C: public void C()
            {{
                C();
            }}|}}
        }}
    }}
}}
]]>
        </Document>
    </Project>
</Workspace>";
            await RunFullyQualifiedNameTest(input);
        }

        [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/49636")]
        public async Task TestExplicitParameterlessConstructor()
        {
            const string input = @"<Workspace>
    <Project Language=""C#"" CommonReferences=""true"" AssemblyName=""Proj1"">
        <Document FilePath=""CurrentDocument.cs""><![CDATA[
{|2:public class Foo|}
{
    public Foo() { }
}
public class B
{
    private void Test()
    {
        var foo = new Foo();
    }
}
]]>
        </Document>
    </Project>
</Workspace>";
            await RunReferenceTest(input);
        }

        [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/49636")]
        public async Task TestExplicitParameterlessConstructor_TwoCalls()
        {
            const string input = @"<Workspace>
    <Project Language=""C#"" CommonReferences=""true"" AssemblyName=""Proj1"">
        <Document FilePath=""CurrentDocument.cs""><![CDATA[
{|3:public class Foo|}
{
    public Foo() { }
}
public class B
{
    private void Test()
    {
        var foo1 = new Foo();
        var foo2 = new Foo();
    }
}
]]>
        </Document>
    </Project>
</Workspace>";
            await RunReferenceTest(input);
        }

        [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/49636")]
        public async Task TestImplicitParameterlessConstructor()
        {
            const string input = @"<Workspace>
    <Project Language=""C#"" CommonReferences=""true"" AssemblyName=""Proj1"">
        <Document FilePath=""CurrentDocument.cs""><![CDATA[
{|1:public class Foo|}
{
}
public class B
{
    private void Test()
    {
        var foo = new Foo();
    }
}
]]>
        </Document>
    </Project>
</Workspace>";
            await RunReferenceTest(input);
        }

        [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/49636")]
        public async Task TestImplicitParameterlessConstructor_TwoCalls()
        {
            const string input = @"<Workspace>
    <Project Language=""C#"" CommonReferences=""true"" AssemblyName=""Proj1"">
        <Document FilePath=""CurrentDocument.cs""><![CDATA[
{|2:public class Foo|}
{
}
public class B
{
    private void Test()
    {
        var foo1 = new Foo();
        var foo2 = new Foo();
    }
}
]]>
        </Document>
    </Project>
</Workspace>";
            await RunReferenceTest(input);
        }

        [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/51633")]
        public async Task TestMethodRefSourceGeneratedDocument()
        {
            const string input = @"<Workspace>
    <Project Language=""C#"" CommonReferences=""true"" AssemblyName=""Proj1"">
        <Document FilePath=""Program.cs""><![CDATA[
namespace ConsoleSample
{
    class Program
    {
        {|1:public Program()
        {
        }|}
    }
}]]>
        </Document>
        <DocumentFromSourceGenerator><![CDATA[
namespace ConsoleSample
{
    internal partial class Program
    {
        public static CreateProgram() => new Program();
    }
}]]>
        </DocumentFromSourceGenerator>
    </Project>
</Workspace>";
            await RunMethodReferenceTest(input);
        }
    }
}
