text: ﻿using System.Linq;
  using System.Threading;
  using NUnit.Framework;
  using Rubberduck.CodeAnalysis.Inspections.Concrete;
  using Rubberduck.CodeAnalysis.QuickFixes;
  using Rubberduck.CodeAnalysis.QuickFixes.Concrete;
  using Rubberduck.Parsing.VBA;
  using Rubberduck.VBEditor.SafeComWrappers;
  using RubberduckTests.Mocks;
  
  namespace RubberduckTests.QuickFixes
  {
      [TestFixture]
      public class PassParameterByValueQuickFixTests : QuickFixTestBase
      {
          [Test]
          [Category("QuickFixes")]
          public void ParameterCanBeByVal_QuickFixWorks_SubNameStartsWithParamName()
          {
              const string inputCode =
                  @"Sub foo(f)
  End Sub";
  
              const string expectedCode =
                  @"Sub foo(ByVal f)
  End Sub";
  
              var actualCode = ApplyQuickFixToFirstInspectionResult(inputCode, state => new ParameterCanBeByValInspection(state));
              Assert.AreEqual(expectedCode, actualCode);
          }
  
          [Test]
          [Category("QuickFixes")]
          public void ParameterCanBeByVal_QuickFixWorks_PassedByUnspecified()
          {
              const string inputCode =
                  @"Sub Foo(arg1 As String)
  End Sub";
  
              const string expectedCode =
                  @"Sub Foo(ByVal arg1 As String)
  End Sub";
  
              var actualCode = ApplyQuickFixToFirstInspectionResult(inputCode, state => new ParameterCanBeByValInspection(state));
              Assert.AreEqual(expectedCode, actualCode);
          }
  
          [Test]
          [Category("QuickFixes")]
          public void ParameterCanBeByVal_QuickFixWorks_PassedByRef()
          {
              const string inputCode =
                  @"Sub Foo(ByRef arg1 As String)
  End Sub";
  
              const string expectedCode =
                  @"Sub Foo(ByVal arg1 As String)
  End Sub";
  
              var actualCode = ApplyQuickFixToFirstInspectionResult(inputCode, state => new ParameterCanBeByValInspection(state));
              Assert.AreEqual(expectedCode, actualCode);
          }
  
          [Test]
          [Category("QuickFixes")]
          public void ParameterCanBeByVal_QuickFixWorks_PassedByUnspecified_MultilineParameter()
          {
              const string inputCode =
                  @"Sub Foo( _
  arg1 As String)
  End Sub";
  
              const string expectedCode =
                  @"Sub Foo( _
  ByVal arg1 As String)
  End Sub";
  
              var actualCode = ApplyQuickFixToFirstInspectionResult(inputCode, state => new ParameterCanBeByValInspection(state));
              Assert.AreEqual(expectedCode, actualCode);
          }
  
          [Test]
          [Category("QuickFixes")]
          public void ParameterCanBeByVal_QuickFixWorks_PassedByRef_MultilineParameter()
          {
              const string inputCode =
                  @"Sub Foo(ByRef _
  arg1 As String)
  End Sub";
  
              const string expectedCode =
                  @"Sub Foo(ByVal _
  arg1 As String)
  End Sub";
  
              var actualCode = ApplyQuickFixToFirstInspectionResult(inputCode, state => new ParameterCanBeByValInspection(state));
              Assert.AreEqual(expectedCode, actualCode);
          }
  
          [Test]
          [Category("QuickFixes")]
          public void ParameterCanBeByVal_InterfaceMember_MultipleParams_OneCanBeByVal_QuickFixWorks()
          {
              //Input
              const string inputCode1 =
                  @"Public Sub DoSomething(ByRef a As Integer, ByRef b As Integer)
  End Sub";
              const string inputCode2 =
                  @"Implements IClass1
  
  Private Sub IClass1_DoSomething(ByRef a As Integer, ByRef b As Integer)
      b = 42
  End Sub";
              const string inputCode3 =
                  @"Implements IClass1
  
  Private Sub IClass1_DoSomething(ByRef a As Integer, ByRef b As Integer)
  End Sub";
  
              //Expected
              const string expectedCode1 =
                  @"Public Sub DoSomething(ByVal a As Integer, ByRef b As Integer)
  End Sub";
              const string expectedCode2 =
                  @"Implements IClass1
  
  Private Sub IClass1_DoSomething(ByVal a As Integer, ByRef b As Integer)
      b = 42
  End Sub";
              const string expectedCode3 =
                  @"Implements IClass1
  
  Private Sub IClass1_DoSomething(ByVal a As Integer, ByRef b As Integer)
  End Sub";
  
              var builder = new MockVbeBuilder();
              var project = builder.ProjectBuilder("TestProject1", ProjectProtection.Unprotected)
                  .AddComponent("IClass1", ComponentType.ClassModule, inputCode1)
                  .AddComponent("Class1", ComponentType.ClassModule, inputCode2)
                  .AddComponent("Class2", ComponentType.ClassModule, inputCode3)
                  .Build();
  
              var component1 = project.Object.VBComponents["IClass1"];
              var component2 = project.Object.VBComponents["Class1"];
              var component3 = project.Object.VBComponents["Class2"];
              var vbe = builder.AddProject(project).Build();
  
              var (state, rewriteManager) = MockParser.CreateAndParseWithRewritingManager(vbe.Object);
              using (state)
              {
  
                  var inspection = new ParameterCanBeByValInspection(state);
                  var rewriteSession = rewriteManager.CheckOutCodePaneSession();
  
                  new PassParameterByValueQuickFix(state).Fix(inspection.GetInspectionResults(CancellationToken.None).First(), rewriteSession);
  
                  var actualCode1 = rewriteSession.CheckOutModuleRewriter(component1.QualifiedModuleName).GetText();
                  var actualCode2 = rewriteSession.CheckOutModuleRewriter(component2.QualifiedModuleName).GetText();
                  var actualCode3 = rewriteSession.CheckOutModuleRewriter(component3.QualifiedModuleName).GetText();
  
                  Assert.AreEqual(expectedCode1, actualCode1);
                  Assert.AreEqual(expectedCode2, actualCode2);
                  Assert.AreEqual(expectedCode3, actualCode3);
              }
          }
  
          [Test]
          [Category("QuickFixes")]
          public void ParameterCanBeByVal_EventMember_MultipleParams_OneCanBeByVal_QuickFixWorks()
          {
              //Input
              const string inputCode1 =
                  @"Public Event Foo(ByRef a As Integer, ByRef b As Integer)";
              const string inputCode2 =
                  @"Private WithEvents abc As Class1
  
  Private Sub abc_Foo(ByRef a As Integer, ByRef b As Integer)
      a = 42
  End Sub";
              const string inputCode3 =
                  @"Private WithEvents abc As Class1
  
  Private Sub abc_Foo(ByRef a As Integer, ByRef b As Integer)
  End Sub";
  
              //Expected
              const string expectedCode1 =
                  @"Public Event Foo(ByRef a As Integer, ByVal b As Integer)";
              const string expectedCode2 =
                  @"Private WithEvents abc As Class1
  
  Private Sub abc_Foo(ByRef a As Integer, ByVal b As Integer)
      a = 42
  End Sub";
              const string expectedCode3 =
                  @"Private WithEvents abc As Class1
  
  Private Sub abc_Foo(ByRef a As Integer, ByVal b As Integer)
  End Sub";
  
              var builder = new MockVbeBuilder();
              var project = builder.ProjectBuilder("TestProject1", ProjectProtection.Unprotected)
                  .AddComponent("Class1", ComponentType.ClassModule, inputCode1)
                  .AddComponent("Class2", ComponentType.ClassModule, inputCode2)
                  .AddComponent("Class3", ComponentType.ClassModule, inputCode3)
                  .Build();
  
              var component1 = project.Object.VBComponents["Class1"];
              var component2 = project.Object.VBComponents["Class2"];
              var component3 = project.Object.VBComponents["Class3"];
              var vbe = builder.AddProject(project).Build();
  
              var (state, rewriteManager) = MockParser.CreateAndParseWithRewritingManager(vbe.Object);
              using (state)
              {
  
                  var inspection = new ParameterCanBeByValInspection(state);
                  var rewriteSession = rewriteManager.CheckOutCodePaneSession();
  
                  new PassParameterByValueQuickFix(state).Fix(inspection.GetInspectionResults(CancellationToken.None).First(), rewriteSession);
  
                  var actualCode1 = rewriteSession.CheckOutModuleRewriter(component1.QualifiedModuleName).GetText();
                  var actualCode2 = rewriteSession.CheckOutModuleRewriter(component2.QualifiedModuleName).GetText();
                  var actualCode3 = rewriteSession.CheckOutModuleRewriter(component3.QualifiedModuleName).GetText();
  
                  Assert.AreEqual(expectedCode1, actualCode1);
                  Assert.AreEqual(expectedCode2, actualCode2);
                  Assert.AreEqual(expectedCode3, actualCode3);
              }
          }
  
          //https://github.com/rubberduck-vba/Rubberduck/issues/2408
          [Test]
          [Category("QuickFixes")]
          public void ParameterCanBeByVal_QuickFixWithOptionalWorks()
          {
              const string inputCode =
                  @"Sub Test(Optional foo As String = ""bar"")
      Debug.Print foo
  End Sub";
  
              const string expectedCode =
                  @"Sub Test(Optional ByVal foo As String = ""bar"")
      Debug.Print foo
  End Sub";
  
              var actualCode = ApplyQuickFixToFirstInspectionResult(inputCode, state => new ParameterCanBeByValInspection(state));
              Assert.AreEqual(expectedCode, actualCode);
          }
  
          //https://github.com/rubberduck-vba/Rubberduck/issues/2408
          [Test]
          [Category("QuickFixes")]
          public void ParameterCanBeByVal_QuickFixWithOptionalByRefWorks()
          {
              const string inputCode =
                  @"Sub Test(Optional ByRef foo As String = ""bar"")
      Debug.Print foo
  End Sub";
  
              const string expectedCode =
                  @"Sub Test(Optional ByVal foo As String = ""bar"")
      Debug.Print foo
  End Sub";
  
              var actualCode = ApplyQuickFixToFirstInspectionResult(inputCode, state => new ParameterCanBeByValInspection(state));
              Assert.AreEqual(expectedCode, actualCode);
          }
  
          //https://github.com/rubberduck-vba/Rubberduck/issues/2408
          [Test]
          [Category("QuickFixes")]
          public void ParameterCanBeByVal_QuickFixWithOptional_LineContinuationsWorks()
          {
              const string inputCode =
                  @"Sub foo(Optional _
    ByRef _
    foo _
    As _
    Byte _
    )
    Debug.Print foo
  End Sub";
  
              const string expectedCode =
                  @"Sub foo(Optional _
    ByVal _
    foo _
    As _
    Byte _
    )
    Debug.Print foo
  End Sub";
  
              var actualCode = ApplyQuickFixToFirstInspectionResult(inputCode, state => new ParameterCanBeByValInspection(state));
              Assert.AreEqual(expectedCode, actualCode);
          }
  
  
          protected override IQuickFix QuickFix(RubberduckParserState state)
          {
              return new PassParameterByValueQuickFix(state);
          }
      }
  }
  