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

using System.Linq;
using System.Xml.Linq;
using Microsoft.CodeAnalysis.Editor.CSharp.CompleteStatement;
using Microsoft.CodeAnalysis.Editor.Shared.Options;
using Microsoft.CodeAnalysis.Editor.UnitTests;
using Microsoft.CodeAnalysis.Options;
using Microsoft.CodeAnalysis.Test.Utilities;
using Microsoft.VisualStudio.Commanding;
using Roslyn.Test.Utilities;
using Xunit;

namespace Microsoft.CodeAnalysis.Editor.CSharp.UnitTests.ConvertNamespace
{
    [UseExportProvider]
    public class ConvertNamespaceCommandHandlerTests
    {
        internal sealed class ConvertNamespaceTestState : AbstractCommandHandlerTestState
        {
            private static readonly TestComposition s_composition = EditorTestCompositions.EditorFeaturesWpf.AddParts(
                typeof(ConvertNamespaceCommandHandler));

            private readonly ConvertNamespaceCommandHandler _commandHandler;

            public ConvertNamespaceTestState(XElement workspaceElement)
                : base(workspaceElement, s_composition)
            {
                _commandHandler = (ConvertNamespaceCommandHandler)GetExportedValues<ICommandHandler>().
                    Single(c => c is ConvertNamespaceCommandHandler);
            }

            public static ConvertNamespaceTestState CreateTestState(string markup)
                => new(GetWorkspaceXml(markup));

            public static XElement GetWorkspaceXml(string markup)
                => XElement.Parse(string.Format("""
                    <Workspace>
                        <Project Language="C#" CommonReferences="true">
                            <Document>{0}</Document>
                        </Project>
                    </Workspace>
                    """, markup));

            internal void AssertCodeIs(string expectedCode)
            {
                MarkupTestFile.GetPosition(expectedCode, out var massaged, out int caretPosition);
                Assert.Equal(massaged, TextView.TextSnapshot.GetText());
                Assert.Equal(caretPosition, TextView.Caret.Position.BufferPosition.Position);
            }

            public void SendTypeChar(char ch)
                => SendTypeChar(ch, _commandHandler.ExecuteCommand, () => EditorOperations.InsertText(ch.ToString()));
        }

        [WpfFact]
        public void TestSingleName()
        {
            using var testState = ConvertNamespaceTestState.CreateTestState(
                """
                namespace N$$
                {
                    class C
                    {
                    }
                }
                """);

            testState.SendTypeChar(';');
            testState.AssertCodeIs(
                """
                namespace N;$$

                class C
                {
                }
                """);
        }

        [WpfFact]
        public void TestOptionOff()
        {
            using var testState = ConvertNamespaceTestState.CreateTestState(
                """
                namespace N$$
                {
                    class C
                    {
                    }
                }
                """);

            testState.Workspace.GlobalOptions.SetGlobalOption(CompleteStatementOptionsStorage.AutomaticallyCompleteStatementOnSemicolon, false);

            testState.SendTypeChar(';');
            testState.AssertCodeIs(
                """
                namespace N;$$
                {
                    class C
                    {
                    }
                }
                """);
        }

        [WpfFact]
        public void TestDottedName1()
        {
            using var testState = ConvertNamespaceTestState.CreateTestState(
                """
                namespace A.B$$
                {
                    class C
                    {
                    }
                }
                """);

            testState.SendTypeChar(';');
            testState.AssertCodeIs(
                """
                namespace A.B;$$

                class C
                {
                }
                """);
        }

        [WpfFact]
        public void TestDottedName2()
        {
            using var testState = ConvertNamespaceTestState.CreateTestState(
                """
                namespace A.$$B
                {
                    class C
                    {
                    }
                }
                """);

            testState.SendTypeChar(';');
            testState.AssertCodeIs(
                """
                namespace A.;$$B
                {
                    class C
                    {
                    }
                }
                """);
        }

        [WpfFact]
        public void TestDottedName3()
        {
            using var testState = ConvertNamespaceTestState.CreateTestState(
                """
                namespace A$$.B
                {
                    class C
                    {
                    }
                }
                """);

            testState.SendTypeChar(';');
            testState.AssertCodeIs(
                """
                namespace A;$$.B
                {
                    class C
                    {
                    }
                }
                """);
        }

        [WpfFact]
        public void TestDottedName4()
        {
            using var testState = ConvertNamespaceTestState.CreateTestState(
                """
                namespace $$A.B
                {
                    class C
                    {
                    }
                }
                """);

            testState.SendTypeChar(';');
            testState.AssertCodeIs(
                """
                namespace ;$$A.B
                {
                    class C
                    {
                    }
                }
                """);
        }

        [WpfFact]
        public void TestAfterWhitespace()
        {
            using var testState = ConvertNamespaceTestState.CreateTestState(
                """
                namespace A.B  $$
                {
                    class C
                    {
                    }
                }
                """);

            testState.SendTypeChar(';');
            testState.AssertCodeIs(
                """
                namespace A.B;$$  

                class C
                {
                }
                """);
        }

        [WpfFact]
        public void TestBeforeName()
        {
            using var testState = ConvertNamespaceTestState.CreateTestState(
                """
                namespace $$N
                {
                    class C
                    {
                    }
                }
                """);

            testState.SendTypeChar(';');
            testState.AssertCodeIs(
                """
                namespace ;$$N
                {
                    class C
                    {
                    }
                }
                """);
        }

        [WpfFact]
        public void TestNestedNamespace()
        {
            using var testState = ConvertNamespaceTestState.CreateTestState(
                """
                namespace N$$
                {
                    namespace N2
                    {
                        class C
                        {
                        }
                    }
                }
                """);

            testState.SendTypeChar(';');
            testState.AssertCodeIs(
                """
                namespace N;$$
                {
                    namespace N2
                    {
                        class C
                        {
                        }
                    }
                }
                """);
        }

        [WpfFact]
        public void TestSiblingNamespace()
        {
            using var testState = ConvertNamespaceTestState.CreateTestState(
                """
                namespace N$$
                {
                }

                namespace N2
                {
                    class C
                    {
                    }
                }
                """);

            testState.SendTypeChar(';');
            testState.AssertCodeIs(
                """
                namespace N;$$
                {
                }

                namespace N2
                {
                    class C
                    {
                    }
                }
                """);
        }

        [WpfFact]
        public void TestOuterUsings()
        {
            using var testState = ConvertNamespaceTestState.CreateTestState(
                """
                using A;
                using B;

                namespace N$$
                {
                    class C
                    {
                    }
                }
                """);

            testState.SendTypeChar(';');
            testState.AssertCodeIs(
                """
                using A;
                using B;

                namespace N;$$

                class C
                {
                }
                """);
        }

        [WpfFact]
        public void TestInnerUsings()
        {
            using var testState = ConvertNamespaceTestState.CreateTestState(
                """
                namespace N$$
                {
                    using A;
                    using B;

                    class C
                    {
                    }
                }
                """);

            testState.SendTypeChar(';');
            testState.AssertCodeIs(
                """
                namespace N;$$

                using A;
                using B;

                class C
                {
                }
                """);
        }

        [WpfFact]
        public void TestCommentAfterName()
        {
            using var testState = ConvertNamespaceTestState.CreateTestState(
                """
                namespace N$$ // Goo
                {
                    class C
                    {
                    }
                }
                """);

            testState.SendTypeChar(';');
            testState.AssertCodeIs(
                """
                namespace N;$$ // Goo

                class C
                {
                }
                """);
        }
    }
}
