using System.Globalization;
using Nop.Plugin.Payments.PayPalCommerce.Domain;
using Nop.Plugin.Payments.PayPalCommerce.Models.Admin;
using Nop.Plugin.Payments.PayPalCommerce.Models.Public;
using Nop.Plugin.Payments.PayPalCommerce.Services;
using Nop.Services.Localization;
using Nop.Web.Factories;
using Nop.Web.Models.Checkout;

namespace Nop.Plugin.Payments.PayPalCommerce.Factories;

/// <summary>
/// Represents the plugin model factory
/// </summary>
public class PayPalCommerceModelFactory
{
    #region Fields

    private readonly ICheckoutModelFactory _checkoutModelFactory;
    private readonly ILocalizationService _localizationService;
    private readonly PayPalCommerceServiceManager _serviceManager;
    private readonly PayPalCommerceSettings _settings;

    #endregion

    #region Ctor

    public PayPalCommerceModelFactory(ICheckoutModelFactory checkoutModelFactory,
        ILocalizationService localizationService,
        PayPalCommerceServiceManager serviceManager,
        PayPalCommerceSettings settings)
    {
        _checkoutModelFactory = checkoutModelFactory;
        _localizationService = localizationService;
        _serviceManager = serviceManager;
        _settings = settings;
    }

    #endregion

    #region Methods

    #region Components

    /// <summary>
    /// Prepare the Pay Later messages model
    /// </summary>
    /// <param name="placement">Button placement</param>
    /// <param name="loadScript">Whether to load Pay Later JS script on the page</param>
    /// <returns>
    /// A task that represents the asynchronous operation
    /// The task result contains the Pay Later messages model
    /// </returns>
    public async Task<MessagesModel> PrepareMessagesModelAsync(ButtonPlacement placement, bool loadScript)
    {
        var ((messageConfig, amount, currencyCode), _) = await _serviceManager.PrepareMessagesAsync(_settings, placement);

        return new()
        {
            Placement = placement,
            LoadScript = loadScript,
            Config = messageConfig,
            Amount = amount,
            CurrencyCode = currencyCode,
            //Country = null //PayPal auto detects this
        };
    }

    /// <summary>
    /// Prepare the payment info model
    /// </summary>
    /// <param name="placement">Button placement</param>
    /// <param name="productId">Product id</param>
    /// <returns>
    /// A task that represents the asynchronous operation
    /// The task result contains the payment info model
    /// </returns>
    public async Task<PaymentInfoModel> PreparePaymentInfoModelAsync(ButtonPlacement placement, int? productId = null)
    {
        var (((scriptUrl, clientToken, userToken), (email, name), (messageConfig, amount), (isRecurring, isShippable)), _) = await _serviceManager
            .PreparePaymentDetailsAsync(_settings, placement, productId);

        return new()
        {
            Placement = placement,
            ProductId = productId,
            Script = (scriptUrl, clientToken, userToken),
            Customer = (email, name),
            MessagesModel = new() { Config = messageConfig, Amount = amount },
            Cart = (isRecurring, isShippable)
        };
    }

    #endregion

    #region Checkout

    /// <summary>
    /// Prepare the checkout payment info model
    /// </summary>
    /// <returns>
    /// A task that represents the asynchronous operation
    /// The task result contains the checkout payment info model
    /// </returns>
    public async Task<CheckoutPaymentInfoModel> PrepareCheckoutPaymentInfoModelAsync()
    {
        var (active, paymentMethod) = await _serviceManager.IsActiveAsync(_settings);
        if (!active || paymentMethod is null)
            return new();

        return await _checkoutModelFactory.PreparePaymentInfoModelAsync(paymentMethod);
    }

    /// <summary>
    /// Get the shopping cart warnings
    /// </summary>
    /// <returns>
    /// A task that represents the asynchronous operation
    /// The task result contains the validation warnings
    /// </returns>
    public async Task<IList<string>> GetShoppingCartWarningsAsync()
    {
        var (warnings, error) = await _serviceManager.ValidateShoppingCartAsync();
        if (!string.IsNullOrEmpty(error))
            return [error];

        return warnings;
    }

    /// <summary>
    /// Check whether the shipping is required for the current cart/product
    /// </summary>
    /// <param name="productId">Product id</param>
    /// <returns>
    /// A task that represents the asynchronous operation
    /// The task result contains the check result; error message if exists
    /// </returns>
    public async Task<(bool ShippingIsRequired, string Error)> CheckShippingIsRequiredAsync(int? productId)
    {
        return await _serviceManager.CheckShippingIsRequiredAsync(productId);
    }

    /// <summary>
    /// Prepare the order model
    /// </summary>
    /// <param name="placement">Button placement</param>
    /// <param name="orderId">Order id (when the order is already created)</param>
    /// <param name="paymentSource">Payment source (e.g. PayPal, Card, etc)</param>
    /// <param name="cardId">Saved card id</param>
    /// <param name="saveCard">Whether to save card payment token</param>
    /// <returns>
    /// A task that represents the asynchronous operation
    /// The task result contains the order model
    /// </returns>
    public async Task<OrderModel> PrepareOrderModelAsync(ButtonPlacement placement, string orderId, string paymentSource, int? cardId, bool saveCard)
    {
        var model = new OrderModel();
        (model.CheckoutIsEnabled, model.LoginIsRequired, _) = await _serviceManager.CheckoutIsEnabledAsync();

        //get the order or create a new one
        var (order, error) = string.IsNullOrEmpty(orderId)
            ? await _serviceManager.CreateOrderAsync(_settings, placement, paymentSource, cardId, saveCard)
            : await _serviceManager.GetOrderAsync(_settings, orderId);
        if (!string.IsNullOrEmpty(error) || order is null)
        {
            model.Error = string.IsNullOrEmpty(error)
                ? await _localizationService.GetResourceAsync("Plugins.Payments.PayPalCommerce.Order.Error")
                : error;
            return model;
        }

        //set some order parameters
        model.OrderId = order.Id;
        model.Status = order.Status;
        model.PayerActionUrl = order.PayerActionUrl;

        return model;
    }

    /// <summary>
    /// Prepare the order shipping model
    /// </summary>
    /// <param name="model">Order shipping model</param>
    /// <returns>
    /// A task that represents the asynchronous operation
    /// The task result contains the order shipping model
    /// </returns>
    public async Task<OrderShippingModel> PrepareOrderShippingModelAsync(OrderShippingModel model)
    {
        var (checkoutIsEnabled, loginIsRequired, _) = await _serviceManager.CheckoutIsEnabledAsync();
        if (!checkoutIsEnabled || loginIsRequired)
            return model;

        var (_, error) = await _serviceManager.UpdateOrderShippingAsync(_settings, model.OrderId,
            (model.AddressCity, model.AddressState, model.AddressCountryCode, model.AddressPostalCode),
            (model.OptionId, model.OptionType));
        if (!string.IsNullOrEmpty(error))
            model.Error = error;

        return model;
    }

    /// <summary>
    /// Prepare the order approved model
    /// </summary>
    /// <param name="orderId">Order id</param>
    /// <param name="liabilityShift">Liability shift</param>
    /// <returns>
    /// A task that represents the asynchronous operation
    /// The task result contains the order approved model
    /// </returns>
    public async Task<OrderApprovedModel> PrepareOrderApprovedModelAsync(string orderId, string liabilityShift)
    {
        var model = new OrderApprovedModel();
        (model.CheckoutIsEnabled, model.LoginIsRequired, var cart) = await _serviceManager.CheckoutIsEnabledAsync();
        if (cart?.Any() != true)
            return model;

        var ((order, payNow), error) = await _serviceManager.OrderIsApprovedAsync(_settings, orderId, null, liabilityShift);
        if (!string.IsNullOrEmpty(error))
            model.Error = error;
        else
            (model.OrderId, model.PayNow) = (order?.Id, payNow);

        return model;
    }

    /// <summary>
    /// Prepare the order confirmation model
    /// </summary>
    /// <param name="orderId">Order id</param>
    /// <param name="orderGuid">Internal order id (used in 3D Secure cases)</param>
    /// <param name="liabilityShift">Liability shift</param>
    /// <param name="approve">Whether the order is approved now; pass false to avoid order approval reprocessing</param>
    /// <returns>
    /// A task that represents the asynchronous operation
    /// The task result contains the order confirmation model
    /// </returns>
    public async Task<OrderConfirmModel> PrepareOrderConfirmModelAsync(string orderId, string orderGuid, string liabilityShift, bool approve)
    {
        var model = new OrderConfirmModel { OrderId = orderId, OrderGuid = orderGuid, LiabilityShift = liabilityShift };
        (model.CheckoutIsEnabled, model.LoginIsRequired, var cart) = await _serviceManager.CheckoutIsEnabledAsync();
        if (cart?.Any() != true)
            return model;

        //prepare common confirmation model parameters
        var checkoutConfirmModel = await _checkoutModelFactory.PrepareConfirmOrderModelAsync(cart);
        model.DisplayCaptcha = checkoutConfirmModel.DisplayCaptcha;
        model.MinOrderTotalWarning = checkoutConfirmModel.MinOrderTotalWarning;
        model.TermsOfServiceOnOrderConfirmPage = checkoutConfirmModel.TermsOfServiceOnOrderConfirmPage;
        model.TermsOfServicePopup = checkoutConfirmModel.TermsOfServicePopup;
        if (!approve)
            return model;

        //order is approved now
        var ((order, _), error) = await _serviceManager.OrderIsApprovedAsync(_settings, orderId, orderGuid, liabilityShift);
        if (!string.IsNullOrEmpty(error))
            model.Error = error;
        else
            model.OrderId = order?.Id;

        return model;
    }

    /// <summary>
    /// Prepare the order completed model
    /// </summary>
    /// <param name="orderId">Order id</param>
    /// <param name="liabilityShift">Liability shift</param>
    /// <returns>
    /// A task that represents the asynchronous operation
    /// The task result contains the order completed model
    /// </returns>
    public async Task<OrderCompletedModel> PrepareOrderCompletedModelAsync(string orderId, string liabilityShift)
    {
        var model = new OrderCompletedModel();
        (model.CheckoutIsEnabled, model.LoginIsRequired, var cart) = await _serviceManager.CheckoutIsEnabledAsync();
        if (cart?.Any() != true)
            return model;

        //first place an order
        var ((nopOrder, order), error) = await _serviceManager.PlaceOrderAsync(_settings, orderId, liabilityShift);
        if (!string.IsNullOrEmpty(error))
            model.Error = error;
        else if (order is null)
            model.Error = await _localizationService.GetResourceAsync("Plugins.Payments.PayPalCommerce.Order.Error");
        else
            model.OrderId = nopOrder.Id.ToString();

        if (nopOrder is null || order is null)
            return model;

        //then confirm the placed order
        var (_, warning) = await _serviceManager.ConfirmOrderAsync(_settings, nopOrder, order);
        if (!string.IsNullOrEmpty(warning))
            model.Warning = warning;

        return model;
    }

    /// <summary>
    /// Prepare the Apple Pay model
    /// </summary>
    /// <param name="placement">Button placement</param>
    /// <param name="shippingIsSet">Whether the shipping details are set</param>
    /// <returns>
    /// A task that represents the asynchronous operation
    /// The task result contains the Apple Pay model
    /// </returns>
    public async Task<ApplePayModel> PrepareApplePayModelAsync(ButtonPlacement placement, bool shippingIsSet = false)
    {
        var model = new ApplePayModel();
        (model.CheckoutIsEnabled, model.LoginIsRequired, _) = await _serviceManager.CheckoutIsEnabledAsync();

        var ((amount, billingAddress, shippingAddress, shipping, storeName), error) = await _serviceManager
            .GetAppleTransactionInfoAsync(_settings, placement);
        if (!string.IsNullOrEmpty(error))
        {
            model.Error = error;
            return model;
        }

        //function to prepare items
        async Task<(string Type, string Price, string Status, string Label)> prepareItemAsync(string type, string value, string resourcePostfix)
        {
            if (!decimal.TryParse(value, NumberStyles.Any, CultureInfo.InvariantCulture, out var amount) || amount <= decimal.Zero)
                return default;

            if (resourcePostfix == "Discount")
                amount = -amount;

            var price = amount.ToString("0.00", CultureInfo.InvariantCulture);
            var status = "final";
            var label = type == "TOTAL"
                ? storeName
                : await _localizationService.GetResourceAsync($"Plugins.Payments.PayPalCommerce.ApplePay.{resourcePostfix}");
            return (type, price, status, label);
        }

        //line items (subtotal, tax, etc)
        var items = new List<(string Type, string Price, string Status, string Label)>
        {
            await prepareItemAsync("TOTAL", amount.Value, "Total"),
            await prepareItemAsync("SUBTOTAL", amount.Breakdown.ItemTotal.Value, "Subtotal"),
            await prepareItemAsync("TAX", amount.Breakdown.TaxTotal.Value, "Tax"),
            await prepareItemAsync("SHIPPING", amount.Breakdown.Shipping.Value, "Shipping"),
            await prepareItemAsync("DISCOUNT", amount.Breakdown.Discount.Value, "Discount")
        };

        model.Placement = placement;
        model.CurrencyCode = amount.CurrencyCode;
        model.BillingAddress = billingAddress;
        model.ShippingAddress = shippingAddress;
        model.Items = items.Where(item => !string.IsNullOrEmpty(item.Type)).ToList();
        model.ShippingOptions = shipping?.Options
            ?.Select(option => ($"{option.Id}|{option.Type}", option.Id, option.Label, option.Amount.Value))
            .ToList();

        return model;
    }

    /// <summary>
    /// Prepare the Apple Pay shipping model
    /// </summary>
    /// <param name="model">Apple Pay shipping model</param>
    /// <returns>
    /// A task that represents the asynchronous operation
    /// The task result contains the Apple Pay shipping model
    /// </returns>
    public async Task<ApplePayShippingModel> PrepareApplePayShippingModelAsync(ApplePayShippingModel model)
    {
        //prepare updated shipping details
        var (shipping, error) = await _serviceManager.UpdateAppleShippingAsync(model.Placement,
            (model.AddressCity, model.AddressState, model.AddressCountryCode, model.AddressPostalCode),
            model.OptionId);
        if (!string.IsNullOrEmpty(error) || shipping?.Options is null)
        {
            model.Error = string.IsNullOrEmpty(error)
                ? await _localizationService.GetResourceAsync("Plugins.Payments.PayPalCommerce.Order.Error")
                : error;
            return model;
        }

        //get line items from the main model
        var applePayModel = await PrepareApplePayModelAsync(model.Placement, true);
        if (!string.IsNullOrEmpty(applePayModel.Error))
        {
            model.Error = applePayModel.Error;
            return model;
        }

        model.Items = applePayModel.Items;
        model.ShippingOptions = shipping?.Options
            ?.Select(option => ($"{option.Id}|{option.Type}", option.Id, option.Label, option.Amount.Value))
            .ToList();

        return model;
    }

    /// <summary>
    /// Prepare the Google Pay model
    /// </summary>
    /// <param name="placement">Button placement</param>
    /// <param name="shippingIsSet">Whether the shipping details are set</param>
    /// <returns>
    /// A task that represents the asynchronous operation
    /// The task result contains the Google Pay model
    /// </returns>
    public async Task<GooglePayModel> PrepareGooglePayModelAsync(ButtonPlacement placement, bool shippingIsSet = false)
    {
        var model = new GooglePayModel();
        (model.CheckoutIsEnabled, model.LoginIsRequired, _) = await _serviceManager.CheckoutIsEnabledAsync();

        var ((amount, country, shippingIsRequired), error) = await _serviceManager.GetGoogleTransactionInfoAsync(placement);
        if (!string.IsNullOrEmpty(error))
        {
            model.Error = error;
            return model;
        }

        shippingIsRequired &= placement != ButtonPlacement.PaymentMethod;

        //function to prepare items
        async Task<(string Type, string Price, string Status, string Label)> prepareItemAsync(string type, string value, string resourcePostfix)
        {
            if (!decimal.TryParse(value, NumberStyles.Any, CultureInfo.InvariantCulture, out var amount) || amount <= decimal.Zero)
                return default;

            if (resourcePostfix == "Discount")
                amount = -amount;

            var price = amount.ToString("0.00", CultureInfo.InvariantCulture);
            var status = !shippingIsRequired || shippingIsSet ? "FINAL" : (type == "TOTAL" ? "ESTIMATED" : "PENDING");
            var label = await _localizationService.GetResourceAsync($"Plugins.Payments.PayPalCommerce.GooglePay.{resourcePostfix}");
            return (type, price, status, label);
        }

        //display items (subtotal, tax, etc)
        var items = new List<(string Type, string Price, string Status, string Label)>
        {
            await prepareItemAsync("TOTAL", amount.Value, "Total"),
            await prepareItemAsync("SUBTOTAL", amount.Breakdown.ItemTotal.Value, "Subtotal"),
            await prepareItemAsync("TAX", amount.Breakdown.TaxTotal.Value, "Tax"),
            await prepareItemAsync("LINE_ITEM", amount.Breakdown.Shipping.Value, "Shipping"),
            await prepareItemAsync("LINE_ITEM", amount.Breakdown.Discount.Value, "Discount")
        };

        model.Placement = placement;
        model.Country = country;
        model.CurrencyCode = amount.CurrencyCode;
        model.ShippingIsRequired = shippingIsRequired;
        model.Items = items.Where(item => !string.IsNullOrEmpty(item.Type)).ToList();

        return model;
    }

    /// <summary>
    /// Prepare the Google Pay shipping model
    /// </summary>
    /// <param name="model">Google Pay shipping model</param>
    /// <returns>
    /// A task that represents the asynchronous operation
    /// The task result contains the Google Pay shipping model
    /// </returns>
    public async Task<GooglePayShippingModel> PrepareGooglePayShippingModelAsync(GooglePayShippingModel model)
    {
        //prepare updated shipping details
        var (shipping, error) = await _serviceManager.UpdateGoogleShippingAsync(model.Placement,
            (model.AddressCity, model.AddressState, model.AddressCountryCode, model.AddressPostalCode),
            model.OptionId);
        if (!string.IsNullOrEmpty(error) || shipping?.Options is null)
        {
            model.Error = string.IsNullOrEmpty(error)
                ? await _localizationService.GetResourceAsync("Plugins.Payments.PayPalCommerce.Order.Error")
                : error;
            return model;
        }

        //get common model parameters
        var googlePayModel = await PrepareGooglePayModelAsync(model.Placement, true);
        if (!string.IsNullOrEmpty(googlePayModel.Error))
        {
            model.Error = googlePayModel.Error;
            return model;
        }

        model.Country = googlePayModel.Country;
        model.CurrencyCode = googlePayModel.CurrencyCode;
        model.Items = googlePayModel.Items;
        model.Options = shipping?.Options?.Select(option =>
        {
            var id = $"{option.Id}|{option.Type}";
            var name = $"{option.Amount.Value} {option.Amount.CurrencyCode}: {option.Id}";

            if (string.IsNullOrEmpty(model.OptionId) && option.Selected == true)
                model.OptionId = id;

            return (id, name, option.Label);
        }).ToList();

        return model;
    }

    /// <summary>
    /// Prepare the setup token model
    /// </summary>
    /// <returns>
    /// A task that represents the asynchronous operation
    /// The task result contains the setup token model
    /// </returns>
    public async Task<SetupTokenModel> PrepareSetupTokenModelAsync()
    {
        var model = new SetupTokenModel();
        (model.CheckoutIsEnabled, model.LoginIsRequired, _) = await _serviceManager.CheckoutIsEnabledAsync();

        var (paymentToken, error) = await _serviceManager.CreateSetupTokenAsync(_settings);
        if (!string.IsNullOrEmpty(error) || paymentToken is null)
        {
            model.Error = string.IsNullOrEmpty(error)
                ? await _localizationService.GetResourceAsync("Plugins.Payments.PayPalCommerce.Order.Error")
                : error;
            return model;
        }

        model.Status = paymentToken.Status;
        model.PayerActionUrl = paymentToken.PayerActionUrl;

        return model;
    }

    /// <summary>
    /// Prepare the recurring order model
    /// </summary>
    /// <param name="setupTokenId">Setup token id</param>
    /// <returns>
    /// A task that represents the asynchronous operation
    /// The task result contains the recurring order model
    /// </returns>
    public async Task<RecurringOrderModel> PrepareRecurringOrderModelAsync(string setupTokenId)
    {
        var model = new RecurringOrderModel();
        (model.CheckoutIsEnabled, model.LoginIsRequired, _) = await _serviceManager.CheckoutIsEnabledAsync();

        var (order, error) = await _serviceManager.CreateRecurringOrderAsync(_settings, setupTokenId);
        if (!string.IsNullOrEmpty(error) || order is null)
        {
            model.Error = string.IsNullOrEmpty(error)
                ? await _localizationService.GetResourceAsync("Plugins.Payments.PayPalCommerce.Order.Error")
                : error;
            return model;
        }

        model.OrderId = order.Id;
        model.Status = order.Status;

        return model;
    }

    #endregion

    #region Payment tokens

    /// <summary>
    /// Prepare payment token list model
    /// </summary>
    /// <param name="deleteTokenId">Identifier of the token to delete</param>
    /// <param name="defaultTokenId">Identifier of the token to mark as default</param>
    /// <returns>
    /// A task that represents the asynchronous operation
    /// The task result contains the payment token list model
    /// </returns>
    public async Task<PaymentTokenListModel> PreparePaymentTokenListModelAsync(int? deleteTokenId = null, int? defaultTokenId = null)
    {
        var model = new PaymentTokenListModel();

        var (active, _) = await _serviceManager.IsActiveAsync(_settings);
        if (!active)
            return model;

        //get all customer's payment tokens
        var (tokens, error) = await _serviceManager.GetPaymentTokensAsync(_settings, true, deleteTokenId, defaultTokenId);
        if (!string.IsNullOrEmpty(error))
        {
            model.VaultIsEnabled = true;
            model.Error = error;
            return model;
        }
        if (!_settings.UseVault && tokens?.Any() != true)
            return model;

        model.VaultIsEnabled = true;
        model.PaymentTokens = tokens.Select(token => new PaymentTokenModel
        {
            Id = token.Id,
            IsPrimaryMethod = token.IsPrimaryMethod,
            Type = token.Type,
            Title = token.Title,
            Expiration = token.Expiration
        }).ToList();

        return model;
    }

    /// <summary>
    /// Prepare saved card list model
    /// </summary>
    /// <param name="placement">Button placement</param>
    /// <returns>
    /// A task that represents the asynchronous operation
    /// The task result contains the saved card list model
    /// </returns>
    public async Task<SavedCardListModel> PrepareSavedCardListModelAsync(ButtonPlacement placement)
    {
        var model = new SavedCardListModel();

        //get customer's card payment tokens
        var (tokens, error) = await _serviceManager.GetSavedCardsAsync(_settings, placement);
        if (!string.IsNullOrEmpty(error))
        {
            model.VaultIsEnabled = true;
            model.Error = error;
            return model;
        }
        if (tokens?.Any() != true)
            return model;

        model.VaultIsEnabled = true;
        var prefix = await _localizationService.GetResourceAsync("Plugins.Payments.PayPalCommerce.Card.Prefix");
        model.PaymentTokens = tokens.Select(token => new PaymentTokenModel
        {
            Id = token.Id,
            IsPrimaryMethod = token.IsPrimaryMethod,
            Type = token.Type,
            Title = $"{prefix} {token.Title}",
            Expiration = token.Expiration
        }).ToList();

        return model;
    }

    #endregion

    #region Onboarding

    /// <summary>
    /// Prepare the merchant model
    /// </summary>
    /// <param name="settings">Plugin settings</param>
    /// <returns>
    /// A task that represents the asynchronous operation
    /// The task result contains the merchant model
    /// </returns>
    public async Task<MerchantModel> PrepareMerchantModelAsync(PayPalCommerceSettings settings)
    {
        var model = new MerchantModel { Messages = new() { Success = new(), Warning = new(), Error = new() } };

        //get merchant details
        var (merchant, error) = await _serviceManager.GetMerchantAsync(settings);
        if (merchant is null || !string.IsNullOrEmpty(error))
        {
            model.Messages.Error.Add(await _localizationService.GetResourceAsync("Plugins.Payments.PayPalCommerce.Onboarding.Error"));
            return model;
        }

        model.MerchantId = merchant.MerchantId;
        model.ConfiguratorSupported = merchant.ConfiguratorSupported;

        //check the features availability and prepare warning notifications
        model.AdvancedCardsEnabled = merchant.AdvancedCards.Active;
        if (!merchant.AdvancedCards.Active)
        {
            var message = "You are not able to offer Advanced Credit and Debit Card payments because its onboarding status is {0}. " +
                "Please reach out to PayPal for more information.";
            model.Messages.Warning.Add(string.Format(message, merchant.AdvancedCards.Status));
        }
        model.ApplePayEnabled = merchant.ApplePay.Active;
        if (!merchant.ApplePay.Active)
        {
            var message = "You are not able to offer Apple Pay because its onboarding status is {0}. " +
                "Please reach out to PayPal for more information.";
            model.Messages.Warning.Add(string.Format(message, merchant.ApplePay.Status));
        }
        model.GooglePayEnabled = merchant.GooglePay.Active;
        if (!merchant.GooglePay.Active)
        {
            var message = "You are not able to offer Google Pay because its onboarding status is {0}. " +
                "Please reach out to PayPal for more information.";
            model.Messages.Warning.Add(string.Format(message, merchant.GooglePay.Status));
        }
        model.VaultingEnabled = merchant.Vaulting.Active;
        if (!merchant.Vaulting.Active)
        {
            var message = "You are not able to offer the Vaulting functionality because its onboarding status is {0}. " +
                "Please reach out to PayPal for more information.";
            model.Messages.Warning.Add(string.Format(message, merchant.Vaulting.Status));
        }

        //check special details of "Advanced Cards" feature and prepare warning notifications
        if (merchant.AdvancedCardsDetails.BelowLimit)
        {
            model.Messages.Warning.Add("PayPal requires more information about your business on paypal.com to fully enable " +
                "Advanced Credit and Debit Card Payments beyond a $500 receiving limitation. " +
                "Please visit <a href=\"https://www.paypal.com/policy/hub/kyc\" target=\"_blank\">https://www.paypal.com/policy/hub/kyc</a>. " +
                "After reaching the $500 limit you will still be offering all other PayPal payment methods except " +
                "Advanced Credit and Debit Card Payments to your customers.");
        }
        if (merchant.AdvancedCardsDetails.OverLimit)
        {
            model.Messages.Warning.Add("PayPal requires more information about your business on paypal.com to fully enable " +
                "Advanced Credit and Debit Card Payments beyond a $500 receiving limitation. " +
                "Please visit <a href=\"https://www.paypal.com/policy/hub/kyc\" target=\"_blank\">https://www.paypal.com/policy/hub/kyc</a>. " +
                "You already surpassed the $500 limitation hence aren't able to process more " +
                "Advanced Credit and Debit Card Payments transactions but are still offering all other PayPal payment methods to your customers. " +
                "Once sorted, simply revisit this page to refresh the onboarding status.");
        }
        if (merchant.AdvancedCardsDetails.NeedMoreData)
        {
            model.Messages.Warning.Add("PayPal requires more information about your business on paypal.com to fully enable " +
                "Advanced Credit and Debit Card Payments. " +
                "Please visit <a href=\"https://www.paypal.com/policy/hub/kyc\" target=\"_blank\">https://www.paypal.com/policy/hub/kyc</a>. " +
                "Until then you are still offering all other PayPal payment methods to your customers. " +
                "Once sorted, simply revisit this page to refresh the onboarding status.");
        }
        if (merchant.AdvancedCardsDetails.OnReview)
        {
            model.Messages.Warning.Add("PayPal is currently reviewing your information after which you’ll be notified of your eligibility for " +
                "Advanced Credit and Debit Card Payments. Until then you are still offering all other PayPal payment methods to your customers.");
        }
        if (merchant.AdvancedCardsDetails.Denied)
        {
            model.Messages.Warning.Add(string.Format("PayPal denied your application to use Advanced Credit and Debit Card Payments. " +
                "You can retry in 90 days, on {0} on paypal.com. Until then you are still offering all other " +
                "PayPal payment methods to your customers.", DateTime.UtcNow.AddDays(90).ToShortDateString()));
        }

        //no need to check further details, if the plugin is already connected 
        if (PayPalCommerceServiceManager.IsConnected(settings))
            return model;

        //check merchant status
        model.DisplayStatus = true;
        model.AccountCreated = !string.IsNullOrEmpty(merchant.MerchantId);
        model.EmailConfirmed = merchant.PrimaryEmailConfirmed ?? false;
        model.PaymentsReceivable = merchant.PaymentsReceivable ?? false;
        if (!model.EmailConfirmed || !model.PaymentsReceivable)
        {
            model.Messages.Warning.Add(await _localizationService.GetResourceAsync("Plugins.Payments.PayPalCommerce.Onboarding.InProcess"));
            if (!model.PaymentsReceivable)
            {
                model.Messages.Warning.Add("Attention: You currently cannot receive payments due to possible restriction on your PayPal account. " +
                    "Please reach out to PayPal Customer Support or connect to " +
                    "<a href=\"https://www.paypal.com/\" target=\"_blank\">https://www.paypal.com/</a> for more information. " +
                    "Once sorted, simply revisit this page to refresh the onboarding status.");
            }
            if (!model.EmailConfirmed)
            {
                model.Messages.Warning.Add("Attention: Please confirm your email address on " +
                    "<a href=\"https://www.paypal.com/businessprofile/settings\" target=\"_blank\">https://www.paypal.com/businessprofile/settings</a>" +
                    " in order to receive payments! You currently cannot receive payments. " +
                    "Once done, simply revisit this page to refresh the onboarding status.");
            }

            return model;
        }

        if (!PayPalCommerceServiceManager.IsConfigured(settings))
        {
            model.Messages.Error.Add(await _localizationService.GetResourceAsync("Plugins.Payments.PayPalCommerce.Onboarding.Error"));
            return model;
        }

        model.Messages.Success.Add(await _localizationService.GetResourceAsync("Plugins.Payments.PayPalCommerce.Onboarding.Completed"));

        return model;
    }

    #endregion

    #endregion
}