package com.braintree.controllers.pages;

import com.braintree.configuration.service.BrainTreeConfigService;
import com.braintree.controllers.handler.BraintreePayPalResponseExpressCheckoutHandler;
import com.braintree.enums.BrainTreePaymentMethod;
import com.braintree.enums.BraintreePageType;
import com.braintree.exceptions.BrainTreeConnectWithPayPalException;
import com.braintree.exceptions.BrainTreeUnverifiedAccountPayPalException;
import com.braintree.facade.BrainTreeConnectWithPayPalFacade;
import com.braintree.facade.BrainTreeRegistrationUserFacade;
import com.braintree.facade.BraintreeB2BRegistrationUserFacade;
import com.braintree.facade.BrainTreeUserFacade;
import com.braintree.facade.BraintreeConfigurationFacade;
import com.braintree.facade.BraintreeConnectWithPayPalNotificationFacade;
import com.braintree.facade.BrainTreeSetUpPasswordFacade;
import com.braintree.facade.impl.DefaultBrainTreeCheckoutFacade;
import com.braintree.facade.impl.DefaultBrainTreePaymentFacade;
import com.braintree.hybris.data.*;
import com.braintree.method.BrainTreePaymentService;
import com.braintree.security.PayPalAutoLoginStrategy;
import com.braintree.util.BrainTreeUtils;
import com.braintree.util.GenericBuilder;
import de.hybris.platform.acceleratorstorefrontcommons.breadcrumb.Breadcrumb;
import de.hybris.platform.acceleratorstorefrontcommons.controllers.pages.AbstractLoginPageController;
import de.hybris.platform.acceleratorstorefrontcommons.controllers.util.GlobalMessages;
import de.hybris.platform.acceleratorstorefrontcommons.forms.RegisterForm;
import de.hybris.platform.b2bacceleratoraddon.constants.B2bacceleratoraddonWebConstants;
import de.hybris.platform.b2bacceleratoraddon.forms.RegistrationForm;
import de.hybris.platform.b2bacceleratorfacades.exception.CustomerAlreadyExistsException;
import de.hybris.platform.b2bacceleratorfacades.exception.RegistrationNotEnabledException;
import de.hybris.platform.b2bacceleratorfacades.registration.B2BRegistrationFacade;
import de.hybris.platform.b2bcommercefacades.data.B2BRegistrationData;
import de.hybris.platform.cms2.exceptions.CMSItemNotFoundException;
import de.hybris.platform.cms2.model.pages.AbstractPageModel;
import de.hybris.platform.cms2.model.pages.ContentPageModel;
import de.hybris.platform.commercefacades.user.data.AddressData;
import de.hybris.platform.commercefacades.user.data.CountryData;
import de.hybris.platform.commercefacades.user.data.CustomerData;
import de.hybris.platform.converters.Converters;
import de.hybris.platform.core.model.c2l.CountryModel;
import de.hybris.platform.servicelayer.dto.converter.Converter;
import de.hybris.platform.servicelayer.i18n.CommonI18NService;
import de.hybris.platform.servicelayer.session.SessionService;
import de.hybris.platform.util.localization.Localization;
import org.apache.log4j.Logger;
import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.validation.BindingResult;
import org.springframework.validation.Validator;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.servlet.mvc.support.RedirectAttributes;
import org.springframework.web.servlet.support.RequestContextUtils;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.util.Collections;
import java.util.List;
import java.util.Objects;
import java.util.Optional;

import static com.braintree.constants.Braintreeb2baddonWebConstants.BRAINTREE_CHECKOUT_DATA;
import static com.braintree.constants.Braintreeb2baddonWebConstants.BRAINTREE_CONFIG_DATA;
import static com.braintree.constants.BraintreeConstants.PropertyConfiguration.BRAINTREE_ACCEPTED_CREDIT_CARD_PAYMENT_METHODS;
import static com.braintree.controllers.Braintreeb2baddonControllerConstants.ACCESS_TOKEN;
import static com.braintree.controllers.Braintreeb2baddonControllerConstants.BRAINTREE_CONNECT_WITH_PAYPAL_ADDITIONAL_DATA;
import static com.braintree.controllers.Braintreeb2baddonControllerConstants.BRAINTREE_THE_PASSWORD_IS_NOT_CORRECT_ERROR;
import static com.braintree.controllers.Braintreeb2baddonControllerConstants.BRAINTREE_USER_ACCOUNT_HAS_BEEN_DISABLED_ERROR;
import static com.braintree.controllers.Braintreeb2baddonControllerConstants.PREPARATION_FOR_LINK_PAYPAL_ACCOUNT;
import static com.braintree.controllers.Braintreeb2baddonControllerConstants.BRAINTREE_PAYPAL_CONNECT_LOGIN_LINKED_ACCOUNTS;
import static com.braintree.controllers.Braintreeb2baddonControllerConstants.BRAINTREE_PAYPAL_CONNECT_REGISTRATION_MESSAGE_SET_UP_PASSWORD_FOR_NEW_USER;
import static com.braintree.controllers.Braintreeb2baddonControllerConstants.BREADCRUBMS;
import static com.braintree.controllers.Braintreeb2baddonControllerConstants.DISABLE_LOGIN_VIA_PAYPAL_TO_THE_EXISTING_USER_MESSAGE;
import static com.braintree.controllers.Braintreeb2baddonControllerConstants.LOGIN_LABEL;
import static com.braintree.controllers.Braintreeb2baddonControllerConstants.LOGIN_PAGE_REDIRECT;
import static com.braintree.controllers.Braintreeb2baddonControllerConstants.PAYPAL_CONNECT_GLOBAL_ERROR;
import static com.braintree.controllers.Braintreeb2baddonControllerConstants.REGISTER_REQUEST_CREATED;
import static com.braintree.controllers.Braintreeb2baddonControllerConstants.REGISTER_REQUEST_NOR_APPROVED;
import static com.braintree.controllers.Braintreeb2baddonControllerConstants.SAVE_PAYMENT_INFO_PAGE;
import static com.braintree.controllers.Braintreeb2baddonControllerConstants.SCP_LINK_CREATE_ACCOUNT;
import static com.braintree.controllers.Braintreeb2baddonControllerConstants.Views.Pages.Login.CONNECT_WITH_PAYPAL_SECURITY_LAYER_PAGE;
import static de.hybris.platform.util.localization.Localization.getLocalizedString;

@Controller
@RequestMapping(value = "/paypal/connect")
public class BrainTreeConnectWithPayPalController extends AbstractLoginPageController {

    private static final Logger LOG = Logger.getLogger(BrainTreeConnectWithPayPalController.class);

    private static final String IS_USER_APPROVED = "isUserApproved";
    private static final String PAYER_ID = "payerId";
    private static final String ADDRESS_DATA = "addressData";
    private static final String SHOULD_SAVE_ADDRESS = "shouldSaveAddress";
    private static final String SHOULD_SAVE_PAYMENT_INFO = "shouldSavePaymentInfo";
    public static final String USER_EMAIL = "userEmail";
    private static final String USER_DATA_ATTRIBUTE = "userData";
    private static final String BASE_SITE_NAME_ATTRIBUTE = "baseSiteName";
    private static final String SAVE_ADDRESS_AND_PAYMENT_INFOS = "saveAddressAndPaymentInfos";
    private static final String SAVE_ADDRESS_AND_PAYMENT_INFOS_URL = "paypal/connect/saveAddressAndPaymentInfos";
    private static final String NOT_SAVE_PAYMENT_AND_ADDRESS_INFOS = "notSavePaymentAndAddressInfos";
    private static final String NOT_SAVE_PAYMENT_AND_ADDRESS_INFOS_URL = "paypal/connect/notSavePaymentAndAddressInfos";
    private static final String CONNECT_WITH_PAYPAL_SECURITY_LAYER = "/paypal/connect/verify-password";
    private static final String PAYPAL_CHECKOUT_DATA = "payPalCheckoutData";
    private static final String SHOULD_SET_UP_PASSWORD_MESSAGE = "shouldShowSetUpPasswordMessage";
    public static final String REDIRECT = "redirect:";
    public static final String CONNECT_LOGIN_SAVE_PAYMENT_INFO_AND_ADDRESS_PAGE = "/paypal/connect/savePaymentInfoAndAddressPage";
    private static final String UNVERIFIED_ACCOUNT_ERROR = "braintree.paypal.unverified.account.error";

    @Resource(name = "brainTreePaymentFacadeImpl")
    private DefaultBrainTreePaymentFacade brainTreePaymentFacade;

    @Resource(name = "defaultBraintreeConfigurationFacade")
    private BraintreeConfigurationFacade brainTreeConfigFacade;

    @Resource(name = "brainTreePaymentService")
    private BrainTreePaymentService brainTreePaymentService;

    @Resource(name = "braintreePayPalResponseExpressCheckoutHandler")
    BraintreePayPalResponseExpressCheckoutHandler braintreePayPalResponseExpressCheckoutHandler;

    @Resource(name = "brainTreeConfigService")
    private BrainTreeConfigService brainTreeConfigService;

    @Resource(name = "brainTreeCheckoutFacade")
    private DefaultBrainTreeCheckoutFacade brainTreeCheckoutFacade;


    @Resource(name = "brainTreeConnectWithPayPalFacade")
    private BrainTreeConnectWithPayPalFacade brainTreeConnectWithPayPalFacade;

    @Resource(name = "braintreeConnectWithPayPalNotificationFacade")
    private BraintreeConnectWithPayPalNotificationFacade braintreeConnectWithPayPalNotificationFacade;

    @Resource(name = "brainTreeRegistrationUserFacade")
    private BrainTreeRegistrationUserFacade brainTreeRegistrationUserFacade;

    @Resource(name = "b2bRegistrationFacade")
    private B2BRegistrationFacade b2bRegistrationFacade;

    @Resource(name = "braintreeB2BRegistrationUserFacade")
    private BraintreeB2BRegistrationUserFacade brainTreeB2BRegistrationUserFacade;

    @Resource(name = "userFacade")
    private BrainTreeUserFacade brainTreeUserFacade;

    @Resource(name = "brainTreeSetUpPasswordFacade")
    private BrainTreeSetUpPasswordFacade brainTreeSetUpPasswordFacade;

    @Resource(name = "payPalAutoLoginStrategy")
    private PayPalAutoLoginStrategy payPalAutoLoginStrategy;

    @Resource(name = "sessionService")
    private SessionService sessionService;

    @Resource(name = "commonI18NService")
    private CommonI18NService commonI18NService;

    @Resource(name = "countryConverter")
    private Converter<CountryModel, CountryData> countryConverter;

    @Resource(name = "brainTreeConnectWithPayPalRegistrationValidator")
    private Validator registrationValidator;

    @GetMapping(value = "/register")
    public String getRegister(final HttpServletRequest request, final HttpServletResponse response,
                              final RedirectAttributes redirectAttributes, final Model model, final HttpSession session,
                              @RequestParam(required = false) String code) throws CMSItemNotFoundException {
        if (code == null) {
            return getDefaultLoginPage(false, session, model);
        }
        BrainTreeConnectWithPayPalUserData userData;
        String accessToken;
        String email;
        BrainTreeConnectWithPayPalUserExistB2BData userExistB2BData;
        try {
            accessToken = brainTreeConnectWithPayPalFacade.exchangeAuthorizationCodeToAccessToken(code);
            userData = brainTreeConnectWithPayPalFacade.getUserDataByAccessToken(accessToken);
            email = brainTreeRegistrationUserFacade.getEmailFromPayPalUserData(userData);
            userExistB2BData = brainTreeB2BRegistrationUserFacade.isCustomerExistAndApprovedByUID(email);
        } catch (BrainTreeUnverifiedAccountPayPalException e) {
            LOG.error(String.format("Unverified PayPal account : %s", e.getMessage()), e);
            GlobalMessages.addFlashMessage(redirectAttributes,
                    GlobalMessages.ERROR_MESSAGES_HOLDER,
                    getLocalizedString(UNVERIFIED_ACCOUNT_ERROR));
            return LOGIN_PAGE_REDIRECT;
        } catch (BrainTreeConnectWithPayPalException e) {
            LOG.error(String.format("Exception raised in getRegister method : %s", e.getMessage()), e);
            return LOGIN_PAGE_REDIRECT;
        }

        sessionService.setAttribute(USER_EMAIL, email);
        if (userExistB2BData.isExist() && userExistB2BData.isApproved()) {
            if (!brainTreeUserFacade.isCustomerWithPayerIdExist(userData.getPayerId())) {
                if (!brainTreeConfigService.isEnableLoginViaPayPalToTheExistingUser()) {
                    GlobalMessages.addFlashMessage(RequestContextUtils.getOutputFlashMap(request),
                            GlobalMessages.ERROR_MESSAGES_HOLDER,
                            getLocalizedString(DISABLE_LOGIN_VIA_PAYPAL_TO_THE_EXISTING_USER_MESSAGE), null);
                    return LOGIN_PAGE_REDIRECT;
                }

                sessionService.setAttribute(USER_DATA_ATTRIBUTE, userData);
                if (brainTreeConfigService.isPayPalLippAdditionalSecurityEnable()) {
                    return REDIRECT + CONNECT_WITH_PAYPAL_SECURITY_LAYER;
                } else {
                    return REDIRECT + PREPARATION_FOR_LINK_PAYPAL_ACCOUNT;
                }
            }
            if (Objects.nonNull(sessionService.getAttribute(SHOULD_SET_UP_PASSWORD_MESSAGE))) {
                GlobalMessages.addFlashMessage(redirectAttributes, GlobalMessages.CONF_MESSAGES_HOLDER,
                        Localization.getLocalizedString(BRAINTREE_PAYPAL_CONNECT_REGISTRATION_MESSAGE_SET_UP_PASSWORD_FOR_NEW_USER));
                sessionService.removeAttribute(SHOULD_SET_UP_PASSWORD_MESSAGE);
            }
            return processLogin(request, response, userData.getPayerId());
        } else if (userExistB2BData.isExist()) {
            if (!brainTreeConfigService.isEnableLoginViaPayPalToTheExistingUser() && userExistB2BData.isApproved()) {
                GlobalMessages.addFlashMessage(RequestContextUtils.getOutputFlashMap(request),
                        GlobalMessages.ERROR_MESSAGES_HOLDER,
                        getLocalizedString(DISABLE_LOGIN_VIA_PAYPAL_TO_THE_EXISTING_USER_MESSAGE), null);
                return LOGIN_PAGE_REDIRECT;
            }

            final boolean isPayPalAddressPresent = brainTreeConnectWithPayPalFacade.isPayPalAddressPresent(email, userData.getAddress());
            final boolean isPayPalPaymentMethodPresent = brainTreeConnectWithPayPalFacade.isPayPalPaymentMethodPresentForConnectWithPayPal(email);

            if (brainTreeConfigService.isPayPalConnectAddPaymentMethodFlow() && !brainTreeUserFacade.isCustomerWithPayerIdExist(userData.getPayerId())
                    && areAddressAndMethodPresent(isPayPalAddressPresent, isPayPalPaymentMethodPresent)) {
                return LOGIN_PAGE_REDIRECT;
            } else if (brainTreeConfigService.isPayPalConnectAddPaymentMethodFlow() && !brainTreeUserFacade.isCustomerWithPayerIdExist(userData.getPayerId())
                    && areAddressAndMethodPresent(isPayPalAddressPresent, isPayPalPaymentMethodPresent)) {
                setSessionAttributeForPaymentAndAddressInfo(!isPayPalAddressPresent, !isPayPalPaymentMethodPresent, false, userData, email);
                return REDIRECT + CONNECT_LOGIN_SAVE_PAYMENT_INFO_AND_ADDRESS_PAGE;
            }
            GlobalMessages.addFlashMessage(redirectAttributes, GlobalMessages.INFO_MESSAGES_HOLDER,
                    Localization.getLocalizedString(REGISTER_REQUEST_NOR_APPROVED));
            return LOGIN_PAGE_REDIRECT;
        }

        sessionService.setAttribute(ACCESS_TOKEN, accessToken);
        final List<CountryData> countries = Converters
                .convertAll(commonI18NService.getAllCountries(), countryConverter);
        model.addAttribute("titles", brainTreeUserFacade.getTitles());
        model.addAttribute("countries", countries);
        populateModelCmsContent(model,
                getContentPageForLabelOrId(B2bacceleratoraddonWebConstants.CMS_REGISTER_PAGE_NAME));
        model.addAttribute(new RegistrationForm());
        return BRAINTREE_CONNECT_WITH_PAYPAL_ADDITIONAL_DATA;
    }

    @RequestMapping(value = "/verify-password", method = RequestMethod.GET)
    public String connectWithPayPalSecurityLayer(final Model model,
                                                 @RequestParam(required = false) final boolean securityIsFailed)
            throws CMSItemNotFoundException {

        BrainTreeConnectWithPayPalUserData userData = sessionService.getAttribute(USER_DATA_ATTRIBUTE);
        String email = brainTreeRegistrationUserFacade.getEmailFromPayPalUserData(userData);

        model.addAttribute(USER_EMAIL, email);
        model.addAttribute(BASE_SITE_NAME_ATTRIBUTE, getBaseSiteService().getCurrentBaseSite().getName());

        storeCmsPageInModel(model, getCmsPage());
        setUpMetaDataForContentPage(model, (ContentPageModel) getCmsPage());

        if (brainTreeUserFacade.isLoginDisabledForCurrentEmail(email)) {
            GlobalMessages.addErrorMessage(model, BRAINTREE_USER_ACCOUNT_HAS_BEEN_DISABLED_ERROR);
        } else if (securityIsFailed) {
            GlobalMessages.addErrorMessage(model, BRAINTREE_THE_PASSWORD_IS_NOT_CORRECT_ERROR);
        }

        return CONNECT_WITH_PAYPAL_SECURITY_LAYER_PAGE;
    }

    @RequestMapping(value = "/preparationForLinkPayPalAccount", method = RequestMethod.GET)
    public String preparationForLinkPayPalAccount(final HttpServletRequest request, final HttpServletResponse response) {
        BrainTreeConnectWithPayPalUserData userData = sessionService.getAttribute(USER_DATA_ATTRIBUTE);
        sessionService.removeAttribute(USER_DATA_ATTRIBUTE);
        String email = brainTreeRegistrationUserFacade.getEmailFromPayPalUserData(userData);

        brainTreeRegistrationUserFacade.setPayerIdToUser(email, userData.getPayerId());
        braintreeConnectWithPayPalNotificationFacade.sendLinkCustomerEmailNotification(email);
        if (brainTreeConfigService.isPayPalConnectAddPaymentMethodFlow()) {
            sessionService.setAttribute(IS_USER_APPROVED, true);
            final boolean isPayPalAddressPresent = brainTreeConnectWithPayPalFacade.isPayPalAddressPresent(email, userData.getAddress());
            final boolean isPayPalPaymentMethodPresent = brainTreeConnectWithPayPalFacade.isPayPalPaymentMethodPresentForConnectWithPayPal(email);
            if (!isPayPalPaymentMethodPresent || !isPayPalAddressPresent){
                setSessionAttributeForPaymentAndAddressInfo(!isPayPalAddressPresent, !isPayPalPaymentMethodPresent, true, userData, email);
                return REDIRECT + CONNECT_LOGIN_SAVE_PAYMENT_INFO_AND_ADDRESS_PAGE;
            }
        }
        GlobalMessages.addFlashMessage(RequestContextUtils.getOutputFlashMap(request),
                GlobalMessages.INFO_MESSAGES_HOLDER,
                Localization.getLocalizedString(BRAINTREE_PAYPAL_CONNECT_LOGIN_LINKED_ACCOUNTS), null);

        return processLogin(request, response, userData.getPayerId());
    }

    @PostMapping(value = {"/saveAddressAndPaymentInfos"})
    public String doHandlePayPalResponse(final HttpServletRequest request, final HttpServletResponse response,
                                         final Model model, RedirectAttributes redirectAttributes) {
        final String email = sessionService.getAttribute(USER_EMAIL);
        final BrainTreeConnectWithPayPalAddressData addressData = sessionService.getAttribute(ADDRESS_DATA);
        if ((boolean) sessionService.getAttribute(SHOULD_SAVE_PAYMENT_INFO)) {
            try {
                savePayPalPayment(request, email);
                GlobalMessages.addFlashMessage(redirectAttributes, GlobalMessages.CONF_MESSAGES_HOLDER, "paypal.connect.saved.payment.method");
            } catch (Exception e) {
                LOG.error("Error during adding payment method for new customer", e);
                GlobalMessages.addFlashMessage(redirectAttributes, GlobalMessages.INFO_MESSAGES_HOLDER, "paypal.connect.saved.payment.method.error");
            }
        }

        if ((boolean) sessionService.getAttribute(SHOULD_SAVE_ADDRESS)) {
            try {
                if (addressData != null) {
                    brainTreeConnectWithPayPalFacade.addAddressForNewUser(addressData, email);
                    GlobalMessages.addFlashMessage(redirectAttributes, GlobalMessages.CONF_MESSAGES_HOLDER, "paypal.connect.saved.address");
                }
            } catch (Exception e) {
                LOG.error("Error during adding address for new customer", e);
                GlobalMessages.addFlashMessage(redirectAttributes, GlobalMessages.INFO_MESSAGES_HOLDER, "paypal.connect.saved.address.error");
            }
        }
        boolean isApproved = sessionService.getAttribute(IS_USER_APPROVED);
        if (isApproved) {
            final String payerId = sessionService.getAttribute(PAYER_ID);
            GlobalMessages.addFlashMessage(redirectAttributes, GlobalMessages.INFO_MESSAGES_HOLDER,
                    Localization.getLocalizedString(BRAINTREE_PAYPAL_CONNECT_LOGIN_LINKED_ACCOUNTS));
            removeSessionAttribute();
            return processLogin(request, response, payerId);
        }
        GlobalMessages.addFlashMessage(redirectAttributes, GlobalMessages.CONF_MESSAGES_HOLDER,
                Localization.getLocalizedString(REGISTER_REQUEST_CREATED));
        removeSessionAttribute();
        return LOGIN_PAGE_REDIRECT;
    }

    @PostMapping(value = {"/notSavePaymentAndAddressInfos"})
    public String doSavePaymentInfo(final RegisterForm form, final HttpServletRequest request,
                                    final HttpServletResponse response,
                                    final Model model, RedirectAttributes redirectAttributes) {
        model.addAttribute(form);
        boolean isApproved = sessionService.getAttribute(IS_USER_APPROVED);
        if (isApproved) {
            final String payerId = sessionService.getAttribute(PAYER_ID);
            GlobalMessages.addFlashMessage(redirectAttributes, GlobalMessages.INFO_MESSAGES_HOLDER,
                    Localization.getLocalizedString(BRAINTREE_PAYPAL_CONNECT_LOGIN_LINKED_ACCOUNTS));
            removeSessionAttribute();
            return processLogin(request, response, payerId);
        }
        GlobalMessages.addFlashMessage(redirectAttributes, GlobalMessages.CONF_MESSAGES_HOLDER,
                Localization.getLocalizedString(REGISTER_REQUEST_CREATED));
        removeSessionAttribute();
        removeSessionAttribute();
        return LOGIN_PAGE_REDIRECT;
    }

    @RequestMapping(value = "/savePaymentInfoAndAddressPage", method = RequestMethod.GET)
    public String getSavePaymentInfoAndAddressPage(final Model model, HttpServletRequest request) throws CMSItemNotFoundException {
        setModelAttributesForPaymentAndAddressInfo(request, model);
        storeCmsPageInModel(model, getCmsPage());
        setUpMetaDataForContentPage(model, (ContentPageModel) getCmsPage());
        return SAVE_PAYMENT_INFO_PAGE;
    }

    @PostMapping(value = "/register")
    public String doRegister(final RegistrationForm form, final BindingResult bindingResult,
                             final HttpServletRequest request, final HttpSession session, final Model model, RedirectAttributes redirectAttributes)
            throws CMSItemNotFoundException {
        populateModelCmsContent(model, getContentPageForLabelOrId(B2bacceleratoraddonWebConstants.CMS_REGISTER_PAGE_NAME));
        model.addAttribute(form);

        registrationValidator.validate(form, bindingResult);
        if (bindingResult.hasErrors()) {
            final List<CountryData> countries = Converters
                    .convertAll(commonI18NService.getAllCountries(), countryConverter);
            model.addAttribute("titles", brainTreeUserFacade.getTitles());
            model.addAttribute("countries", countries);
            return BRAINTREE_CONNECT_WITH_PAYPAL_ADDITIONAL_DATA;
        }

        final String accessToken = (String) sessionService.getAttribute(ACCESS_TOKEN);
        final BrainTreeConnectWithPayPalUserData userData =
                brainTreeConnectWithPayPalFacade.getUserDataByAccessToken(accessToken);
        session.removeAttribute(ACCESS_TOKEN);

        try {
            b2bRegistrationFacade.register(convertFormAndPayPalDataToRegistrationData(form, userData));
            final String email = brainTreeRegistrationUserFacade.getEmailFromPayPalUserData(userData);
            brainTreeRegistrationUserFacade.setPayerIdToUser(email, userData.getPayerId());
            brainTreeSetUpPasswordFacade.setPasswordForSingleAuthenticationActiveToCustomer(email, false);
            sessionService.setAttribute(SHOULD_SET_UP_PASSWORD_MESSAGE, Boolean.TRUE);
        } catch (BrainTreeUnverifiedAccountPayPalException e) {
            LOG.error(String.format("Unverified PayPal account : %s", e.getMessage()), e);
            GlobalMessages.addFlashMessage(redirectAttributes,
                    GlobalMessages.ERROR_MESSAGES_HOLDER,
                    Localization.getLocalizedString(UNVERIFIED_ACCOUNT_ERROR));
            return LOGIN_PAGE_REDIRECT;
        } catch (final CustomerAlreadyExistsException | RegistrationNotEnabledException e) {
            LOG.error("Failed to register account. Account already exists.", e);
            GlobalMessages.addErrorMessage(model, Localization.getLocalizedString(PAYPAL_CONNECT_GLOBAL_ERROR));
            return getRegistrationView();
        }

        final String email = sessionService.getAttribute(USER_EMAIL);
        if (brainTreeConfigService.isPayPalConnectAddPaymentMethodFlow()) {
            final boolean shutSavePaymentInfo = brainTreeConnectWithPayPalFacade.shouldSavePayPalPaymentInfo();
            setSessionAttributeForPaymentAndAddressInfo(true, shutSavePaymentInfo, false, userData, email);
            return REDIRECT + CONNECT_LOGIN_SAVE_PAYMENT_INFO_AND_ADDRESS_PAGE;
        }
        GlobalMessages.addFlashMessage(RequestContextUtils.getOutputFlashMap(request),
                GlobalMessages.CONF_MESSAGES_HOLDER, getLocalizedString(REGISTER_REQUEST_CREATED), null);
        return LOGIN_PAGE_REDIRECT;
    }

    private B2BRegistrationData convertFormAndPayPalDataToRegistrationData(RegistrationForm form,
                                                                           BrainTreeConnectWithPayPalUserData userData) {
        final B2BRegistrationData registrationData = new B2BRegistrationData();
        BeanUtils.copyProperties(form, registrationData);
        registrationData.setName(userData.getName());
        registrationData.setEmail(brainTreeRegistrationUserFacade.getEmailFromPayPalUserData(userData));
        return registrationData;
    }


    private String processLogin(final HttpServletRequest request, final HttpServletResponse response, String payerId) {
        final CustomerData customer = brainTreeUserFacade.getCustomerByPayerId(payerId);
        payPalAutoLoginStrategy.login(customer.getUid(), request, response);
        return getSuccessRedirect(request, response);
    }

    protected void populateModelCmsContent(final Model model, final ContentPageModel contentPageModel) {

        storeCmsPageInModel(model, contentPageModel);
        setUpMetaDataForContentPage(model, contentPageModel);

        final Breadcrumb registrationBreadcrumbEntry = new Breadcrumb("#",
                getMessageSource().getMessage(SCP_LINK_CREATE_ACCOUNT, null, getI18nService().getCurrentLocale()), null);
        model.addAttribute(BREADCRUBMS, Collections.singletonList(registrationBreadcrumbEntry));
    }

    @Override
    protected AbstractPageModel getCmsPage() throws CMSItemNotFoundException {
        return getContentPageForLabelOrId(LOGIN_LABEL);
    }

    @Override
    protected String getSuccessRedirect(HttpServletRequest request, HttpServletResponse response) {
        return REDIRECT_PREFIX + ROOT;
    }

    @Override
    protected String getView() {
        return B2bacceleratoraddonWebConstants.Views.LOGIN_PAGE;
    }

    private String getRegistrationView() {
        return B2bacceleratoraddonWebConstants.Views.REGISTRATION_PAGE;
    }

    private void setSessionAttributeForPaymentAndAddressInfo(final boolean shutSaveAddress,
                                                             final boolean shutSavePaymentInfo,
                                                             final boolean isUserApproved,
                                                             final BrainTreeConnectWithPayPalUserData userData,
                                                             final String email) {

        sessionService.setAttribute(SHOULD_SAVE_ADDRESS, shutSaveAddress);
        sessionService.setAttribute(SHOULD_SAVE_PAYMENT_INFO, shutSavePaymentInfo);
        sessionService.setAttribute(IS_USER_APPROVED, isUserApproved);
        sessionService.setAttribute(PAYER_ID, userData.getPayerId());
        sessionService.setAttribute(ADDRESS_DATA, userData.getAddress());
        sessionService.setAttribute(USER_EMAIL, email);
    }

    private void removeSessionAttribute() {
        sessionService.removeAttribute(PAYER_ID);
        sessionService.removeAttribute(ADDRESS_DATA);
        sessionService.removeAttribute(SHOULD_SAVE_ADDRESS);
        sessionService.removeAttribute(SHOULD_SAVE_PAYMENT_INFO);
        sessionService.removeAttribute(USER_EMAIL);
        sessionService.removeAttribute(IS_USER_APPROVED);
    }

    private void setModelAttributesForPaymentAndAddressInfo(HttpServletRequest request, Model model) {
        final BraintreeCheckoutData checkoutData = brainTreeCheckoutFacade.getBraintreeCheckoutData(BraintreePageType.LOGIN);
        model.addAttribute(BRAINTREE_CHECKOUT_DATA, checkoutData);
        model.addAttribute(BRAINTREE_CONFIG_DATA, brainTreeConfigFacade.getBraintreePluginConfigurationData());
        model.addAttribute(SHOULD_SAVE_ADDRESS, sessionService.getAttribute(SHOULD_SAVE_ADDRESS));
        model.addAttribute(SHOULD_SAVE_PAYMENT_INFO, sessionService.getAttribute(SHOULD_SAVE_PAYMENT_INFO));
        model.addAttribute(NOT_SAVE_PAYMENT_AND_ADDRESS_INFOS, NOT_SAVE_PAYMENT_AND_ADDRESS_INFOS_URL);
        model.addAttribute(SAVE_ADDRESS_AND_PAYMENT_INFOS, SAVE_ADDRESS_AND_PAYMENT_INFOS_URL);
    }

    private void savePayPalPayment(final HttpServletRequest request, String email) {
        PayPalExpressResponse payPalExpressResponse;
        AddressData hybrisBillingAddress;
        payPalExpressResponse = braintreePayPalResponseExpressCheckoutHandler.handlePayPalResponse(request);
        String payPalEmail = payPalExpressResponse.getDetails().getEmail();
        if (Optional.ofNullable(payPalExpressResponse.getDetails()).map(PayPalDetails::getBillingAddress).isPresent()) {
            hybrisBillingAddress = braintreePayPalResponseExpressCheckoutHandler.getPayPalAddress(
                    payPalExpressResponse.getDetails(), payPalExpressResponse.getDetails().getBillingAddress());
            hybrisBillingAddress.setEmail(payPalExpressResponse.getDetails().getEmail());
        } else {
            hybrisBillingAddress = new AddressData();
            hybrisBillingAddress.setEmail(payPalEmail);
        }

        final BrainTreeSubscriptionInfoData paymentInfoData = GenericBuilder.of(BrainTreeSubscriptionInfoData::new)
                .with(BrainTreeSubscriptionInfoData::setEmail, payPalEmail)
                .with(BrainTreeSubscriptionInfoData::setNonce, payPalExpressResponse.getNonce())
                .with(BrainTreeSubscriptionInfoData::setSavePaymentInfo, true)
                .with(BrainTreeSubscriptionInfoData::setAddressData, hybrisBillingAddress)
                .build();

        brainTreePaymentFacade.createPaymentInfoForLogin(paymentInfoData, email);
    }

    private boolean areAddressAndMethodPresent(boolean isPayPalAddressPresent, boolean isPayPalPaymentMethodPresent) {
        return isPayPalAddressPresent && isPayPalPaymentMethodPresent;
    }
}
