﻿// 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.Collections.Immutable;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis.CodeActions;
using Microsoft.CodeAnalysis.CodeFixes;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.Diagnostics;
using Microsoft.CodeAnalysis.Test.Utilities;
using Roslyn.Test.Utilities;
using Xunit;
using Xunit.Abstractions;

namespace Microsoft.CodeAnalysis.Editor.CSharp.UnitTests.Diagnostics.SimplifyTypeNames
{
    public partial class BatchFixerTests : AbstractCSharpDiagnosticProviderBasedUserDiagnosticTest
    {
        public BatchFixerTests(ITestOutputHelper logger)
             : base(logger)
        {
        }

        internal override (DiagnosticAnalyzer, CodeFixProvider) CreateDiagnosticProviderAndFixer(Workspace workspace)
            => (new QualifyWithThisAnalyzer(), new QualifyWithThisFixer());

        [DiagnosticAnalyzer(LanguageNames.CSharp)]
        private class QualifyWithThisAnalyzer : DiagnosticAnalyzer
        {
            public static readonly DiagnosticDescriptor Descriptor = DescriptorFactory.CreateSimpleDescriptor("QualifyWithThis");

            public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics
            {
                get
                {
                    return ImmutableArray.Create(Descriptor);
                }
            }

            public override void Initialize(AnalysisContext context)
                => context.RegisterSyntaxNodeAction<SyntaxKind>(AnalyzeNode, SyntaxKind.IdentifierName);

            private static void AnalyzeNode(SyntaxNodeAnalysisContext context)
            {
                if (context.Node is SimpleNameSyntax node)
                {
                    var symbol = context.SemanticModel.GetSymbolInfo(node).Symbol;
                    if (symbol != null && symbol.Kind == SymbolKind.Field)
                    {
                        var diagnostic = Diagnostic.Create(Descriptor, node.GetLocation());
                        context.ReportDiagnostic(diagnostic);
                    }
                }
            }
        }

        private class QualifyWithThisFixer : CodeFixProvider
        {
            public override ImmutableArray<string> FixableDiagnosticIds
            {
                get
                {
                    return ImmutableArray.Create(QualifyWithThisAnalyzer.Descriptor.Id);
                }
            }

            public override async Task RegisterCodeFixesAsync(CodeFixContext context)
            {
                var root = await context.Document.GetSyntaxRootAsync(context.CancellationToken).ConfigureAwait(false);
                if (root.FindNode(context.Span, getInnermostNodeForTie: true) is SimpleNameSyntax node)
                {
                    var leadingTrivia = node.GetLeadingTrivia();
                    var newNode = SyntaxFactory.MemberAccessExpression(
                        SyntaxKind.SimpleMemberAccessExpression,
                        SyntaxFactory.ThisExpression(),
                        node.WithoutLeadingTrivia())
                        .WithLeadingTrivia(leadingTrivia);

                    var newRoot = root.ReplaceNode(node, newNode);
                    var newDocument = context.Document.WithSyntaxRoot(newRoot);

                    // Disable RS0005 as this is test code and we don't need telemetry for created code action.
#pragma warning disable RS0005 // Do not use generic CodeAction.Create to create CodeAction
                    var fix = CodeAction.Create("QualifyWithThisFix", _ => Task.FromResult(newDocument));
#pragma warning restore RS0005 // Do not use generic CodeAction.Create to create CodeAction

                    context.RegisterCodeFix(fix, context.Diagnostics);
                }
            }

            public override FixAllProvider GetFixAllProvider()
                => WellKnownFixAllProviders.BatchFixer;
        }

        #region "Fix all occurrences tests"

        [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/320")]
        [Trait(Traits.Feature, Traits.Features.CodeActionsFixAllOccurrences)]
        public async Task TestFixAllInDocument_QualifyWithThis()
        {
            var input = @"
<Workspace>
    <Project Language=""C#"" AssemblyName=""Assembly1"" CommonReferences=""true"">
        <Document>
class C
{
    int Sign;
    void F()
    {
        string x = @""namespace Namespace
    {
        class Type
        {
            void Goo()
            {
                int x = 1 "" + {|FixAllInDocument:Sign|} + @"" "" + Sign + @""3;
            }
        }
    }
"";
    }
}
        </Document>
    </Project>
</Workspace>";

            var expected = @"
<Workspace>
    <Project Language=""C#"" AssemblyName=""Assembly1"" CommonReferences=""true"">
        <Document>
class C
{
    int Sign;
    void F()
    {
        string x = @""namespace Namespace
    {
        class Type
        {
            void Goo()
            {
                int x = 1 "" + this.Sign + @"" "" + this.Sign + @""3;
            }
        }
    }
"";
    }
}
        </Document>
    </Project>
</Workspace>";

            await TestInRegularAndScriptAsync(input, expected);
        }

        #endregion
    }
}
