﻿using System;
using System.Collections.Specialized;
using System.Configuration;
using Infrastructure.Settings;
using Moq;
using Shouldly;
using Xunit;

namespace Infrastructure.Tests.Settings
{
    public class ConfigReaderTests
    {
        private IConfigReader _configReader;
        private readonly Mock<IConfigurationManager> _configurationManagerMock = new Mock<IConfigurationManager>();

        private const string StringAppSettingName = "StringAppSetting",
            StringAppSetting = "STRING_APP_SETTING",
            StringArrayAppSettingName = "StringArrayAppSetting";

        private void CommonArrange()
        {
            _configurationManagerMock.Setup(x => x.AppSettings).Returns(new NameValueCollection
            {
                {StringAppSettingName, StringAppSetting}
            });

            _configReader = new ConfigReader(_configurationManagerMock.Object);
        }

        [Fact]
        public void Should_Retrieve_Correct_ConnectionString()
        {
            // Arrange
            CommonArrange();
            const string conStrName = "Name";
            const string conStr = "CONNECTION_STRING";

            _configurationManagerMock.Setup(x => x.ConnectionStrings(conStrName))
                .Returns(new ConnectionStringSettings(conStrName, conStr));

            // Act
            var result = _configReader.GetConnectionString(conStrName);

            // Assert
            result.ShouldBe(conStr);
        }

        [Fact]
        public void Should_Throw_On_Missing_ConnectionString()
        {
            // Act
            Should.Throw<Exception>(() => { _configReader.GetConnectionString("Name"); });
        }

        [Fact]
        public void Should_Retrieve_Correct_AppSetting_As_String()
        {
            // Arrange
            CommonArrange();

            // Act
            var result = _configReader.GetStringSetting(StringAppSettingName);

            // Assert
            result.ShouldBe(StringAppSetting);
        }

        [Fact]
        public void Should_Return_Default_When_AppSetting_Not_Found()
        {
            // Arrange
            CommonArrange();
            const string defaultValue = "APP_SETTING";

            // Act
            var result = _configReader.GetStringSetting("SomeName", defaultValue);

            // Assert
            result.ShouldBe(defaultValue);
        }

        [Fact]
        public void Should_Return_Null_When_AppSetting_Not_Found_And_No_Default_Value_Is_Provided()
        {
            // Arrange
            CommonArrange();

            // Act
            var result = _configReader.GetStringSetting("SomeName");

            // Assert
            result.ShouldBeNull();
        }

        [Theory]
        [InlineData(',')]
        [InlineData('&')]
        [InlineData('-')]
        [InlineData('_')]
        public void Should_Retrieve_Correct_AppSetting_As_Array_Of_Strings_Delimited_By_Comma(char delimiter)
        {
            // Arrange
            CommonArrange();
            var settingValues = $"value1{delimiter}value2{delimiter}value3";
            _configurationManagerMock.Object.AppSettings.Add(StringArrayAppSettingName, settingValues);

            // Act
            var result = _configReader.GetStringArraySetting(StringArrayAppSettingName, delimiter);

            // Assert
            result.ShouldBe(settingValues.Split(delimiter));
        }

        [Fact]
        public void Should_Return_Empty_Array_When_AppSetting_Not_Found()
        {
            // Arrange
            CommonArrange();

            // Act
            var result = _configReader.GetStringArraySetting("SomeOtherValue");

            // Assert
            result.ShouldNotBeNull();
            result.ShouldBeEmpty();
        }
    }
}
