using FluentAssertions;
using Nop.Core.Domain.Catalog;
using Nop.Core.Domain.Customers;
using Nop.Core.Domain.Discounts;
using Nop.Core.Domain.Orders;
using Nop.Core.Domain.Shipping;
using Nop.Core.Domain.Stores;
using Nop.Core.Domain.Tax;
using Nop.Data;
using Nop.Services.Attributes;
using Nop.Services.Catalog;
using Nop.Services.Common;
using Nop.Services.Configuration;
using Nop.Services.Customers;
using Nop.Services.Discounts;
using Nop.Services.Orders;
using Nop.Services.Stores;
using Nop.Tests.Nop.Services.Tests.Payments;
using NUnit.Framework;

namespace Nop.Tests.Nop.Services.Tests.Orders;

[TestFixture]
public class OrderTotalCalculationServiceTests : ServiceTest
{
    private IOrderTotalCalculationService _orderTotalCalcService;
    private IProductService _productService;
    private ICustomerService _customerService;
    private IDiscountService _discountService;
    private TaxSettings _taxSettings;
    private ISettingService _settingService;
    private IShoppingCartService _shoppingCartService;
    private ShoppingCartSettings _shoppingCartSettings;
    private IStoreService _storeService;
    private RewardPointsSettings _rewardPointsSettings;
    private IGenericAttributeService _genericAttributeService;


    private Discount _discount;
    private Customer _customer;
    private Store _store;
    private string _checkoutAttrXml;

    #region Utilities

    private async Task<ShoppingCartItem> CreateTestShopCartItemAsync(decimal productPrice, int quantity = 1)
    {
        //shopping cart
        var product = new Product
        {
            Name = "Product name 1",
            Price = productPrice,
            CustomerEntersPrice = false,
            Published = true
        };

        await _productService.InsertProductAsync(product);

        var shoppingCartItem = new ShoppingCartItem
        {
            CustomerId = _customer.Id,
            ProductId = product.Id,
            StoreId = _store.Id,
            Quantity = quantity
        };

        return shoppingCartItem;
    }

    private async Task<List<ShoppingCartItem>> GetShoppingCartAsync()
    {
        var product = await _productService.GetProductBySkuAsync("FR_451_RB");
        var sci1 = new ShoppingCartItem
        {
            ProductId = product.Id,
            Quantity = 2
        };
        product = await _productService.GetProductBySkuAsync("FIRST_PRP");
        var sci2 = new ShoppingCartItem
        {
            ProductId = product.Id,
            Quantity = 3
        };

        var cart = new List<ShoppingCartItem> { sci1, sci2 };
        foreach (var sci in cart)
        {
            sci.CustomerId = _customer.Id;
        }

        return cart;
    }

    #endregion

    [SetUp]
    public async Task SetUp()
    {
        _settingService = GetService<ISettingService>();

        var shippingSettings = GetService<ShippingSettings>();
        shippingSettings.ActiveShippingRateComputationMethodSystemNames.Add("FixedRateTestShippingRateComputationMethod");
        _taxSettings = GetService<TaxSettings>();
        _taxSettings.ActiveTaxProviderSystemName = "FixedTaxRateTest";
        _taxSettings.ShippingIsTaxable = true;
        await _settingService.SaveSettingAsync(shippingSettings);
        await _settingService.SaveSettingAsync(_taxSettings);

        _orderTotalCalcService = GetService<IOrderTotalCalculationService>();
        _productService = GetService<IProductService>();
        _customerService = GetService<ICustomerService>();
        _discountService = GetService<IDiscountService>();
        _shoppingCartService = GetService<IShoppingCartService>();
        _shoppingCartSettings = GetService<ShoppingCartSettings>();
        _storeService = GetService<IStoreService>();
        _rewardPointsSettings = GetService<RewardPointsSettings>();

        _genericAttributeService = GetService<IGenericAttributeService>();
        var checkoutAttributeService = GetService<IAttributeService<CheckoutAttribute, CheckoutAttributeValue>>();

        var attr = await checkoutAttributeService.GetAttributeByIdAsync(1);

        var values = await checkoutAttributeService.GetAttributeValuesAsync(attr.Id);

        var val = values.FirstOrDefault(p => p.Name == "Yes")?.Id.ToString();

        _checkoutAttrXml = GetService<IAttributeParser<CheckoutAttribute, CheckoutAttributeValue>>().AddAttribute(string.Empty, attr, val);

        _discount = new Discount
        {
            IsActive = true,
            Name = "Discount 1",
            DiscountType = DiscountType.AssignedToOrderSubTotal,
            DiscountAmount = 3,
            DiscountLimitation = DiscountLimitationType.Unlimited
        };

        _customer = await _customerService.GetCustomerByEmailAsync(NopTestsDefaults.AdminEmail);
        _store = (await _storeService.GetAllStoresAsync()).First();

        await _genericAttributeService.SaveAttributeAsync(_customer,
            NopCustomerDefaults.SelectedPaymentMethodAttribute, "Payments.TestMethod", 1);
    }

    [TearDown]
    public async Task TearDown()
    {
        var settingService = GetService<ISettingService>();

        var shippingSettings = GetService<ShippingSettings>();
        shippingSettings.ActiveShippingRateComputationMethodSystemNames.Clear();

        _taxSettings.PaymentMethodAdditionalFeeIsTaxable = false;
        _taxSettings.ActiveTaxProviderSystemName = string.Empty;
        _taxSettings.ShippingIsTaxable = false;
        await settingService.SaveSettingAsync(shippingSettings);
        await settingService.SaveSettingAsync(_taxSettings);

        var product = await _productService.GetProductBySkuAsync("FR_451_RB");
        product.AdditionalShippingCharge = 0M;
        product.IsFreeShipping = true;
        await _productService.UpdateProductAsync(product);

        product = await _productService.GetProductBySkuAsync("FIRST_PRP");
        product.AdditionalShippingCharge = 0M;
        product.IsFreeShipping = true;
        await _productService.UpdateProductAsync(product);

        await GetService<IGenericAttributeService>().SaveAttributeAsync<string>(_customer, NopCustomerDefaults.SelectedPaymentMethodAttribute, null, 1);

        foreach (var item in GetService<IRepository<Discount>>().Table.Where(d => d.Name == "Discount 1").ToList())
            await _discountService.DeleteDiscountAsync(item);

        await _productService.DeleteProductsAsync(GetService<IRepository<Product>>().Table.Where(p => p.Name == "Product name 1").ToList());

        await _genericAttributeService.SaveAttributeAsync<string>(_customer, NopCustomerDefaults.CheckoutAttributes, null, _store.Id);
    }

    [Test]
    public async Task CanGetShoppingCartSubTotalExcludingTax()
    {
        //10% - default tax rate
        var (discountAmount, appliedDiscounts, subTotalWithoutDiscount, subTotalWithDiscount, taxRates) = await _orderTotalCalcService.GetShoppingCartSubTotalAsync(await GetShoppingCartAsync(), false);
        discountAmount.Should().Be(0);
        appliedDiscounts.Count.Should().Be(0);
        subTotalWithoutDiscount.Should().Be(207M);
        subTotalWithDiscount.Should().Be(207M);
        taxRates.Count.Should().Be(1);
        taxRates.ContainsKey(10).Should().BeTrue();
        taxRates[10].Should().Be(20.7M);
    }

    [Test]
    public async Task CanGetShoppingCartSubTotalIncludingTax()
    {
        var (discountAmount, appliedDiscounts, subTotalWithoutDiscount, subTotalWithDiscount, taxRates) = await _orderTotalCalcService.GetShoppingCartSubTotalAsync(await GetShoppingCartAsync(), true);
        discountAmount.Should().Be(0);
        appliedDiscounts.Count.Should().Be(0);
        subTotalWithoutDiscount.Should().Be(227.7M);
        subTotalWithDiscount.Should().Be(227.7M);
        taxRates.Count.Should().Be(1);
        taxRates.ContainsKey(10).Should().BeTrue();
        taxRates[10].Should().Be(20.7M);
    }

    [Test]
    public async Task CanGetShoppingCartSubTotal()
    {
        var (discountAmountInclTax, discountAmountExclTax, appliedDiscounts, subTotalWithoutDiscountInclTax, subTotalWithoutDiscountExclTax, subTotalWithDiscountInclTax, subTotalWithDiscountExclTax, taxRates) = await _orderTotalCalcService.GetShoppingCartSubTotalsAsync(await GetShoppingCartAsync());

        discountAmountExclTax.Should().Be(0);
        subTotalWithoutDiscountExclTax.Should().Be(207M);
        subTotalWithDiscountExclTax.Should().Be(207M);

        discountAmountInclTax.Should().Be(0);
        subTotalWithoutDiscountInclTax.Should().Be(227.7M);
        subTotalWithDiscountInclTax.Should().Be(227.7M);

        appliedDiscounts.Count.Should().Be(0);
        taxRates.Count.Should().Be(1);
        taxRates.ContainsKey(10).Should().BeTrue();
        taxRates[10].Should().Be(20.7M);
    }

    [Test]
    public async Task CanGetShoppingCartSubTotalWithCheckoutAttribute()
    {
        var (discountAmountInclTax, discountAmountExclTax, appliedDiscounts, subTotalWithoutDiscountInclTax, subTotalWithoutDiscountExclTax, subTotalWithDiscountInclTax, subTotalWithDiscountExclTax, taxRates) = await _orderTotalCalcService.GetShoppingCartSubTotalsAsync(await GetShoppingCartAsync());

        discountAmountExclTax.Should().Be(0);
        subTotalWithoutDiscountExclTax.Should().Be(207M);
        subTotalWithDiscountExclTax.Should().Be(207M);

        discountAmountInclTax.Should().Be(0);
        subTotalWithoutDiscountInclTax.Should().Be(227.7M);
        subTotalWithDiscountInclTax.Should().Be(227.7M);

        appliedDiscounts.Count.Should().Be(0);
        taxRates.Count.Should().Be(1);
        taxRates.ContainsKey(10).Should().BeTrue();
        taxRates[10].Should().Be(20.7M);

        await _genericAttributeService.SaveAttributeAsync(_customer, NopCustomerDefaults.CheckoutAttributes, _checkoutAttrXml, _store.Id);

        (_, _, _, subTotalWithoutDiscountInclTax, subTotalWithoutDiscountExclTax, subTotalWithDiscountInclTax, subTotalWithDiscountExclTax, taxRates) = await _orderTotalCalcService.GetShoppingCartSubTotalsAsync(await GetShoppingCartAsync());

        subTotalWithoutDiscountExclTax.Should().Be(217M);
        subTotalWithDiscountExclTax.Should().Be(217M);

        subTotalWithoutDiscountInclTax.Should().Be(238.7M);
        subTotalWithDiscountInclTax.Should().Be(238.7M);

        await _genericAttributeService.SaveAttributeAsync<string>(_customer, NopCustomerDefaults.CheckoutAttributes, null, _store.Id);
    }

    [Test]
    public async Task CanGetShoppingCartSubtotalDiscountExcludingTax()
    {
        await _discountService.InsertDiscountAsync(_discount);

        //10% - default tax rate
        var (discountAmount, appliedDiscounts, subTotalWithoutDiscount, subTotalWithDiscount, taxRates) = await _orderTotalCalcService.GetShoppingCartSubTotalAsync(await GetShoppingCartAsync(), false);

        await _discountService.DeleteDiscountAsync(_discount);

        discountAmount.Should().Be(3);
        appliedDiscounts.Count.Should().Be(1);
        appliedDiscounts.First().Name.Should().Be("Discount 1");
        subTotalWithoutDiscount.Should().Be(207M);
        subTotalWithDiscount.Should().Be(204M);
        taxRates.Count.Should().Be(1);
        taxRates.ContainsKey(10).Should().BeTrue();
        taxRates[10].Should().Be(20.4M);
    }

    [Test]
    public async Task CanGetShoppingCartSubtotalDiscountIncludingTax()
    {
        await _discountService.InsertDiscountAsync(_discount);

        var (discountAmount, appliedDiscounts, subTotalWithoutDiscount, subTotalWithDiscount, taxRates) = await _orderTotalCalcService.GetShoppingCartSubTotalAsync(await GetShoppingCartAsync(), true);

        await _discountService.DeleteDiscountAsync(_discount);

        //The comparison test failed before, because of a very tiny number difference.
        //discountAmount.ShouldEqual(3.3);
        (Math.Round(discountAmount, 10) == 3.3M).Should().BeTrue();
        appliedDiscounts.Count.Should().Be(1);
        appliedDiscounts.First().Name.Should().Be("Discount 1");
        subTotalWithoutDiscount.Should().Be(227.7M);
        subTotalWithDiscount.Should().Be(224.4M);
        taxRates.Count.Should().Be(1);
        taxRates.ContainsKey(10).Should().BeTrue();
        taxRates[10].Should().Be(20.4M);
    }

    [Test]
    public async Task CanGetShoppingCartSubtotalDiscountExcludingAndIncludingTax()
    {
        await _discountService.InsertDiscountAsync(_discount);

        //10% - default tax rate
        var (discountAmountInclTax, discountAmountExclTax, appliedDiscounts, subTotalWithoutDiscountInclTax, subTotalWithoutDiscountExclTax, subTotalWithDiscountInclTax, subTotalWithDiscountExclTax, taxRates) = await _orderTotalCalcService.GetShoppingCartSubTotalsAsync(await GetShoppingCartAsync());

        await _discountService.DeleteDiscountAsync(_discount);

        discountAmountExclTax.Should().Be(3);
        subTotalWithoutDiscountExclTax.Should().Be(207M);
        subTotalWithDiscountExclTax.Should().Be(204M);

        (Math.Round(discountAmountInclTax, 10) == 3.3M).Should().BeTrue();
        subTotalWithoutDiscountInclTax.Should().Be(227.7M);
        subTotalWithDiscountInclTax.Should().Be(224.4M);

        appliedDiscounts.Count.Should().Be(1);
        appliedDiscounts.First().Name.Should().Be("Discount 1");
        taxRates.Count.Should().Be(1);
        taxRates.ContainsKey(10).Should().BeTrue();
        taxRates[10].Should().Be(20.4M);
    }

    [Test]
    public async Task ShippingShouldBeFreeWhenAllShoppingCartItemsAreMarkedAsFreeShipping()
    {
        await TearDown();
        await SetUp();

        var product = await _productService.GetProductBySkuAsync("FR_451_RB");
        product.IsFreeShipping = true;
        await _productService.UpdateProductAsync(product);

        await _productService.GetProductBySkuAsync("FIRST_PRP");
        product.IsFreeShipping = true;
        await _productService.UpdateProductAsync(product);

        var isFreeShipping = await _orderTotalCalcService.IsFreeShippingAsync(await GetShoppingCartAsync());
        isFreeShipping.Should().BeTrue();
    }

    [Test]
    public async Task ShippingShouldNotBeFreeWhenSomeOfShoppingCartItemsAreNotMarkedAsFreeShipping()
    {
        var product = await _productService.GetProductBySkuAsync("FR_451_RB");
        product.IsFreeShipping = false;
        await _productService.UpdateProductAsync(product);
        var isFreeShipping = await _orderTotalCalcService.IsFreeShippingAsync(await GetShoppingCartAsync());
        product.IsFreeShipping = true;
        await _productService.UpdateProductAsync(product);
        isFreeShipping.Should().BeFalse();
    }

    [Test]
    public async Task ShippingShouldBeFreeWhenCustomerIsInRoleWithFreeShipping()
    {
        var product = await _productService.GetProductBySkuAsync("FR_451_RB");
        product.IsFreeShipping = false;
        await _productService.UpdateProductAsync(product);
        var role = await _customerService.GetCustomerRoleBySystemNameAsync(NopCustomerDefaults.AdministratorsRoleName);
        role.FreeShipping = true;
        await _customerService.UpdateCustomerRoleAsync(role);
        product.IsFreeShipping = true;
        await _productService.UpdateProductAsync(product);
        role.FreeShipping = false;
        await _customerService.UpdateCustomerRoleAsync(role);
        var isFreeShipping = await _orderTotalCalcService.IsFreeShippingAsync(await GetShoppingCartAsync());
        isFreeShipping.Should().BeTrue();
    }

    [Test]
    public async Task CanGetShippingTotalWithFixedShippingRateExcludingTax()
    {
        var product = await _productService.GetProductBySkuAsync("FR_451_RB");
        product.AdditionalShippingCharge = 21.25M;
        product.IsFreeShipping = false;
        await _productService.UpdateProductAsync(product);

        var (shipping, taxRate, appliedDiscounts) =
            await _orderTotalCalcService.GetShoppingCartShippingTotalAsync(await GetShoppingCartAsync(), false);

        product.AdditionalShippingCharge = 0M;
        product.IsFreeShipping = true;
        await _productService.UpdateProductAsync(product);

        shipping.Should().NotBeNull();
        //10 - default fixed shipping rate, 42.5 - additional shipping change
        shipping.Should().Be(52.5M);
        appliedDiscounts.Count.Should().Be(0);
        //10 - default fixed tax rate
        taxRate.Should().Be(10);
    }

    [Test]
    public async Task CanGetShippingTotalWithFixedShippingRateIncludingTax()
    {
        var product = await _productService.GetProductBySkuAsync("FR_451_RB");
        product.AdditionalShippingCharge = 21.25M;
        product.IsFreeShipping = false;
        await _productService.UpdateProductAsync(product);

        var (shipping, taxRate, appliedDiscounts) =
            await _orderTotalCalcService.GetShoppingCartShippingTotalAsync(await GetShoppingCartAsync(), true);

        product.AdditionalShippingCharge = 0M;
        product.IsFreeShipping = true;
        await _productService.UpdateProductAsync(product);

        shipping.Should().NotBeNull();
        //10 - default fixed shipping rate, 42.5 - additional shipping change
        shipping.Should().Be(57.75M);
        appliedDiscounts.Count.Should().Be(0);
        //10 - default fixed tax rate
        taxRate.Should().Be(10);
    }

    [Test]
    public async Task CanGetShippingTotalsWithFixedShippingRate()
    {
        var product = await _productService.GetProductBySkuAsync("FR_451_RB");
        product.AdditionalShippingCharge = 21.25M;
        product.IsFreeShipping = false;
        await _productService.UpdateProductAsync(product);

        var (shippingInclTax, shippingExclTax, taxRate, appliedDiscounts) =
            await _orderTotalCalcService.GetShoppingCartShippingTotalsAsync(await GetShoppingCartAsync());

        product.AdditionalShippingCharge = 0M;
        product.IsFreeShipping = true;
        await _productService.UpdateProductAsync(product);

        shippingInclTax.Should().NotBeNull();
        //10 - default fixed shipping rate, 42.5 - additional shipping change
        shippingInclTax.Should().Be(57.75M);
        appliedDiscounts.Count.Should().Be(0);
        //10 - default fixed tax rate
        taxRate.Should().Be(10);

        shippingExclTax.Should().NotBeNull();
        //10 - default fixed shipping rate, 42.5 - additional shipping change
        shippingExclTax.Should().Be(52.5M);
    }

    [Test]
    public async Task CanGetShippingTotalDiscountExcludingTax()
    {
        var product = await _productService.GetProductBySkuAsync("FR_451_RB");
        product.AdditionalShippingCharge = 21.25M;
        product.IsFreeShipping = false;
        await _productService.UpdateProductAsync(product);

        _discount.DiscountType = DiscountType.AssignedToShipping;
        await _discountService.InsertDiscountAsync(_discount);

        var (shipping, taxRate, appliedDiscounts) =
            await _orderTotalCalcService.GetShoppingCartShippingTotalAsync(await GetShoppingCartAsync(), false);

        await _discountService.DeleteDiscountAsync(_discount);
        _discount.DiscountType = DiscountType.AssignedToOrderSubTotal;
        product.AdditionalShippingCharge = 0M;
        product.IsFreeShipping = true;
        await _productService.UpdateProductAsync(product);

        appliedDiscounts.Count.Should().Be(1);
        appliedDiscounts.First().Name.Should().Be("Discount 1");
        shipping.Should().NotBeNull();
        //10 - default fixed shipping rate, 42.5 - additional shipping change, -3 - discount
        shipping.Should().Be(49.5M);
        //10 - default fixed tax rate
        taxRate.Should().Be(10);
    }

    [Test]
    public async Task CanGetShippingTotalDiscountIncludingTax()
    {
        var product = await _productService.GetProductBySkuAsync("FR_451_RB");
        product.AdditionalShippingCharge = 21.25M;
        product.IsFreeShipping = false;
        await _productService.UpdateProductAsync(product);

        _discount.DiscountType = DiscountType.AssignedToShipping;
        await _discountService.InsertDiscountAsync(_discount);

        var (shipping, taxRate, appliedDiscounts) =
            await _orderTotalCalcService.GetShoppingCartShippingTotalAsync(await GetShoppingCartAsync(), true);

        await _discountService.DeleteDiscountAsync(_discount);
        _discount.DiscountType = DiscountType.AssignedToOrderSubTotal;
        product.AdditionalShippingCharge = 0M;
        product.IsFreeShipping = true;
        await _productService.UpdateProductAsync(product);

        appliedDiscounts.Count.Should().Be(1);
        appliedDiscounts.First().Name.Should().Be("Discount 1");
        shipping.Should().NotBeNull();
        //10 - default fixed shipping rate, 42.5 - additional shipping change, -3 - discount
        shipping.Should().Be(54.45M);
        //10 - default fixed tax rate
        taxRate.Should().Be(10);
    }

    [Test]
    public async Task CanGetTaxTotal()
    {
        //207 - items, 10 - shipping (fixed), 20 - payment fee

        TestPaymentMethod.AdditionalHandlingFee = 20M;
        var product = await _productService.GetProductBySkuAsync("FR_451_RB");
        product.IsFreeShipping = false;
        await _productService.UpdateProductAsync(product);

        //1. shipping is taxable, payment fee is taxable
        _taxSettings.ShippingIsTaxable = true;
        _taxSettings.PaymentMethodAdditionalFeeIsTaxable = true;

        await _settingService.SaveSettingAsync(_taxSettings);

        var (taxTotal, taxRates) = await GetService<IOrderTotalCalculationService>().GetTaxTotalAsync(await GetShoppingCartAsync());
        taxTotal.Should().Be(23.7M);
        taxRates.Should().NotBeNull();
        taxRates.Count.Should().Be(1);
        taxRates.ContainsKey(10).Should().BeTrue();
        taxRates[10].Should().Be(23.7M);

        //2. shipping is taxable, payment fee is not taxable
        _taxSettings.PaymentMethodAdditionalFeeIsTaxable = false;
        await _settingService.SaveSettingAsync(_taxSettings);

        (taxTotal, taxRates) = await GetService<IOrderTotalCalculationService>().GetTaxTotalAsync(await GetShoppingCartAsync());
        taxTotal.Should().Be(21.7M);
        taxRates.Should().NotBeNull();
        taxRates.Count.Should().Be(1);
        taxRates.ContainsKey(10).Should().BeTrue();
        taxRates[10].Should().Be(21.7M);

        //3. shipping is not taxable, payment fee is taxable
        _taxSettings.ShippingIsTaxable = false;
        _taxSettings.PaymentMethodAdditionalFeeIsTaxable = true;
        await _settingService.SaveSettingAsync(_taxSettings);

        (taxTotal, taxRates) = await GetService<IOrderTotalCalculationService>().GetTaxTotalAsync(await GetShoppingCartAsync());
        taxTotal.Should().Be(22.7M);
        taxRates.Should().NotBeNull();
        taxRates.Count.Should().Be(1);
        taxRates.ContainsKey(10).Should().BeTrue();
        taxRates[10].Should().Be(22.7M);

        //4. shipping is not taxable, payment fee is not taxable
        _taxSettings.ShippingIsTaxable = false;
        _taxSettings.PaymentMethodAdditionalFeeIsTaxable = false;
        await _settingService.SaveSettingAsync(_taxSettings);

        (taxTotal, taxRates) = await GetService<IOrderTotalCalculationService>().GetTaxTotalAsync(await GetShoppingCartAsync());
        taxTotal.Should().Be(20.7M);
        taxRates.Should().NotBeNull();
        taxRates.Count.Should().Be(1);
        taxRates.ContainsKey(10).Should().BeTrue();
        taxRates[10].Should().Be(20.7M);

        TestPaymentMethod.AdditionalHandlingFee = 0M;
        product = await _productService.GetProductBySkuAsync("FR_451_RB");
        product.IsFreeShipping = false;
        await _productService.UpdateProductAsync(product);
    }

    [Test]
    public async Task CanGetShoppingCartTotalWithoutShippingRequired()
    {
        await TearDown();
        await SetUp();

        //shipping is taxable, payment fee is taxable
        _taxSettings.ShippingIsTaxable = true;
        _taxSettings.PaymentMethodAdditionalFeeIsTaxable = true;

        await _settingService.SaveSettingAsync(_taxSettings);

        TestPaymentMethod.AdditionalHandlingFee = 20M;

        //207 - items, 20 - payment fee, 22.7 - tax
        var (cartTotal, _, _, _, _, _) =
            await _orderTotalCalcService.GetShoppingCartTotalAsync(await GetShoppingCartAsync());
        cartTotal.Should().Be(249.7M);

        TestPaymentMethod.AdditionalHandlingFee = 0M;
    }

    [Test]
    public async Task CanGetShoppingCartTotalWithShippingRequired()
    {
        //shipping is taxable, payment fee is taxable
        _taxSettings.ShippingIsTaxable = true;
        _taxSettings.PaymentMethodAdditionalFeeIsTaxable = true;

        await _settingService.SaveSettingAsync(_taxSettings);

        var product = await _productService.GetProductBySkuAsync("FR_451_RB");
        product.IsFreeShipping = false;
        await _productService.UpdateProductAsync(product);

        TestPaymentMethod.AdditionalHandlingFee = 20M;

        //207 - items, 10 - shipping (fixed), 20 - payment fee, 23.7 - tax
        var (cartTotal, _, _, _, _, _) =
            await _orderTotalCalcService.GetShoppingCartTotalAsync(await GetShoppingCartAsync());
        cartTotal.Should().Be(260.7M);

        TestPaymentMethod.AdditionalHandlingFee = 0M;
        product.IsFreeShipping = true;
        await _productService.UpdateProductAsync(product);
    }

    [Test]
    public async Task CanGetShoppingCartItemUnitPrice()
    {
        var items = await GetShoppingCartAsync();
        var (unitPrice, _, _) = await _shoppingCartService.GetUnitPriceAsync(items[0], true);
        unitPrice.Should().Be(new decimal(27.0));
    }

    [Test]
    public async Task CanGetShoppingCartItemSubtotal()
    {
        var items = await GetShoppingCartAsync();
        var (subTotal, _, _, _) = await _shoppingCartService.GetSubTotalAsync(items[0], true);
        subTotal.Should().Be(new decimal(54.0));
    }

    [Test]
    [TestCase(12.00009, 12.00)]
    [TestCase(12.119, 12.12)]
    [TestCase(12.115, 12.12)]
    [TestCase(12.114, 12.11)]
    public async Task TestGetUnitPriceWhenRoundPricesDuringCalculationIsTruePriceMustBeRounded(decimal inputPrice, decimal expectedPrice)
    {
        // arrange
        var shoppingCartItem = await CreateTestShopCartItemAsync(inputPrice);

        // act
        _shoppingCartSettings.RoundPricesDuringCalculation = true;
        await _settingService.SaveSettingAsync(_shoppingCartSettings);

        var (resultPrice, _, _) = await GetService<IShoppingCartService>().GetUnitPriceAsync(shoppingCartItem, true);

        // assert
        resultPrice.Should().Be(expectedPrice);
    }

    [Test]
    [TestCase(12.00009, 12.00009)]
    [TestCase(12.119, 12.119)]
    [TestCase(12.115, 12.115)]
    [TestCase(12.114, 12.114)]
    public async Task TestGetUnitPriceWhenNotRoundPricesDuringCalculationIsFalsePriceMustNotBeRounded(decimal inputPrice, decimal expectedPrice)
    {
        // arrange            
        var shoppingCartItem = await CreateTestShopCartItemAsync(inputPrice);

        // act
        _shoppingCartSettings.RoundPricesDuringCalculation = false;
        await _settingService.SaveSettingAsync(_shoppingCartSettings);

        var (resultPrice, _, _) = await GetService<IShoppingCartService>().GetUnitPriceAsync(shoppingCartItem, true);

        // assert
        resultPrice.Should().Be(expectedPrice);
    }

    [Test]
    public async Task CanGetShoppingCartTotalDiscount()
    {
        _discount.DiscountType = DiscountType.AssignedToOrderTotal;

        await _discountService.InsertDiscountAsync(_discount);

        //shipping is taxable, payment fee is taxable
        _taxSettings.ShippingIsTaxable = true;
        _taxSettings.PaymentMethodAdditionalFeeIsTaxable = true;

        await _settingService.SaveSettingAsync(_taxSettings);

        TestPaymentMethod.AdditionalHandlingFee = 20M;

        var product = await _productService.GetProductBySkuAsync("FR_451_RB");
        product.IsFreeShipping = false;
        await _productService.UpdateProductAsync(product);

        //207 - items, 10 - shipping (fixed), 20 - payment fee, 23.7 - tax, [-3] - discount
        var (scTotal, discountAmount, appliedDiscounts, _, _, _) = await GetService<IOrderTotalCalculationService>().GetShoppingCartTotalAsync(await GetShoppingCartAsync());
        await _discountService.DeleteDiscountAsync(_discount);
        _discount.DiscountType = DiscountType.AssignedToOrderSubTotal;
        TestPaymentMethod.AdditionalHandlingFee = 0M;

        product.IsFreeShipping = true;
        await _productService.UpdateProductAsync(product);

        scTotal.Should().Be(257.7M);
        discountAmount.Should().Be(3);
        appliedDiscounts.Count.Should().Be(1);
        appliedDiscounts.First().Name.Should().Be("Discount 1");
    }

    [Test]
    public async Task CanConvertRewardPointsToAmount()
    {
        _rewardPointsSettings.ExchangeRate = 15M;

        await _settingService.SaveSettingAsync(_rewardPointsSettings);

        var rewardPointsToAmount = await GetService<IOrderTotalCalculationService>().ConvertRewardPointsToAmountAsync(100);

        _rewardPointsSettings.ExchangeRate = 1M;

        await _settingService.SaveSettingAsync(_rewardPointsSettings);

        rewardPointsToAmount.Should().Be(1500);
    }

    [Test]
    public async Task CanCheckMinimumRewardPointsToUseRequirement()
    {
        _rewardPointsSettings.Enabled = true;
        _rewardPointsSettings.MinimumRewardPointsToUse = 0;

        await _settingService.SaveSettingAsync(_rewardPointsSettings);

        GetService<IOrderTotalCalculationService>().CheckMinimumRewardPointsToUseRequirement(0).Should().BeTrue();
        GetService<IOrderTotalCalculationService>().CheckMinimumRewardPointsToUseRequirement(1).Should().BeTrue();
        GetService<IOrderTotalCalculationService>().CheckMinimumRewardPointsToUseRequirement(10).Should().BeTrue();

        _rewardPointsSettings.MinimumRewardPointsToUse = 2;
        await _settingService.SaveSettingAsync(_rewardPointsSettings);

        GetService<IOrderTotalCalculationService>().CheckMinimumRewardPointsToUseRequirement(0).Should().BeFalse();
        GetService<IOrderTotalCalculationService>().CheckMinimumRewardPointsToUseRequirement(1).Should().BeFalse();
        GetService<IOrderTotalCalculationService>().CheckMinimumRewardPointsToUseRequirement(2).Should().BeTrue();
        GetService<IOrderTotalCalculationService>().CheckMinimumRewardPointsToUseRequirement(10).Should().BeTrue();
    }
}