using OptionStudy.UnitApp.Next;

using System.Globalization;

namespace OptionStudy.Next
{
    /// <summary>
    /// Json文件 配置源
    /// </summary>
    public class JsonConfigurationSourceTest : IDisposable
    {
        private readonly ITestOutputHelper testOutput;

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

        #region 空值测试
        /// <summary>
        /// Key值为没有内容的空对象{}
        /// </summary>
        [Fact]
        public void EmptyObject_AddsAsNull_Test()
        {
            var json = @"{""key"": { }}";

            var jsonProvider = new JsonConfigurationProvider(new JsonConfigurationSource());
            jsonProvider.Load(StringToStream(json));

            Assert.True(jsonProvider.TryGet("key",out string? keyValue));
            Assert.Null(keyValue);
        }

        /// <summary>
        /// key值为null
        /// </summary>
        [Fact]
        public void NullObject_AddsEmptyString_Test()
        {
            var json = @"{ ""key"": null}";

            var jsonProvider = new JsonConfigurationProvider(new JsonConfigurationSource());
            jsonProvider.Load(StringToStream(json));

            Assert.True(jsonProvider.TryGet("key", out string? value));
            Assert.Equal("", value);
        }

        /// <summary>
        /// 没有父类的嵌套项
        /// </summary>
        [Fact]
        public void NestedObject_DoesNotAddParent_Test()
        {
            var json = @"
                            {
                                ""key"": 
                                {
                                    ""nested"": ""value""
                                }
                            }
                        ";

            var jsonProvider = new JsonConfigurationProvider(new JsonConfigurationSource());
            jsonProvider.Load(StringToStream(json));

            Assert.False(jsonProvider.TryGet("key", out _));
            Assert.True(jsonProvider.TryGet("key:nested",out string? nestedValue));
            Assert.Equal("value", nestedValue);
        }
        #endregion

        /// <summary>
        /// 获取json数据源,从json文本
        /// </summary>
        [Fact]
        public void BuildConfigurationSource_FromJsonStream()
        {
            var json = @"
            {
                ""firstname"": ""test"",
                ""test.last.name"": ""last.name"",
                    ""residential.address"": {
                        ""street.name"": ""Something street"",
                        ""zipcode"": ""12345""
                    }
            }";

            var config = new ConfigurationBuilder().AddJsonStream(StringToStream(json)).Build();

            Assert.Equal("test", config["firstname"]);
            Assert.Equal("last.name", config["test.last.name"]);
            Assert.Equal("Something street", config["residential.address:STREET.name"]);
            Assert.Equal("12345", config["residential.address:zipcode"]);
        }

        /// <summary>
        /// JsonStream 为 null时,构建配置源异常
        /// </summary>
        [Fact]
        public void BuildConfigurationSource_FromNullJsonStream()
        {
            Assert.Throws<InvalidOperationException>(() =>
            {
                _ = new ConfigurationBuilder().AddJsonStream(null).Build();
            });
        }

        /// <summary>
        /// 提供者为 JsonStreamProvider (流提供者)时,配置源重新加载异常
        /// </summary>
        [Fact]
        public void ConfigurationSource_ReloadThrows_FromStreamProvider()
        {
            var json = @"
            {
                ""firstname"": ""test""
            }";
            var config = new ConfigurationBuilder().AddJsonStream(StringToStream(json)).Build();

            Assert.Throws<InvalidOperationException>(() => config.Reload());
        }

        /// <summary>
        /// 从有效的json中加载键值对
        /// </summary>
        [Fact]
        public void ConfigurationSource_LoadKeyValuePairs_FromValidJson()
        {
            var json = @"
            {
                ""firstname"": ""test"",
                ""test.last.name"": ""last.name"",
                    ""residential.address"": {
                        ""street.name"": ""Something street"",
                        ""zipcode"": ""12345""
                    }
            }";

            var jsonSource = new JsonStreamConfigurationSource ();
            jsonSource.Stream = StringToStream(json);

            var jsonPorvider = jsonSource.Build(new ConfigurationBuilder()) as JsonStreamConfigurationProvider;
            //jsonPorvider?.Load(StringToStream(json));

            var root = new ConfigurationBuilder()
                .Add(jsonSource)
                //.Add(jsonPorvider.Source) //或者这种写法
                .Build();

            //推荐使用下面的AddJsonStream()扩展方法,上面只是展示了一步步构建 ConfigurationRoot 的过程
            //new ConfigurationBuilder().AddJsonStream(StringToStream(json)).Build();

            Assert.Equal("test", root.GetValue<string>("firstname"));
            Assert.Equal("last.name", root.GetValue<string>("test.last.name"));
            Assert.Equal("Something street", root.GetValue<string>("residential.address:STREET.name"));
            Assert.Equal("12345", root.GetValue<string>("residential.address:zipcode"));
        }

        /// <summary>
        /// 获取空字符串值
        /// </summary>
        [Fact]
        public void ConfigurationSource_LoadEmptyValue_Test()
        {
            var json = @"
            {
                ""name"": """"
            }";

            var configurationRoot = new ConfigurationBuilder().AddJsonStream(StringToStream(json)).Build();

            Assert.Equal(string.Empty, configurationRoot.GetValue<string>("name"));
        }

        /// <summary>
        /// 按文化区域获取配置项
        /// </summary>
        [Fact]
        public void ConfigurationSource_LoadWithCulture()
        {
            var previousCulture = CultureInfo.CurrentCulture;

            try
            {
                CultureInfo.CurrentCulture = new CultureInfo("fr-FR");

                var json = @"
                {
                    ""number"": 3.14
                }";

                var configurationRoot = new ConfigurationBuilder().AddJsonStream(StringToStream(json)).Build();
                Assert.Equal("3.14", configurationRoot.GetValue<string>("number"));
            }
            finally
            {
                CultureInfo.CurrentCulture = previousCulture;
            }
        }

        /// <summary>
        /// Json无根元素时,抛出异常
        /// </summary>
        [Fact]
        public void ConfigurationSource_NonObjectRootIsInvalid()
        {
            var json = @"""test""";

            var exception = Assert.Throws<FormatException>(() => LoadProvider(json));

            Assert.NotNull(exception.Message);
        }

        /// <summary>
        /// 支持并忽略 json 中的注释
        /// </summary>
        [Fact]
        public void ConfigurationSource_SupportAndIgnoreComments()
        {
            var json = @"/* 注释将被忽略后,正常解析 */
                {/* Comments */
                ""name"": /* Comments */ ""test"",
                ""address"": {
                    ""street"": ""Something street"", /* Comments */
                    ""zipcode"": ""12345""
                }
            }";

            var configurationRoot = new ConfigurationBuilder().AddJsonStream(StringToStream(json)).Build();

            Assert.Equal("test", configurationRoot.GetValue<string>("name"));
            Assert.Equal("Something street", configurationRoot.GetValue<string>("address:street"));
            Assert.Equal("12345", configurationRoot.GetValue<string>("address:zipcode"));
        }

        /// <summary>
        /// 支持并忽略json尾部逗号
        /// </summary>
        [Fact]
        public void ConfigurationSource_SupportAndIgnoreTrailingCommas()
        {
            var json = @"
            {
                ""firstname"": ""test"",
                ""test.last.name"": ""last.name"",
                    ""residential.address"": {
                        ""street.name"": ""Something street"",  
                        ""zipcode"": ""12345"",
                    },
            }";

            var configurationRoot = new ConfigurationBuilder().AddJsonStream(StringToStream(json)).Build();

            Assert.Equal("test", configurationRoot.GetValue<string>("firstname"));
            Assert.Equal("last.name", configurationRoot.GetValue<string>("test.last.name"));
            Assert.Equal("Something street", configurationRoot.GetValue<string>("residential.address:STREET.name"));
            Assert.Equal("12345", configurationRoot.GetValue<string>("residential.address:zipcode"));
        }

        /// <summary>
        /// 在完成分析之前发现意外结束时抛出异常
        /// </summary>
        [Fact]
        public void ConfigurationProvider_ThrowExceptionWhenUnexpectedEndFoundBeforeFinishParsing()
        {
            var json = @"{
                ""name"": ""test"",
                ""address"": {
                    ""street"": ""Something street"",
                    ""zipcode"": ""12345""
                }
            /* Missing a right brace here*/";
            var exception = Assert.Throws<FormatException>(() => 
            { 
                LoadProvider(json);
            });
            Assert.Contains("Could not parse the JSON file.", exception.Message);
        }

        /// <summary>
        /// 在完成分析之前缺少封闭大括号时抛出异常
        /// </summary>
        [Fact]
        public void ConfigurationProvider_ThrowExceptionWhenMissingCurlyBeforeFinishParsing()
        {
            var json = @"
            {
              ""Data"": {
            ";

            var exception = Assert.Throws<FormatException>(() => LoadProvider(json));
            Assert.Contains("Could not parse the JSON file.", exception.Message);
        }

        /// <summary>
        ///  文件路径为null时,抛出异常
        /// </summary>
        [Fact]
        public void ConfigurationSource_ThrowExceptionWhenPassingNullAsFilePath()
        {
            Assert.Throws<ArgumentException>(() => new ConfigurationBuilder().AddJsonFile(path: null));
        }

        /// <summary>
        /// 文件路径为空字符串时,抛出异常
        /// </summary>
        [Fact]
        public void ConfigurationSource_ThrowExceptionWhenPassingEmptyStringAsFilePath()
        {
            Assert.Throws<ArgumentException>(() => new ConfigurationBuilder().AddJsonFile(string.Empty));
        }

        /// <summary>
        /// json文件不存在时,抛出异常
        /// </summary>
        [Fact]
        public void ConfigurationSource_Throws_On_Missing_Configuration_File()
        {
            var config = new ConfigurationBuilder().AddJsonFile("NotExistingConfig.json", optional: false);
            var exception = Assert.Throws<FileNotFoundException>(() => config.Build());

            // Assert
            Assert.StartsWith($"The configuration file 'NotExistingConfig.json' was not found and is not optional. The expected physical path was '", exception.Message);
        }

        /// <summary>
        /// json文件不存在时,不抛出异常
        /// </summary>
        [Fact]
        public void ConfigurationSource_Does_Not_Throw_On_Optional_Configuration()
        {
            var config = new ConfigurationBuilder().AddJsonFile("NotExistingConfig.json", optional: true).Build();
        }

        /// <summary>
        /// json内容为空白时,抛出异常
        /// </summary>
        [Fact]
        public void ConfigurationSource_ThrowFormatExceptionWhenFileIsEmpty()
        {
            var exception = Assert.Throws<FormatException>(() => LoadProvider(@""));
            Assert.Contains("Could not parse the JSON file.", exception.Message);
        }

        [Fact]
        public void UseJsonConfigurationSource_Test()
        {
            var root = new ConfigurationBuilder()
                .AddEnvironmentVariables()
                .AddJsonFile("Configs/appsettings.json",true,true)
                .Build();

            var option = root.Get<AppOption>();

            Assert.NotNull(option);
        }

        #region 私有辅助方法
        private Stream StringToStream(string str)
        {
            var memStream = new MemoryStream();
            var textWriter = new StreamWriter(memStream);
            textWriter.Write(str);
            textWriter.Flush();
            memStream.Seek(0, SeekOrigin.Begin);

            return memStream;
        }

        private JsonConfigurationProvider LoadProvider(string json)
        {
            var p = new JsonConfigurationProvider(new JsonConfigurationSource { Optional = true });
            p.Load(StringToStream(json));
            return p;
        }
        #endregion

        public void Dispose()
        {

        }
    }
}