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

namespace Microsoft.CodeAnalysis.Editor.CSharp.UnitTests.ImplementInterface
{
    [Trait(Traits.Feature, Traits.Features.CodeActionsImplementInterface)]
    public class ImplementImplicitlyTests : AbstractCSharpCodeActionTest
    {
        private const int SingleMember = 0;
        private const int SameInterface = 1;
        private const int AllInterfaces = 2;

        protected override CodeRefactoringProvider CreateCodeRefactoringProvider(Workspace workspace, TestParameters parameters)
            => new CSharpImplementImplicitlyCodeRefactoringProvider();

        protected override ImmutableArray<CodeAction> MassageActions(ImmutableArray<CodeAction> actions)
            => FlattenActions(actions);

        [Fact]
        public async Task TestSingleMember()
        {
            await TestInRegularAndScriptAsync(
                """
                interface IGoo { void Goo1(); void Goo2(); }
                interface IBar { void Bar(); }

                class C : IGoo, IBar
                {
                    void IGoo.[||]Goo1() { }

                    void IGoo.Goo2() { }

                    void IBar.Bar() { }
                }
                """,
                """
                interface IGoo { void Goo1(); void Goo2(); }
                interface IBar { void Bar(); }

                class C : IGoo, IBar
                {
                    public void Goo1() { }

                    void IGoo.Goo2() { }

                    void IBar.Bar() { }
                }
                """, index: SingleMember);
        }

        [Fact]
        public async Task TestSameInterface()
        {
            await TestInRegularAndScriptAsync(
                """
                interface IGoo { void Goo1(); void Goo2(); }
                interface IBar { void Bar(); }

                class C : IGoo, IBar
                {
                    void IGoo.[||]Goo1() { }

                    void IGoo.Goo2() { }

                    void IBar.Bar() { }
                }
                """,
                """
                interface IGoo { void Goo1(); void Goo2(); }
                interface IBar { void Bar(); }

                class C : IGoo, IBar
                {
                    public void Goo1() { }

                    public void Goo2() { }

                    void IBar.Bar() { }
                }
                """, index: SameInterface);
        }

        [Fact]
        public async Task TestAllInterfaces()
        {
            await TestInRegularAndScriptAsync(
                """
                interface IGoo { void Goo1(); void Goo2(); }
                interface IBar { void Bar(); }

                class C : IGoo, IBar
                {
                    void IGoo.[||]Goo1() { }

                    void IGoo.Goo2() { }

                    void IBar.Bar() { }
                }
                """,
                """
                interface IGoo { void Goo1(); void Goo2(); }
                interface IBar { void Bar(); }

                class C : IGoo, IBar
                {
                    public void Goo1() { }

                    public void Goo2() { }

                    public void Bar() { }
                }
                """, index: AllInterfaces);
        }

        [Fact]
        public async Task TestProperty()
        {
            await TestInRegularAndScriptAsync(
                """
                interface IGoo { int Goo1 { get; } }

                class C : IGoo
                {
                    int IGoo.[||]Goo1 { get { } }
                }
                """,
                """
                interface IGoo { int Goo1 { get; } }

                class C : IGoo
                {
                    public int Goo1 { get { } }
                }
                """, index: SingleMember);
        }

        [Fact]
        public async Task TestEvent()
        {
            await TestInRegularAndScriptAsync(
                """
                interface IGoo { event Action E; }

                class C : IGoo
                {
                    event Action IGoo.[||]E { add { } remove { } }
                }
                """,
                """
                interface IGoo { event Action E; }

                class C : IGoo
                {
                    public event Action E { add { } remove { } }
                }
                """, index: SingleMember);
        }

        [Fact]
        public async Task TestNotOnImplicitMember()
        {
            await TestMissingAsync(
                """
                interface IGoo { void Goo1(); }

                class C : IGoo
                {
                    public void [||]Goo1() { }
                }
                """);
        }

        [Fact]
        public async Task TestNotOnUnboundExplicitImpl()
        {
            await TestMissingAsync(
                """
                class C : IGoo
                {
                    void IGoo.[||]Goo1() { }
                }
                """);
        }

        [Fact]
        public async Task TestCollision()
        {
            // Currently we don't do anything special here.  But we just test here to make sure we
            // don't blow up here.
            await TestInRegularAndScriptAsync(
                """
                interface IGoo { void Goo1(); }

                class C : IGoo
                {
                    void IGoo.[||]Goo1() { }

                    private void Goo1() { }
                }
                """,
                """
                interface IGoo { void Goo1(); }

                class C : IGoo
                {
                    public void Goo1() { }

                    private void Goo1() { }
                }
                """, index: SingleMember);
        }

        [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/48027")]
        public async Task TestSingleMemberAndContainingTypeHasNoInterface()
        {
            await TestMissingAsync(
                """
                using System;
                using System.Collections;

                class C
                {
                    IEnumerator IEnumerable.[||]GetEnumerator()
                    {
                        throw new NotImplementedException();
                    }
                }
                """);
        }

        [Fact]
        public async Task TestPreserveReadOnly()
        {
            await TestInRegularAndScriptAsync(
                """
                interface IGoo { void Goo1(); }

                class C : IGoo
                {
                    readonly void IGoo.[||]Goo1() { }
                }
                """,
                """
                interface IGoo { void Goo1(); }

                class C : IGoo
                {
                    public readonly void Goo1() { }
                }
                """);
        }
    }
}
