using System.Threading.Tasks;
using Microsoft.CodeAnalysis.Testing;
using NUnit.Framework;
using VerifyCS =
    Microsoft.CodeAnalysis.CSharp.Testing.CSharpAnalyzerVerifier<Robust.Analyzers.PrototypeAnalyzer, Microsoft.CodeAnalysis.Testing.DefaultVerifier>;

namespace Robust.Analyzers.Tests;

[Parallelizable(ParallelScope.All | ParallelScope.Fixtures)]
[TestFixture]
[TestOf(typeof(PrototypeAnalyzer))]
public sealed class PrototypeAnalyzerTest
{
    private static Task Verifier(string code, params DiagnosticResult[] expected)
    {
        var test = new RTAnalyzerTest<PrototypeAnalyzer>()
        {
            TestState =
            {
                Sources = { code }
            },
        };

        TestHelper.AddEmbeddedSources(
            test.TestState,
            "Robust.Shared.Prototypes.Attributes.cs",
            "Robust.Shared.Prototypes.IPrototype.cs",
            "Robust.Shared.Serialization.Manager.Attributes.DataFieldAttribute.cs"
        );

        // ExpectedDiagnostics cannot be set, so we need to AddRange here...
        test.TestState.ExpectedDiagnostics.AddRange(expected);

        return test.RunAsync();
    }

    [Test]
    public async Task RedundantTypeTest()
    {
        const string code = """
            using Robust.Shared.Prototypes;

            [Prototype]
            public sealed partial class GoodAutoPrototype : IPrototype
            {
                [IdDataField]
                public string ID { get; private set; } = default!;
            }

            [Prototype("someOtherName")]
            public sealed partial class GoodUnmatchedPrototype : IPrototype
            {
                [IdDataField]
                public string ID { get; private set; } = default!;
            }

            [Prototype("badMatched")]
            public sealed partial class BadMatchedPrototype : IPrototype
            {
                [IdDataField]
                public string ID { get; private set; } = default!;
            }

            [Prototype(ProtoName)]
            public sealed partial class GoodNonLiteralMatchedPrototype : IPrototype
            {
                public const string ProtoName = "goodNonLiteralMatched";

                [IdDataField]
                public string ID { get; private set; } = default!;
            }

            [Prototype(ProtoName)]
            public sealed partial class GoodNonLiteralUnmatchedPrototype : IPrototype
            {
                public const string ProtoName = "someOtherNameEntirely";

                [IdDataField]
                public string ID { get; private set; } = default!;
            }

            [Prototype("goodDoesNotEndWithPrototypeWord")]
            public sealed partial class GoodDoesNotEndWithPrototypeWord : IPrototype
            {
                [IdDataField]
                public string ID { get; private set; } = default!;
            }
            """;

        await Verifier(code,
            // /0/Test0.cs(9,2): warning RA0033: Prototype BadMatchedPrototype has explicitly set type "badMatched" that matches autogenerated value
            VerifyCS.Diagnostic(PrototypeAnalyzer.PrototypeRedundantTypeRule).WithSpan(17, 12, 17, 24).WithArguments("BadMatchedPrototype", "badMatched")
        );
    }

    [Test]
    public async Task AliasTest()
    {
        const string code = """
            using Robust.Shared.Prototypes;
            using PPrototypeAttribute = Robust.Shared.Prototypes.PrototypeAttribute;

            [PPrototype("badMatched")]
            public sealed partial class BadMatchedPrototype : IPrototype
            {
                [IdDataField]
                public string ID { get; private set; } = default!;
            }
            """;

        await Verifier(code,
            // /0/Test0.cs(4,2): warning RA0042: Prototype BadMatchedPrototype has explicitly set type "badMatched" that matches autogenerated value
            VerifyCS.Diagnostic(PrototypeAnalyzer.PrototypeRedundantTypeRule).WithSpan(4, 13, 4, 25).WithArguments("BadMatchedPrototype", "badMatched")
        );
    }


    [Test]
    public async Task MoreAttributesTest()
    {
        const string code = """
            using System;
            using Robust.Shared.Prototypes;
            using PPrototypeAttribute = Robust.Shared.Prototypes.PrototypeAttribute;

            [FooBarAttribute]
            [PPrototype("badMatched")]
            public sealed partial class BadMatchedPrototype : IPrototype
            {
                [IdDataField]
                public string ID { get; private set; } = default!;
            }

            [AttributeUsage(AttributeTargets.Class, Inherited = false)]
            public sealed class FooBarAttribute : Attribute;
            """;

        await Verifier(code,
            // /0/Test0.cs(4,2): warning RA0042: Prototype BadMatchedPrototype has explicitly set type "badMatched" that matches autogenerated value
            VerifyCS.Diagnostic(PrototypeAnalyzer.PrototypeRedundantTypeRule).WithSpan(6, 13, 6, 25).WithArguments("BadMatchedPrototype", "badMatched")
        );
    }

    [Test]
    public async Task NameEndsWithPrototypeTest()
    {
        const string code = """
            using Robust.Shared.Prototypes;

            [Prototype]
            public sealed partial class GoodAutoPrototype : IPrototype
            {
                [IdDataField]
                public string ID { get; private set; } = default!;
            }

            [Prototype("ThisIsFine")]
            public sealed partial class GoodManual : IPrototype
            {
                [IdDataField]
                public string ID { get; private set; } = default!;
            }

            [Prototype]
            public sealed partial class BadAuto : IPrototype
            {
                [IdDataField]
                public string ID { get; private set; } = default!;
            }
            """;

        await Verifier(code,
            // /0/Test0.cs(18,29): error RA0043: Prototype BadAuto does not end with the word Prototype
            VerifyCS.Diagnostic(PrototypeAnalyzer.PrototypeEndsWithPrototypeRule).WithSpan(18, 29, 18, 36).WithArguments("BadAuto")
        );
    }
}
