using OptionStudy.UnitApp.Next;

namespace OptionStudy.Next
{
    /// <summary>
    /// 配置绑定 测试
    /// 引用 Microsoft.Extensions.Configuration.Binder 包
    /// </summary>
    public class ConfigBinderTest : IDisposable
    {
        private readonly ITestOutputHelper testOutput;

        public ConfigBinderTest(ITestOutputHelper testOutputHelper)
        {
            this.testOutput = testOutputHelper;
        }

        /// <summary>
        /// 绑定配置项的值
        /// </summary>
        [Fact]
        public void Binder_ConfigItem_Test()
        {
            IDictionary<string, string?> memoryData = new Dictionary<string, string?>()
            {
                ["AppName"] = "MemoryAppName",
                ["AppVersion"] = "0.0.0.1",
                ["EMail:ReceiveAddress"] = "memory@163.com",
                ["EMail:Recipient"] = "memory",
                ["foo"] = null,
                ["bar"] = "",
                ["baz"] = "123",
            };

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

            //针对 object : 直接返回原始值(字符串或null)
            Assert.Null(root.GetValue<object>("foo"));
            Assert.Equal(string.Empty, root.GetValue<object>("bar"));
            Assert.Equal("123", root.GetValue<object>("baz"));


            //针对普通类型: 针对类型的 TypeConverter 来 完成类型转换
            Assert.Equal(0,root.GetValue<int>("foo"));
            Assert.Equal(123, root.GetValue<int>("baz"));

            //针对 Nullable<T> 类型:原始值为""或null时返回null,否则转换成基础类型。匹配不了了,则借值为 Default(T)
            Assert.Null(root.GetValue<int?>("foo"));
            Assert.Null(root.GetValue<int?>("bar"));

            testOutput.WriteLine("绑定配置项的值");
        }

        /// <summary>
        /// 绑定配置项的值,使用自定义转换器
        /// </summary>
        [Fact]
        public void Binder_ConfigItem_CustomConverter_Test()
        {
            IDictionary<string, string?> memoryData = new Dictionary<string, string?>()
            {
                ["Point"] = "(123,456)",
            };

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

            var point = root.GetValue<Point>("Point");

            Assert.Equal(123, point.x);
            Assert.Equal(456,point.y);

            testOutput.WriteLine("绑定配置项的值,使用自定义转换器");
        }

        /// <summary>
        /// 绑定复合对象
        /// 自定义的类或结构等对象
        /// </summary>
        [Fact]
        public void Binder_ComplexObject_Test()
        {
            IDictionary<string, string?> memoryData = new Dictionary<string, string?>()
            {
                ["AppName"] = "MemoryAppName",
                ["AppVersion"] = "0.0.0.1",
                ["EMail:ReceiveAddress"] = "memory@163.com",
                ["EMail:Recipient"] = "memory",
            };

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

            var appOption = root.Get<AppOption>();
            Assert.Equal("MemoryAppName", appOption.AppName);
            Assert.Equal("0.0.0.1", appOption.AppVersion.ToString());
            Assert.Equal("memory@163.com", appOption.EMail?.ReceiveAddress);
            Assert.Equal("memory", appOption.EMail?.Recipient);

            var email = root.GetSection("EMail").Get<ReceiveMailOption>();
            Assert.Equal("memory@163.com", email.ReceiveAddress);
            Assert.Equal("memory", email.Recipient);

            testOutput.WriteLine("绑定复合对象");
        }

        /// <summary>
        /// 绑定集合(包括数组)
        /// </summary>
        [Fact]
        public void Binder_Set_Test()
        {
            IDictionary<string, string?> memoryData = new Dictionary<string, string?>()
            {
                ["0:AppName"]                    = "MemoryAppName",
                ["0:AppVersion"]                 = "0.0.0.1",
                ["0:EMail:ReceiveAddress"]       = "memory@163.com",
                ["0:EMail:Recipient"]            = "memory",

                ["1:AppName"]                    = "MemoryAppName",
                ["1:AppVersion"]                 = "0.0.0.1",
                ["1:EMail:ReceiveAddress"]       = "memory@163.com",
                ["1:EMail:Recipient"]            = "memory",

                ["2;AppName"]                    = "MemoryAppName",
                ["2;AppVersion"]                 = "0.0.0.1",
                ["2;EMail:ReceiveAddress"]       = "memory@163.com",
                ["2;EMail:Recipient"]            = "memory",
            };

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

            var appOptions = root.Get<IList<AppOption>>();

            Assert.Equal("MemoryAppName", appOptions[0].AppName);
            Assert.Equal("0.0.0.1", appOptions[0].AppVersion.ToString());
            Assert.Equal("memory@163.com", appOptions[0].EMail?.ReceiveAddress);
            Assert.Equal("memory", appOptions[0].EMail?.Recipient);

            Assert.Equal("MemoryAppName", appOptions[1].AppName);
            Assert.Equal("0.0.0.1", appOptions[1].AppVersion.ToString());
            Assert.Equal("memory@163.com", appOptions[1].EMail?.ReceiveAddress);
            Assert.Equal("memory", appOptions[1].EMail?.Recipient);

            Assert.Equal("MemoryAppName", appOptions[1].AppName);
            Assert.Equal("0.0.0.1", appOptions[1].AppVersion.ToString());
            Assert.Equal("memory@163.com", appOptions[1].EMail?.ReceiveAddress);
            Assert.Equal("memory", appOptions[1].EMail?.Recipient);

            testOutput.WriteLine("绑定集合(包括数组)");
        }

        /// <summary>
        /// 绑定集合与数组的差别
        /// 某项绑定失败,不影响其它项绑定,但是数组表现为空项而集合表现为忽略失败项
        /// </summary>
        [Fact]
        public void Binder_CollectionAndArray_Differences_Test()
        {
            IDictionary<string, string?> memoryData = new Dictionary<string, string?>()
            {
                ["0:AppName"] = "0MemoryAppName",
                ["0:AppVersion"] = "0.0.0.1",


                ["1:AppName"] = "1MemoryAppName",
                ["1:AppVersion"] = "A.B.C.D",                   //此项改为非版本数值,故意出错

                ["2:AppName"] = "2MemoryAppName",
                ["2:AppVersion"] = "2.1.2.3",
            };

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

            var list = root.Get<IList<AppOption>>();
            var array = root.Get<AppOption[]>();

            Assert.Equal(2, list.Count);
            
            Assert.Equal(3, array.Length);
            Assert.Null(array[1]);

            testOutput.WriteLine("绑定集合与数组的差别");
        }

        /// <summary>
        /// 绑定字典
        /// </summary>
        [Fact]
        public void Binder_Dictionary_Test()
        {
            IDictionary<string, string?> memoryData = new Dictionary<string, string?>()
            {
                ["One:AppName"] = "0MemoryAppName",
                ["One:AppVersion"] = "0.0.0.1",
                ["One:EMail:ReceiveAddress"] = "memory@163.com",
                ["One:EMail:Recipient"] = "memory",


                ["Two:AppName"] = "1MemoryAppName",
                ["Two:AppVersion"] = "1.2.3.4",                
                ["Two:EMail:ReceiveAddress"] = "1@163.com",
                ["Two:EMail:Recipient"] = "memory1",

                ["Three:AppName"] = "2MemoryAppName",
                ["Three:AppVersion"] = "2.0.0.0",
                ["Three:EMail:ReceiveAddress"] = "2@163.com",
                ["Three:EMail:Recipient"] = "memory2",
            };

            var root = new ConfigurationBuilder().AddInMemoryCollection(memoryData).Build();
            var dic = root.Get<IDictionary<string,AppOption>>();
            
            Assert.Equal(3, dic.Count);
            Assert.Equal("0MemoryAppName", dic["One"].AppName);
            Assert.Equal("1MemoryAppName", dic["Two"].AppName);
            Assert.Equal("2MemoryAppName", dic["Three"].AppName);

            testOutput.WriteLine("绑定字典");
        }

        /// <summary>
        /// 绑定字典有错
        /// 存在转换错误时,抛出异常
        /// </summary>
        [Fact]
        public void Binder_Dictionary_HasError_Test()
        {
            IDictionary<string, string?> memoryData = new Dictionary<string, string?>()
            {
                ["One:AppName"] = "0MemoryAppName",
                ["One:AppVersion"] = "0.0.0.1",
                ["One:EMail:ReceiveAddress"] = "memory@163.com",
                ["One:EMail:Recipient"] = "memory",


                ["Two:AppName"] = "1MemoryAppName",
                ["Two:AppVersion"] = "A.B.C.D",                   //此项改为非版本数值,故意出错
                ["Two:EMail:ReceiveAddress"] = "1@163.com",
                ["Two:EMail:Recipient"] = "memory1",

                ["Three:AppName"] = "2MemoryAppName",
                ["Three:AppVersion"] = "2.0.0.0",
                ["Three:EMail:ReceiveAddress"] = "2@163.com",
                ["Three:EMail:Recipient"] = "memory2",
            };

            var root = new ConfigurationBuilder().AddInMemoryCollection(memoryData).Build();
            
            //["Two:AppVersion"] = "A.B.C.D"值不能转换成版本类,会抛出异常
            Action getConfigDic = () => 
            {
                root.Get<IDictionary<string, AppOption>>();
            };

            Assert.Throws<InvalidOperationException>(getConfigDic);

            testOutput.WriteLine("绑定字典:存在转换错误时,抛出异常!");
        }

        public void Dispose()
        {

        }
    }
}