// 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.CodeFixes;
using Microsoft.CodeAnalysis.CodeStyle;
using Microsoft.CodeAnalysis.CSharp.SimplifyThisOrMe;
using Microsoft.CodeAnalysis.Diagnostics;
using Microsoft.CodeAnalysis.Editor.CSharp.UnitTests.Diagnostics;
using Microsoft.CodeAnalysis.Editor.UnitTests.CodeActions;
using Microsoft.CodeAnalysis.Test.Utilities;
using Roslyn.Test.Utilities;
using Xunit;
using Xunit.Abstractions;

namespace Microsoft.CodeAnalysis.Editor.CSharp.UnitTests.SimplifyThisOrMe;

public sealed partial class SimplifyThisOrMeTests : AbstractCSharpDiagnosticProviderBasedUserDiagnosticTest_NoEditor
{
    public SimplifyThisOrMeTests(ITestOutputHelper logger)
        : base(logger)
    {
    }

    internal override (DiagnosticAnalyzer, CodeFixProvider) CreateDiagnosticProviderAndFixer(Workspace workspace)
        => (new CSharpSimplifyThisOrMeDiagnosticAnalyzer(), new CSharpSimplifyThisOrMeCodeFixProvider());

    [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsSimplifyThisOrMe)]
    public Task TestSimplifyDiagnosticId()
        => TestInRegularAndScriptAsync(
            """
            using System;

            class C
            {
                private int x = 0;
                public void z()
                {
                    var a = [|this.x|];
                }
            }
            """,
            """
            using System;

            class C
            {
                private int x = 0;
                public void z()
                {
                    var a = x;
                }
            }
            """);

    [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsSimplifyThisOrMe)]
    [WorkItem("https://github.com/dotnet/roslyn/issues/6682")]
    public Task TestThisWithNoType()
        => TestInRegularAndScriptAsync(
            """
            class Program
            {
                dynamic x = 7;

                static void Main(string[] args)
                {
                    [|this|].x = default(dynamic);
                }
            }
            """,
            """
            class Program
            {
                dynamic x = 7;

                static void Main(string[] args)
                {
                    x = default(dynamic);
                }
            }
            """);

    [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsSimplifyThisOrMe)]
    public Task TestAppropriateDiagnosticOnMissingQualifier()
        => TestDiagnosticInfoAsync(
            """
            class C
            {
                int SomeProperty { get; set; }

                void M()
                {
                    [|this|].SomeProperty = 1;
                }
            }
            """,
            options: Option(CodeStyleOptions2.QualifyPropertyAccess, false, NotificationOption2.Warning),
            diagnosticId: IDEDiagnosticIds.RemoveThisOrMeQualificationDiagnosticId,
            diagnosticSeverity: DiagnosticSeverity.Warning);

    [Fact]
    [Trait(Traits.Feature, Traits.Features.CodeActionsSimplifyThisOrMe)]
    [Trait(Traits.Feature, Traits.Features.CodeActionsFixAllOccurrences)]
    public Task TestFixAllInSolution_RemoveThis()
        => TestInRegularAndScriptAsync("""
            <Workspace>
                <Project Language="C#" AssemblyName="Assembly1" CommonReferences="true">
                    <Document>
            using System;
            class ProgramA
            {
                private int x = 0;
                private int y = 0;
                private int z = 0;

                private System.Int32 F(System.Int32 p1, System.Int16 p2)
                {
                    System.Int32 i1 = {|FixAllInSolution:this.x|};
                    System.Int16 s1 = this.y;
                    System.Int32 i2 = this.z;
                    System.Console.Write(i1 + s1 + i2);
                    System.Console.WriteLine(i1 + s1 + i2);
                    System.Exception ex = null;
                    return i1 + s1 + i2;
                }
            }

            class ProgramB
            {
                private int x2 = 0;
                private int y2 = 0;
                private int z2 = 0;

                private System.Int32 F(System.Int32 p1, System.Int16 p2)
                {
                    System.Int32 i1 = this.x2;
                    System.Int16 s1 = this.y2;
                    System.Int32 i2 = this.z2;
                    System.Console.Write(i1 + s1 + i2);
                    System.Console.WriteLine(i1 + s1 + i2);
                    System.Exception ex = null;
                    return i1 + s1 + i2;
                }
            }
                    </Document>
                    <Document>
            using System;
            class ProgramA2
            {
                private int x = 0;
                private int y = 0;
                private int z = 0;

                private System.Int32 F(System.Int32 p1, System.Int16 p2)
                {
                    System.Int32 i1 = this.x;
                    System.Int16 s1 = this.y;
                    System.Int32 i2 = this.z;
                    System.Console.Write(i1 + s1 + i2);
                    System.Console.WriteLine(i1 + s1 + i2);
                    System.Exception ex = null;
                    return i1 + s1 + i2;
                }
            }

            class ProgramB2
            {
                private int x2 = 0;
                private int y2 = 0;
                private int z2 = 0;

                private System.Int32 F(System.Int32 p1, System.Int16 p2)
                {
                    System.Int32 i1 = this.x2;
                    System.Int16 s1 = this.y2;
                    System.Int32 i2 = this.z2;
                    System.Console.Write(i1 + s1 + i2);
                    System.Console.WriteLine(i1 + s1 + i2);
                    System.Exception ex = null;
                    return i1 + s1 + i2;
                }
            }
                    </Document>
                </Project>
                <Project Language="C#" AssemblyName="Assembly2" CommonReferences="true">
                    <Document>
            using System;
            class ProgramA3
            {
                private int x = 0;
                private int y = 0;
                private int z = 0;

                private System.Int32 F(System.Int32 p1, System.Int16 p2)
                {
                    System.Int32 i1 = this.x;
                    System.Int16 s1 = this.y;
                    System.Int32 i2 = this.z;
                    System.Console.Write(i1 + s1 + i2);
                    System.Console.WriteLine(i1 + s1 + i2);
                    System.Exception ex = null;
                    return i1 + s1 + i2;
                }
            }

            class ProgramB3
            {
                private int x2 = 0;
                private int y2 = 0;
                private int z2 = 0;

                private System.Int32 F(System.Int32 p1, System.Int16 p2)
                {
                    System.Int32 i1 = this.x2;
                    System.Int16 s1 = this.y2;
                    System.Int32 i2 = this.z2;
                    System.Console.Write(i1 + s1 + i2);
                    System.Console.WriteLine(i1 + s1 + i2);
                    System.Exception ex = null;
                    return i1 + s1 + i2;
                }
            }
                    </Document>
                </Project>
            </Workspace>
            """, """
            <Workspace>
                <Project Language="C#" AssemblyName="Assembly1" CommonReferences="true">
                    <Document>
            using System;
            class ProgramA
            {
                private int x = 0;
                private int y = 0;
                private int z = 0;

                private System.Int32 F(System.Int32 p1, System.Int16 p2)
                {
                    System.Int32 i1 = x;
                    System.Int16 s1 = y;
                    System.Int32 i2 = z;
                    System.Console.Write(i1 + s1 + i2);
                    System.Console.WriteLine(i1 + s1 + i2);
                    System.Exception ex = null;
                    return i1 + s1 + i2;
                }
            }

            class ProgramB
            {
                private int x2 = 0;
                private int y2 = 0;
                private int z2 = 0;

                private System.Int32 F(System.Int32 p1, System.Int16 p2)
                {
                    System.Int32 i1 = x2;
                    System.Int16 s1 = y2;
                    System.Int32 i2 = z2;
                    System.Console.Write(i1 + s1 + i2);
                    System.Console.WriteLine(i1 + s1 + i2);
                    System.Exception ex = null;
                    return i1 + s1 + i2;
                }
            }
                    </Document>
                    <Document>
            using System;
            class ProgramA2
            {
                private int x = 0;
                private int y = 0;
                private int z = 0;

                private System.Int32 F(System.Int32 p1, System.Int16 p2)
                {
                    System.Int32 i1 = x;
                    System.Int16 s1 = y;
                    System.Int32 i2 = z;
                    System.Console.Write(i1 + s1 + i2);
                    System.Console.WriteLine(i1 + s1 + i2);
                    System.Exception ex = null;
                    return i1 + s1 + i2;
                }
            }

            class ProgramB2
            {
                private int x2 = 0;
                private int y2 = 0;
                private int z2 = 0;

                private System.Int32 F(System.Int32 p1, System.Int16 p2)
                {
                    System.Int32 i1 = x2;
                    System.Int16 s1 = y2;
                    System.Int32 i2 = z2;
                    System.Console.Write(i1 + s1 + i2);
                    System.Console.WriteLine(i1 + s1 + i2);
                    System.Exception ex = null;
                    return i1 + s1 + i2;
                }
            }
                    </Document>
                </Project>
                <Project Language="C#" AssemblyName="Assembly2" CommonReferences="true">
                    <Document>
            using System;
            class ProgramA3
            {
                private int x = 0;
                private int y = 0;
                private int z = 0;

                private System.Int32 F(System.Int32 p1, System.Int16 p2)
                {
                    System.Int32 i1 = x;
                    System.Int16 s1 = y;
                    System.Int32 i2 = z;
                    System.Console.Write(i1 + s1 + i2);
                    System.Console.WriteLine(i1 + s1 + i2);
                    System.Exception ex = null;
                    return i1 + s1 + i2;
                }
            }

            class ProgramB3
            {
                private int x2 = 0;
                private int y2 = 0;
                private int z2 = 0;

                private System.Int32 F(System.Int32 p1, System.Int16 p2)
                {
                    System.Int32 i1 = x2;
                    System.Int16 s1 = y2;
                    System.Int32 i2 = z2;
                    System.Console.Write(i1 + s1 + i2);
                    System.Console.WriteLine(i1 + s1 + i2);
                    System.Exception ex = null;
                    return i1 + s1 + i2;
                }
            }
                    </Document>
                </Project>
            </Workspace>
            """);

    [Fact]
    [Trait(Traits.Feature, Traits.Features.CodeActionsSimplifyThisOrMe)]
    [Trait(Traits.Feature, Traits.Features.CodeActionsFixAllOccurrences)]
    public async Task TestFixAllInSolution_RemoveMemberAccessQualification()
    {
        var options =
            new OptionsCollection(GetLanguage())
            {
                { CodeStyleOptions2.QualifyPropertyAccess, false, NotificationOption2.Suggestion },
                { CodeStyleOptions2.QualifyFieldAccess, true, NotificationOption2.Suggestion },
            };

        await TestInRegularAndScriptAsync(
            initialMarkup: """
            <Workspace>
                <Project Language="C#" AssemblyName="Assembly1" CommonReferences="true">
                    <Document>
            using System;

            class C
            {
                int Property { get; set; }
                int OtherProperty { get; set; }

                void M()
                {
                    {|FixAllInSolution:this.Property|} = 1;
                    var x = this.OtherProperty;
                }
            }
                    </Document>
                    <Document>
            using System;

            class D
            {
                string StringProperty { get; set; }
                int field;

                void N()
                {
                    this.StringProperty = string.Empty;
                    this.field = 0; // ensure qualification isn't removed
                }
            }
                    </Document>
                </Project>
            </Workspace>
            """,
            expectedMarkup: """
            <Workspace>
                <Project Language="C#" AssemblyName="Assembly1" CommonReferences="true">
                    <Document>
            using System;

            class C
            {
                int Property { get; set; }
                int OtherProperty { get; set; }

                void M()
                {
                    Property = 1;
                    var x = OtherProperty;
                }
            }
                    </Document>
                    <Document>
            using System;

            class D
            {
                string StringProperty { get; set; }
                int field;

                void N()
                {
                    StringProperty = string.Empty;
                    this.field = 0; // ensure qualification isn't removed
                }
            }
                    </Document>
                </Project>
            </Workspace>
            """,
            new(options: options));
    }
}
