using FluentAssertions;
using Nop.Core.Configuration;
using Nop.Core.Domain.Catalog;
using Nop.Core.Domain.Configuration;
using Nop.Services.Configuration;
using NUnit.Framework;

namespace Nop.Tests.Nop.Services.Tests.Configuration;

[TestFixture]
public class SettingServiceTests : ServiceTest<Setting>
{
    private ISettingService _settingService;

    [OneTimeSetUp]
    public void SetUp()
    {
        _settingService = GetService<ISettingService>();
    }

    [TearDown]
    public async Task TearDown()
    {
        await _settingService.DeleteSettingAsync<TestSettings>();
    }

    protected override CrudData<Setting> CrudData
    {
        get
        {
            var newSettings = new Setting { Name = "SettingServiceTests.TestCrud", Value = "TestCrud", StoreId = 1 };

            var updateSettings = new Setting
            {
                Name = "SettingServiceTests.TestCrud",
                Value = "TestCrud updated",
                StoreId = 1
            };

            return new CrudData<Setting>
            {
                BaseEntity = newSettings,
                UpdatedEntity = updateSettings,
                Insert = async (setting) =>
                {
                    await _settingService.SetSettingAsync(setting.Name, setting.Value, setting.StoreId);
                    var s = await _settingService.GetSettingAsync("SettingServiceTests.TestCrud", 1);
                    setting.Id = s.Id;

                },
                Update = async setting => await _settingService.SetSettingAsync(setting.Name, setting.Value, setting.StoreId),
                GetById = _settingService.GetSettingByIdAsync,
                Delete = _settingService.DeleteSettingAsync,
                IsEqual = (first, second) => first.StoreId == second.StoreId && first.Name.Equals(second.Name, StringComparison.InvariantCultureIgnoreCase) && first.Value == second.Value
            };
        }
    }
    
    [Test]
    public async Task CanGetSettings()
    {
        var setting = await _settingService.LoadSettingAsync<CatalogSettings>();

        setting.Should().NotBeNull();
        setting.DefaultViewMode.Should().Be("grid");
        setting.NewProductsPageSizeOptions.Should().Be("6, 3, 9");
    }

    [Test]
    public async Task CanGetAllSettings()
    {
        var settings = await _settingService.GetAllSettingsAsync();
        settings.Should().NotBeNull();
        settings.Any().Should().BeTrue();
    }

    [Test]
    public async Task CanGetSettingByKey()
    {
        var setting = await _settingService.GetSettingByKeyAsync<string>("CatalogSettings.DefaultViewMode");
        setting.Should().Be("grid");
    }

    [Test]
    public async Task CanGetTypedSettingValueByKey()
    {
        var setting = await _settingService.GetSettingByKeyAsync<int>("CatalogSettings.NumberOfProductTags");
        setting.Should().Be(15);
    }

    [Test]
    public async Task DefaultValueReturnedIfSettingDoesNotExist()
    {
        var setting = await _settingService.GetSettingByKeyAsync("NonExistentKey", 1.0f);
        setting.Should().Be(1.0f);
    }

    [Test]
    public async Task CanDeleteSetting()
    {
        var setting = new TestSettings
        {
            ServerName = "Ruby",
            Ip = "192.168.0.1",
            PortNumber = 21,
            Username = "admin",
            Password = "password"
        };

        await _settingService.SaveSettingAsync(setting);
        setting = await _settingService.LoadSettingAsync<TestSettings>();
        setting.Should().NotBeNull();
        setting.Username.Should().Be("admin");
        setting.Password.Should().Be("password");
        await _settingService.DeleteSettingAsync(setting, settings => settings.Username);
        await _settingService.DeleteSettingAsync(
            await _settingService.GetSettingAsync(_settingService.GetSettingKey(setting,
                settings => settings.Password)));
        setting = await _settingService.LoadSettingAsync<TestSettings>();
        setting.Should().NotBeNull();
        setting.Username.Should().BeNullOrEmpty();
        setting.Password.Should().BeNullOrEmpty();
        var ipExists = await _settingService.SettingExistsAsync(setting, settings => setting.Ip);
        ipExists.Should().BeTrue();
        await _settingService.DeleteSettingAsync<TestSettings>();
        var portNumber =
            await _settingService.GetSettingAsync(_settingService.GetSettingKey(setting,
                settings => settings.PortNumber));

        portNumber.Should().BeNull();

        ipExists = await _settingService.SettingExistsAsync(setting, settings => setting.Ip);
        ipExists.Should().BeFalse();
    }

    [Test]
    public async Task CanSaveSetting()
    {
        var setting = new TestSettings
        {
            ServerName = "Ruby",
            Ip = "192.168.0.1",
            PortNumber = 21,
            Username = "admin",
            Password = "password"
        };

        await _settingService.SaveSettingAsync(setting);
        setting = await _settingService.LoadSettingAsync<TestSettings>();
        setting.Should().NotBeNull();

        setting.ServerName.Should().Be("Ruby");
        setting.Ip.Should().Be("192.168.0.1");
        setting.PortNumber.Should().Be(21);
        setting.Username.Should().Be("admin");
        setting.Password.Should().Be("password");

        setting.PortNumber = 24;

        await _settingService.SaveSettingAsync(setting, settings => settings.PortNumber);

        setting = await _settingService.LoadSettingAsync<TestSettings>();
        setting.Should().NotBeNull();

        setting.PortNumber.Should().Be(24);

        setting.Ip = "192.168.1.1";

        await _settingService.SaveSettingOverridablePerStoreAsync(setting, settings => settings.Ip, false);

        var ip = (await _settingService.GetSettingAsync(
            _settingService.GetSettingKey(setting, settings => settings.Ip)))?.Value;

        ip.Should().NotBeNull().And.Be("192.168.1.1");

        setting.Ip = "192.168.0.2";

        await _settingService.SaveSettingOverridablePerStoreAsync(setting, settings => settings.Ip, false, 1);

        ip = (await _settingService.GetSettingAsync(
            _settingService.GetSettingKey(setting, settings => settings.Ip), 1))?.Value;

        ip.Should().BeNullOrEmpty();

        await _settingService.SaveSettingOverridablePerStoreAsync(setting, settings => settings.Ip, true, 1);

        ip = (await _settingService.GetSettingAsync(
            _settingService.GetSettingKey(setting, settings => settings.Ip), 1))?.Value;

        ip.Should().NotBeNull().And.Be("192.168.0.2");
    }

    #region Nested class

    public class TestSettings : ISettings
    {
        public string ServerName { get; set; }
        public string Ip { get; set; }
        public int PortNumber { get; set; }
        public string Username { get; set; }
        public string Password { get; set; }
    }

    #endregion
}