using Microsoft.Extensions.Options;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Hosting.Internal;
using Microsoft.VisualStudio.TestPlatform.ObjectModel;
using Xunit;

namespace OptionsPattern.Sutdy.Experience
{
    /// <summary>
    /// 6.1 配置选项 编程体验
    /// </summary>
    public class OptionsPatternTest:IDisposable
    {
        private readonly ITestOutputHelper testOutput;
        public OptionsPatternTest(ITestOutputHelper outputHelper)
        {
            testOutput = outputHelper;
        }

        [Fact]
        public void Test()
        {
            testOutput.WriteLine("6.1 配置选项 编程体验");
        }

        /// <summary>
        /// 将配置绑定为 Option 对象
        /// </summary>
        [Fact]
        public void BindConfiguration_As_OptionsObject_Test()
        {
            var configuration = new ConfigurationBuilder().AddJsonFile("Configs/appsettings.json",false,true).Build();

            var appOption = new ServiceCollection()
                .AddOptions()
                .Configure<AppOption>(configuration)
                .BuildServiceProvider()
                .GetRequiredService<IOptions<AppOption>>().Value;

            Assert.NotNull(configuration);
            Assert.NotNull(appOption);

            Assert.Equal("JsonAppName", appOption.AppName);
            Assert.Equal(new Version(0,0,0,1), appOption.AppVersion);
            Assert.Equal("json@163.com", appOption.EMail?.ReceiveAddress);
            Assert.Equal("json", appOption.EMail?.Recipient);

            testOutput.WriteLine("将配置绑定为 Option 对象");
        }
        
        /// <summary>
        /// 提供具名的 Options 对象
        /// </summary>
        [Fact]
        public void Provide_NamedOptionsObject_Test()
        {
            var memoryData = new Dictionary<string, string?>() 
            {
                ["One:AppName"] = "OneAppName",
                ["One:AppVersion"] = "1.1.1.1",
                ["One:EMail:ReceiveAddress"] = "One@163.com",
                ["One:EMail:Recipient"] = "One",

                ["Two:AppName"] = "TwoAppName",
                ["Two:AppVersion"] = "2.2.2.2",
                ["Two:EMail:ReceiveAddress"] = "Two@163.com",
                ["Two:EMail:Recipient"] = "Two",
            };

            var configuration = new ConfigurationBuilder().AddInMemoryCollection(memoryData).Build();

            var serviceProvider = new ServiceCollection()
                .AddOptions()
                .Configure<AppOption>("One", configuration.GetSection("One"))
                .Configure<AppOption>("Two", configuration.GetSection("Two"))
                .BuildServiceProvider();

             var optionsSnapshot = serviceProvider.GetRequiredService<IOptionsSnapshot<AppOption>>();

            var one = optionsSnapshot.Get("One");
            var two = optionsSnapshot.Get("Two");

            Assert.NotNull(one);
            Assert.NotNull(two);

            Assert.Equal("OneAppName", one.AppName);
            Assert.Equal(new Version(1,1,1,1), one.AppVersion);
            Assert.Equal("One@163.com", one.EMail?.ReceiveAddress);
            Assert.Equal("One", one.EMail?.Recipient);

            Assert.Equal("TwoAppName", two.AppName);
            Assert.Equal(new Version(2, 2, 2, 2), two.AppVersion);
            Assert.Equal("Two@163.com", two.EMail?.ReceiveAddress);
            Assert.Equal("Two", two.EMail?.Recipient);

            testOutput.WriteLine("提供具名的 Options 对象");
        }

        /// <summary>
        /// 直接初始化 Options 对象
        /// (不使用IConfiguration)
        /// </summary>
        [Fact]
        public void IniOptions_NoConfiguration_Test()
        {
            var appOption = new ServiceCollection()
                .AddOptions()
                .Configure<AppOption>(configOption =>
                {
                    configOption.AppName = "NoConfigurationAppName";
                    configOption.AppVersion = new Version(0, 0, 0, 1);
                    configOption.EMail = new ReceiveMailOption()
                    {
                        ReceiveAddress = "NoConfigurationAppName@163.com",
                        Recipient = "NoConfigurationAppName",
                    };
                })
                .BuildServiceProvider()
                .GetRequiredService<IOptions<AppOption>>().Value;

            Assert.NotNull(appOption);

            Assert.Equal("NoConfigurationAppName", appOption.AppName);
            Assert.Equal(new Version(0, 0, 0, 1), appOption.AppVersion);
            Assert.Equal("NoConfigurationAppName@163.com", appOption.EMail?.ReceiveAddress);
            Assert.Equal("NoConfigurationAppName", appOption.EMail?.Recipient);

            testOutput.WriteLine("直接初始化 Options 对象");
        }

        /// <summary>
        /// 根据依赖服务的 Options 设置
        /// </summary>
        [Theory]
        [InlineData("pro")]
        [InlineData("dev")]
        [InlineData("test")]
        public void SetOptions_By_DependentService_Test(string environmentName)
        {
            var services = new ServiceCollection();
            services
                .AddSingleton<IHostEnvironment>(new HostingEnvironment() { EnvironmentName = environmentName })
                .AddOptions<AppOption>()// AddOptions() 与 AddOptions<TOption>() 返回的类型不一样。之后的 Configure 扩展不一样 
                .Configure<IHostEnvironment>((appOption, hostEnv) => // 泛型参数确定 Action 参数
                {
                    appOption.AppName = hostEnv.EnvironmentName + nameof(appOption.AppName);
                    appOption.AppVersion = new Version();
                    appOption.EMail = new ReceiveMailOption()
                    {
                        ReceiveAddress = $"{hostEnv.EnvironmentName}@163.com",
                        Recipient = hostEnv.EnvironmentName,
                    };
                });
            var appOption = services
                .BuildServiceProvider()
                .GetRequiredService< IOptions<AppOption> >()
                .Value;


            Assert.NotNull(appOption);
            Assert.Equal($"{environmentName}AppName",appOption.AppName);
            Assert.Equal(new Version(),appOption.AppVersion);
            Assert.Equal($"{environmentName}@163.com",appOption.EMail?.ReceiveAddress);
            Assert.Equal($"{environmentName}",appOption.EMail?.Recipient);

            testOutput.WriteLine("根据依赖服务的 Options 设置");
        }

        /// <summary>
        /// 验证 Options 的有效性:通过
        /// </summary>
        [Fact]
        public void Validate_Options_Success_Test()
        {
            var services = new ServiceCollection();
            services
                .AddOptions<AppOption>()
                .Configure(configOption =>
                {
                    configOption.AppName = "ValidateAppName";
                    configOption.AppVersion = new Version(0, 0, 0, 1);
                    configOption.EMail = new ReceiveMailOption()
                    {
                        ReceiveAddress = "Validate@163.com",
                        Recipient = "Validate",
                    };
                })
                .Validate(option =>
                { 
                    return true;
                });

            //不发生异常
            Action codeSnippet = () =>
            {
                _ = services
                    .BuildServiceProvider()
                    .GetService<IOptions<AppOption>>()
                    ?.Value;
            };

            var exception = Record.Exception(codeSnippet);
            Assert.Null(exception);

            testOutput.WriteLine("验证 Options 的有效性:通过");
        }

        /// <summary>
        /// 验证 Options 的有效性:无效并引发异常
        /// </summary>
        [Fact]
        public void Validate_Options_Fail_Test()
        {
            var services = new ServiceCollection();
            services
                .AddOptions<AppOption>()
                .Configure(configOption =>
                {
                    configOption.AppName = "ValidateAppName";
                    configOption.AppVersion = new Version(0, 0, 0, 1);
                    configOption.EMail = new ReceiveMailOption()
                    {
                        ReceiveAddress = "Validate@163.com",
                        Recipient = "Validate",
                    };
                })
                .Validate(option => //返回false,表示验证错误,会抛出异常
                {
                    return false;
                });

            //引发异常
            Assert.Throws<OptionsValidationException>(() => 
            {
                _ = services
                   .BuildServiceProvider()
                   .GetService<IOptions<AppOption>>()
                   ?.Value;
            });

            testOutput.WriteLine("验证 Options 的有效性:无效并引发异常");
        }

        public void Dispose()
        {
            
        }
    }
}