// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using System;
using System.Globalization;
using Xunit;
using Xunit.Abstractions;

namespace Microsoft.AspNetCore.Testing
{
    [TestCaseOrderer("Microsoft.AspNetCore.Testing.AlphabeticalOrderer", "Microsoft.AspNetCore.Testing.Tests")]
    public class ConditionalTheoryTest : IClassFixture<ConditionalTheoryTest.ConditionalTheoryAsserter>
    {
        public ConditionalTheoryTest(ConditionalTheoryAsserter asserter)
        {
            Asserter = asserter;
        }

        public ConditionalTheoryAsserter Asserter { get; }

        [ConditionalTheory(Skip = "Test is always skipped.")]
        [InlineData(0)]
        public void ConditionalTheorySkip(int arg)
        {
            Assert.True(false, "This test should always be skipped.");
        }

        private static int _conditionalTheoryRuns = 0;

        [ConditionalTheory]
        [InlineData(0)]
        [InlineData(1)]
        [InlineData(2, Skip = "Skip these data")]
        public void ConditionalTheoryRunOncePerDataLine(int arg)
        {
            _conditionalTheoryRuns++;
            Assert.True(_conditionalTheoryRuns <= 2, $"Theory should run 2 times, but ran {_conditionalTheoryRuns} times.");
        }

        [ConditionalTheory, Trait("Color", "Blue")]
        [InlineData(1)]
        public void ConditionalTheoriesShouldPreserveTraits(int arg)
        {
            Assert.True(true);
        }

        [ConditionalTheory(Skip = "Skip this")]
        [MemberData(nameof(GetInts))]
        public void ConditionalTheoriesWithSkippedMemberData(int arg)
        {
            Assert.True(false, "This should never run");
        }

        private static int _conditionalMemberDataRuns = 0;

        [ConditionalTheory]
        [InlineData(4)]
        [MemberData(nameof(GetInts))]
        public void ConditionalTheoriesWithMemberData(int arg)
        {
            _conditionalMemberDataRuns++;
            Assert.True(_conditionalTheoryRuns <= 3, $"Theory should run 2 times, but ran {_conditionalMemberDataRuns} times.");
        }

        public static TheoryData<int> GetInts
            => new TheoryData<int> { 0, 1 };

        [ConditionalTheory]
        [OSSkipCondition(OperatingSystems.Windows)]
        [OSSkipCondition(OperatingSystems.MacOSX)]
        [OSSkipCondition(OperatingSystems.Linux)]
        [MemberData(nameof(GetActionTestData))]
        public void ConditionalTheoryWithFuncs(Func<int, int> func)
        {
            Assert.True(false, "This should never run");
        }

        [Fact]
        public void TestAlwaysRun()
        {
            // This is required to ensure that this type at least gets initialized.
            Assert.True(true);
        }

#if NETCOREAPP
        [ConditionalTheory]
        [FrameworkSkipCondition(RuntimeFrameworks.CLR)]
        [MemberData(nameof(GetInts))]
        public void ThisTestMustRunOnCoreCLR(int value)
        {
            Asserter.TestRan = true;
        }
#elif NET472
        [ConditionalTheory]
        [FrameworkSkipCondition(RuntimeFrameworks.CoreCLR)]
        [MemberData(nameof(GetInts))]
        public void ThisTestMustRunOnCLR(int value)
        {
            Asserter.TestRan = true;
        }
#else
#error Target frameworks need to be updated.
#endif

        // Test is named this way to be the lowest test in the alphabet, it relies on test ordering
        [Fact]
        public void ZzzzzzzEnsureThisIsTheLastTest()
        {
            Assert.True(Asserter.TestRan);
        }

        public static TheoryData<Func<int, int>> GetActionTestData
            => new TheoryData<Func<int, int>>
            {
                (i) => i * 1
            };

        public class ConditionalTheoryAsserter : IDisposable
        {
            public bool TestRan { get; set; }

            public void Dispose()
            {
            }
        }

        [ConditionalTheory]
        [MemberData(nameof(SkippableData))]
        public void WithSkipableData(Skippable skippable)
        {
            Assert.Null(skippable.Skip);
            Assert.Equal(1, skippable.Data);
        }

        public static TheoryData<Skippable> SkippableData => new TheoryData<Skippable>
        {
            new Skippable() { Data = 1 },
            new Skippable() { Data = 2, Skip = "This row should be skipped." }
        };

        public class Skippable : IXunitSerializable
        {
            public Skippable() { }
            public int Data { get; set; }
            public string Skip { get; set; }

            public void Serialize(IXunitSerializationInfo info)
            {
                info.AddValue(nameof(Data), Data, typeof(int));
            }

            public void Deserialize(IXunitSerializationInfo info)
            {
                Data = info.GetValue<int>(nameof(Data));
            }

            public override string ToString()
            {
                return Data.ToString(CultureInfo.InvariantCulture);
            }
        }
    }
}
