package com.braintree.facade.impl;


import com.braintree.command.request.BrainTreeAddressRequest;
import com.braintree.command.request.BrainTreeCustomerRequest;
import com.braintree.command.request.BrainTreeDeletePaymentMethodRequest;
import com.braintree.command.request.BrainTreeUpdateCreditCardRequest;
import com.braintree.command.request.BrainTreeUpdateCustomerRequest;
import com.braintree.command.result.BrainTreeBillingAddressResult;
import com.braintree.command.result.BrainTreeFindCustomerResult;
import com.braintree.command.result.BrainTreeUpdateCreditCardBillingAddressResult;
import com.braintree.configuration.service.BrainTreeConfigService;
import com.braintree.customer.service.BrainTreeCustomerAccountService;
import com.braintree.exceptions.BrainTreeConnectWithPayPalException;
import com.braintree.exceptions.BraintreeDeletePaymentDetailsWithNotCapturedOrderException;
import com.braintree.form.UpdateCreditCardBillingAddressForm;
import com.braintree.hybris.data.BraintreeUserData;
import com.braintree.method.BrainTreePaymentService;
import com.braintree.model.BrainTreePaymentInfoModel;
import com.braintree.order.service.BraintreeOrderRetrievalService;
import com.braintree.servicelayer.i18n.BraintreeRegionI18NService;
import de.hybris.bootstrap.annotations.UnitTest;
import de.hybris.platform.commercefacades.order.data.CCPaymentInfoData;
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.commercefacades.user.data.RegionData;
import de.hybris.platform.commercefacades.user.data.TitleData;
import de.hybris.platform.commerceservices.customer.CustomerAccountService;
import de.hybris.platform.commerceservices.customer.CustomerEmailResolutionService;
import de.hybris.platform.commerceservices.i18n.CommerceCommonI18NService;
import de.hybris.platform.commerceservices.strategies.CheckoutCustomerStrategy;
import de.hybris.platform.commerceservices.user.UserMatchingService;
import de.hybris.platform.converters.Populator;
import de.hybris.platform.core.PK;
import de.hybris.platform.core.model.c2l.CountryModel;
import de.hybris.platform.core.model.c2l.RegionModel;
import de.hybris.platform.core.model.order.CartModel;
import de.hybris.platform.core.model.order.payment.CreditCardPaymentInfoModel;
import de.hybris.platform.core.model.user.AddressModel;
import de.hybris.platform.core.model.user.CustomerModel;
import de.hybris.platform.core.model.user.TitleModel;
import de.hybris.platform.order.CartService;
import de.hybris.platform.payment.AdapterException;
import de.hybris.platform.payment.dto.BillingInfo;
import de.hybris.platform.servicelayer.dto.converter.Converter;
import de.hybris.platform.servicelayer.i18n.CommonI18NService;
import de.hybris.platform.servicelayer.model.ModelService;
import de.hybris.platform.servicelayer.user.PasswordEncoderService;
import de.hybris.platform.servicelayer.user.UserService;
import org.apache.commons.lang3.StringUtils;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
import org.mockito.Captor;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.Spy;
import org.mockito.runners.MockitoJUnitRunner;

import java.util.Collections;
import java.util.List;
import java.util.Optional;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import static org.mockito.Matchers.any;
import static org.mockito.Matchers.eq;
import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;

@UnitTest
@RunWith(MockitoJUnitRunner.class)
public class DefaultBrainTreeUserFacadeTest {

    private static final PK PK_VALUE = PK.fromLong(10000);
    private static final String REGION_ISO_CODE = "regionIsoCode";
    private static final String COUNTRY_ISO_CODE = "countryIsoCode";
    private static final String LAST_NAME = "lastName";
    private static final String FIRST_NAME = "firstName";
    private static final String TOWN = "town";
    private static final String POSTAL_CODE = "postalCode";
    private static final String LINE_1 = "line1";
    private static final String EMAIL = "email@emal.com";
    private static final String BRAINTREE_ADDRESS_ID = "braintreeAddressId";
    private static final String ADDRESS_ID = "addressId";
    private static final String BRAINTREE_CUSTOMER_ID = "braintreeCustomerId";
    private static final String PAYMENT_METHOD_ID = "paymentMethodId";
    private static final String PAYMENT_METHOD_TOKEN = "paymentMethodToken";
    private static final String LINE_2 = "line2";
    private static final String NONCE = "nonce";
    private static final String PAY_PAL_PAYMENT_PROVIDER = "PayPal";
    private static final String CREDIT_CARD_PAYMENT_PROVIDER = "CreditCard";
    private static final String VENMO_ACCOUNT_PAYMENT_PROVIDER = "VenmoAccount";
    private static final String ANDROID_PAY_CARD_PAYMENT_PROVIDER = "AndroidPayCard";
    private static final String VISA_CHECKOUT_CARD_PAYMENT_PROVIDER = "VisaCheckoutCard";
    private static final String US_BANK_ACCOUNT_PAYMENT_PROVIDER = "UsBankAccount";
    private static final String PAYER_ID = "payerId";
    private static final String UID = "uid";
    private static final int RESTRICTION_OF_PAYMENT_METHOD_REMOVAL = 1;
    private static final String GUEST = "guest";
    private static final String PASSWORD = "12341234";

    @Mock
    private BrainTreeCustomerAccountService brainTreeCustomerAccountService;
    @Mock
    private BrainTreePaymentService brainTreePaymentService;
    @Mock
    private BrainTreeConfigService brainTreeConfigService;
    @Mock
    private DefaultBrainTreePaymentFacade brainTreePaymentFacade;
    @Mock
    private BraintreeRegionI18NService regionI18NService;
    @Mock
    private BraintreeOrderRetrievalService orderRetrievalService;
    @Mock
    private CustomerEmailResolutionService customerEmailResolutionService;
    @Mock
    private Converter<BrainTreeBillingAddressResult, AddressModel> addressResultToAddressModelConverter;
    @Mock
    private Converter<BrainTreePaymentInfoModel, CCPaymentInfoData> brainTreePaymentInfoConverter;
    @Mock
    private Converter<CustomerModel, CustomerData> customerConverter;
    @Mock
    private Populator<CCPaymentInfoData, BrainTreePaymentInfoModel> braintreePaymentInfoReversePopulator;

    @Mock
    private UserService userService;
    @Mock
    private CustomerAccountService customerAccountService;
    @Mock
    private CommerceCommonI18NService commerceCommonI18NService;
    @Mock
    private CommonI18NService commonI18NService;
    @Mock
    private ModelService modelService;
    @Mock
    private CartService cartService;
    @Mock
    private UserMatchingService userMatchingService;
    @Mock
    private PasswordEncoderService passwordEncoderService;
    @Mock
    private CheckoutCustomerStrategy checkoutCustomerStrategy;
    @Mock
    private Populator<AddressData, AddressModel> addressReversePopulator;
    @Mock
    private Converter<AddressModel, AddressData> addressConverter;
    @Mock
    private Converter<CreditCardPaymentInfoModel, CCPaymentInfoData> creditCardPaymentInfoConverter;
    @Mock
    private Converter<TitleModel, TitleData> titleConverter;
    @Mock
    private Populator<CCPaymentInfoData, CreditCardPaymentInfoModel> cardPaymentInfoReversePopulator;

    @Spy
    @InjectMocks
    private DefaultBrainTreeUserFacade unit;

    @Captor
    private ArgumentCaptor<AddressData> addressDataCaptor;
    @Captor
    private ArgumentCaptor<BrainTreeAddressRequest> addressRequestCaptor;
    @Captor
    private ArgumentCaptor<BrainTreeUpdateCreditCardRequest> updateCreditCardRequestCaptor;
    @Captor
    private ArgumentCaptor<BrainTreeCustomerRequest> customerRequestCaptor;
    @Captor
    private ArgumentCaptor<BrainTreeDeletePaymentMethodRequest> deletePaymentMethodRequestCaptor;
    @Captor
    private ArgumentCaptor<BrainTreeUpdateCustomerRequest> updateCustomerRequestCaptor;

    @Mock
    private CustomerModel customer;
    @Mock
    private AddressModel address;
    @Mock
    private AddressModel billingAddress;
    @Mock
    private CountryModel country;
    @Mock
    private RegionModel region;
    @Mock
    private BrainTreePaymentInfoModel paymentInfo;
    @Mock
    private BrainTreePaymentInfoModel secondPaymentInfo;
    @Mock
    private CreditCardPaymentInfoModel creditCardPaymentInfo;
    @Mock
    private CartModel cart;

    @Test
    public void shouldAddAddressToCurrentCustomer() {
        AddressData addressData = new AddressData();
        when(address.getPk()).thenReturn(PK_VALUE);
        when(checkoutCustomerStrategy.getCurrentUserForCheckout()).thenReturn(customer);
        when(modelService.create(AddressModel.class)).thenReturn(address);

        unit.addAddress(addressData);

        verify(customerAccountService).saveAddressEntry(customer, address);
    }

    @Test
    public void shouldSetRegionToNullIfNoSuchRegionExistDuringAddAddress() {
        AddressData addressData = new AddressData();
        RegionData regionData = new RegionData();
        CountryData countryData = new CountryData();
        addressData.setRegion(regionData);
        addressData.setCountry(countryData);
        regionData.setIsocode(REGION_ISO_CODE);
        countryData.setIsocode(COUNTRY_ISO_CODE);
        when(address.getPk()).thenReturn(PK_VALUE);
        when(commonI18NService.getCountry(COUNTRY_ISO_CODE)).thenReturn(country);
        when(regionI18NService.findRegion(country, REGION_ISO_CODE)).thenReturn(null);
        when(checkoutCustomerStrategy.getCurrentUserForCheckout()).thenReturn(customer);
        when(modelService.create(AddressModel.class)).thenReturn(address);

        unit.addAddress(addressData);

        verify(customerAccountService).saveAddressEntry(customer, address);
        verify(addressReversePopulator).populate(addressDataCaptor.capture(), eq(address));
        assertNull(addressDataCaptor.getValue().getRegion());
    }

    @Test
    public void shouldSetVisibleInAddressBookFalseIfAddressDataDoNotHaveLastName() {
        AddressData addressData = new AddressData();
        addressData.setLastName(LAST_NAME);
        addressData.setVisibleInAddressBook(true);
        when(address.getPk()).thenReturn(PK_VALUE);

        when(checkoutCustomerStrategy.getCurrentUserForCheckout()).thenReturn(customer);
        when(modelService.create(AddressModel.class)).thenReturn(address);

        unit.addAddress(addressData);

        verify(customerAccountService).saveAddressEntry(customer, address);
        verify(addressReversePopulator).populate(addressDataCaptor.capture(), eq(address));
        assertFalse(addressDataCaptor.getValue().isVisibleInAddressBook());
    }

    @Test
    public void shouldSetDefaultAddressEntryDuringAddAddress() {
        AddressData addressData = new AddressData();
        addressData.setDefaultAddress(false);
        addressData.setVisibleInAddressBook(true);
        addressData.setFirstName(FIRST_NAME);
        addressData.setLastName(LAST_NAME);

        when(address.getPk()).thenReturn(PK_VALUE);
        when(customer.getDefaultShipmentAddress()).thenReturn(null);

        when(checkoutCustomerStrategy.getCurrentUserForCheckout()).thenReturn(customer);
        when(modelService.create(AddressModel.class)).thenReturn(address);

        unit.addAddress(addressData);

        verify(customerAccountService).setDefaultAddressEntry(customer, address);
    }

    @Test
    public void shouldReturnFalseIfCustomerAddressNotVisibleInAddressBook() {
        AddressData addressData = new AddressData();

        when(userService.getCurrentUser()).thenReturn(customer);
        when(customer.getAddresses()).thenReturn(List.of(address));

        when(address.getVisibleInAddressBook()).thenReturn(false);

        boolean result = unit.isDuplicateAddressForCurrentUser(addressData);

        assertFalse(result);
    }

    @Test
    public void shouldReturnFalseIfAddressesHaveDifferentCountry() {
        AddressData addressData = new AddressData();
        CountryData countryData = new CountryData();
        addressData.setCountry(countryData);

        when(userService.getCurrentUser()).thenReturn(customer);
        when(customer.getAddresses()).thenReturn(List.of(address));

        when(address.getVisibleInAddressBook()).thenReturn(true);
        when(address.getCountry()).thenReturn(country);
        when(country.getIsocode()).thenReturn(COUNTRY_ISO_CODE);

        boolean result = unit.isDuplicateAddressForCurrentUser(addressData);

        assertFalse(result);
    }

    @Test
    public void shouldReturnFalseIfAddressesHaveDifferentRegions() {
        AddressData addressData = new AddressData();
        CountryData countryData = new CountryData();
        RegionData regionData = new RegionData();
        addressData.setCountry(countryData);
        addressData.setRegion(regionData);
        countryData.setIsocode(COUNTRY_ISO_CODE);

        when(userService.getCurrentUser()).thenReturn(customer);
        when(customer.getAddresses()).thenReturn(List.of(address));

        when(address.getVisibleInAddressBook()).thenReturn(true);
        when(address.getCountry()).thenReturn(country);
        when(address.getRegion()).thenReturn(region);
        when(country.getIsocode()).thenReturn(COUNTRY_ISO_CODE);
        when(region.getIsocode()).thenReturn(REGION_ISO_CODE);

        boolean result = unit.isDuplicateAddressForCurrentUser(addressData);

        assertFalse(result);
    }

    @Test
    public void shouldReturnTrueIfAddressDataHaveNullRegion() {
        AddressData addressData = new AddressData();
        CountryData countryData = new CountryData();
        countryData.setIsocode(COUNTRY_ISO_CODE);
        addressData.setCountry(countryData);
        addressData.setFirstName(FIRST_NAME);
        addressData.setLastName(LAST_NAME);
        addressData.setTown(TOWN);
        addressData.setPostalCode(POSTAL_CODE);
        addressData.setLine1(LINE_1);

        when(userService.getCurrentUser()).thenReturn(customer);
        when(customer.getAddresses()).thenReturn(List.of(address));

        when(country.getIsocode()).thenReturn(COUNTRY_ISO_CODE);

        when(address.getVisibleInAddressBook()).thenReturn(true);
        when(address.getCountry()).thenReturn(country);
        when(address.getRegion()).thenReturn(region);
        when(address.getFirstname()).thenReturn(FIRST_NAME);
        when(address.getLastname()).thenReturn(LAST_NAME);
        when(address.getTown()).thenReturn(TOWN);
        when(address.getPostalcode()).thenReturn(POSTAL_CODE);
        when(address.getLine1()).thenReturn(LINE_1);

        boolean result = unit.isDuplicateAddressForCurrentUser(addressData);

        assertTrue(result);
    }

    @Test
    public void shouldReturnTrueIfAddressModelHaveNullRegion() {
        AddressData addressData = new AddressData();
        CountryData countryData = new CountryData();
        RegionData regionData = new RegionData();
        countryData.setIsocode(COUNTRY_ISO_CODE);
        regionData.setIsocode(REGION_ISO_CODE);
        addressData.setCountry(countryData);
        addressData.setRegion(regionData);
        addressData.setFirstName(FIRST_NAME);
        addressData.setLastName(LAST_NAME);
        addressData.setTown(TOWN);
        addressData.setPostalCode(POSTAL_CODE);
        addressData.setLine1(LINE_1);

        when(userService.getCurrentUser()).thenReturn(customer);
        when(customer.getAddresses()).thenReturn(List.of(address));

        when(country.getIsocode()).thenReturn(COUNTRY_ISO_CODE);

        when(address.getVisibleInAddressBook()).thenReturn(true);
        when(address.getCountry()).thenReturn(country);
        when(address.getRegion()).thenReturn(null);
        when(address.getFirstname()).thenReturn(FIRST_NAME);
        when(address.getLastname()).thenReturn(LAST_NAME);
        when(address.getTown()).thenReturn(TOWN);
        when(address.getPostalcode()).thenReturn(POSTAL_CODE);
        when(address.getLine1()).thenReturn(LINE_1);

        boolean result = unit.isDuplicateAddressForCurrentUser(addressData);

        assertTrue(result);
    }

    @Test
    public void shouldReturnFalseIfAddressesHaveDifferentFirstNames() {
        AddressData addressData = new AddressData();
        CountryData countryData = new CountryData();
        RegionData regionData = new RegionData();
        addressData.setCountry(countryData);
        addressData.setRegion(regionData);
        countryData.setIsocode(COUNTRY_ISO_CODE);
        regionData.setIsocode(REGION_ISO_CODE);

        when(userService.getCurrentUser()).thenReturn(customer);
        when(customer.getAddresses()).thenReturn(List.of(address));

        when(country.getIsocode()).thenReturn(COUNTRY_ISO_CODE);
        when(region.getIsocode()).thenReturn(REGION_ISO_CODE);

        when(address.getVisibleInAddressBook()).thenReturn(true);
        when(address.getCountry()).thenReturn(country);
        when(address.getRegion()).thenReturn(region);
        when(address.getFirstname()).thenReturn(FIRST_NAME);

        boolean result = unit.isDuplicateAddressForCurrentUser(addressData);

        assertFalse(result);
    }

    @Test
    public void shouldReturnFalseIfAddressesHaveDifferentLastNames() {
        AddressData addressData = new AddressData();
        CountryData countryData = new CountryData();
        RegionData regionData = new RegionData();
        countryData.setIsocode(COUNTRY_ISO_CODE);
        regionData.setIsocode(REGION_ISO_CODE);
        addressData.setCountry(countryData);
        addressData.setRegion(regionData);
        addressData.setFirstName(FIRST_NAME);

        when(userService.getCurrentUser()).thenReturn(customer);
        when(customer.getAddresses()).thenReturn(List.of(address));

        when(country.getIsocode()).thenReturn(COUNTRY_ISO_CODE);
        when(region.getIsocode()).thenReturn(REGION_ISO_CODE);

        when(address.getVisibleInAddressBook()).thenReturn(true);
        when(address.getCountry()).thenReturn(country);
        when(address.getRegion()).thenReturn(region);
        when(address.getFirstname()).thenReturn(FIRST_NAME);
        when(address.getLastname()).thenReturn(LAST_NAME);

        boolean result = unit.isDuplicateAddressForCurrentUser(addressData);

        assertFalse(result);
    }

    @Test
    public void shouldReturnFalseIfAddressesHaveDifferentTowns() {
        AddressData addressData = new AddressData();
        CountryData countryData = new CountryData();
        RegionData regionData = new RegionData();
        countryData.setIsocode(COUNTRY_ISO_CODE);
        regionData.setIsocode(REGION_ISO_CODE);
        addressData.setCountry(countryData);
        addressData.setRegion(regionData);
        addressData.setFirstName(FIRST_NAME);
        addressData.setLastName(LAST_NAME);

        when(userService.getCurrentUser()).thenReturn(customer);
        when(customer.getAddresses()).thenReturn(List.of(address));

        when(country.getIsocode()).thenReturn(COUNTRY_ISO_CODE);
        when(region.getIsocode()).thenReturn(REGION_ISO_CODE);

        when(address.getVisibleInAddressBook()).thenReturn(true);
        when(address.getCountry()).thenReturn(country);
        when(address.getRegion()).thenReturn(region);
        when(address.getFirstname()).thenReturn(FIRST_NAME);
        when(address.getLastname()).thenReturn(LAST_NAME);
        when(address.getTown()).thenReturn(TOWN);

        boolean result = unit.isDuplicateAddressForCurrentUser(addressData);

        assertFalse(result);
    }

    @Test
    public void shouldReturnFalseIfAddressesHaveDifferentPostalCodes() {
        AddressData addressData = new AddressData();
        CountryData countryData = new CountryData();
        RegionData regionData = new RegionData();
        countryData.setIsocode(COUNTRY_ISO_CODE);
        regionData.setIsocode(REGION_ISO_CODE);
        addressData.setCountry(countryData);
        addressData.setRegion(regionData);
        addressData.setFirstName(FIRST_NAME);
        addressData.setLastName(LAST_NAME);
        addressData.setTown(TOWN);

        when(userService.getCurrentUser()).thenReturn(customer);
        when(customer.getAddresses()).thenReturn(List.of(address));

        when(country.getIsocode()).thenReturn(COUNTRY_ISO_CODE);
        when(region.getIsocode()).thenReturn(REGION_ISO_CODE);

        when(address.getVisibleInAddressBook()).thenReturn(true);
        when(address.getCountry()).thenReturn(country);
        when(address.getRegion()).thenReturn(region);
        when(address.getFirstname()).thenReturn(FIRST_NAME);
        when(address.getLastname()).thenReturn(LAST_NAME);
        when(address.getTown()).thenReturn(TOWN);
        when(address.getPostalcode()).thenReturn(POSTAL_CODE);

        boolean result = unit.isDuplicateAddressForCurrentUser(addressData);

        assertFalse(result);
    }

    @Test
    public void shouldReturnFalseIfAddressesHaveDifferentLine1() {
        AddressData addressData = new AddressData();
        CountryData countryData = new CountryData();
        RegionData regionData = new RegionData();
        countryData.setIsocode(COUNTRY_ISO_CODE);
        regionData.setIsocode(REGION_ISO_CODE);
        addressData.setCountry(countryData);
        addressData.setRegion(regionData);
        addressData.setFirstName(FIRST_NAME);
        addressData.setLastName(LAST_NAME);
        addressData.setTown(TOWN);
        addressData.setPostalCode(POSTAL_CODE);

        when(userService.getCurrentUser()).thenReturn(customer);
        when(customer.getAddresses()).thenReturn(List.of(address));

        when(country.getIsocode()).thenReturn(COUNTRY_ISO_CODE);
        when(region.getIsocode()).thenReturn(REGION_ISO_CODE);

        when(address.getVisibleInAddressBook()).thenReturn(true);
        when(address.getCountry()).thenReturn(country);
        when(address.getRegion()).thenReturn(region);
        when(address.getFirstname()).thenReturn(FIRST_NAME);
        when(address.getLastname()).thenReturn(LAST_NAME);
        when(address.getTown()).thenReturn(TOWN);
        when(address.getPostalcode()).thenReturn(POSTAL_CODE);
        when(address.getLine1()).thenReturn(LINE_1);

        boolean result = unit.isDuplicateAddressForCurrentUser(addressData);

        assertFalse(result);
    }

    @Test
    public void shouldReturnTrueIfAddressDataLine1NullAndAddressModelLine1IsEmpty() {
        AddressData addressData = new AddressData();
        CountryData countryData = new CountryData();
        RegionData regionData = new RegionData();
        countryData.setIsocode(COUNTRY_ISO_CODE);
        regionData.setIsocode(REGION_ISO_CODE);
        addressData.setCountry(countryData);
        addressData.setRegion(regionData);
        addressData.setFirstName(FIRST_NAME);
        addressData.setLastName(LAST_NAME);
        addressData.setTown(TOWN);
        addressData.setPostalCode(POSTAL_CODE);

        when(userService.getCurrentUser()).thenReturn(customer);
        when(customer.getAddresses()).thenReturn(List.of(address));

        when(country.getIsocode()).thenReturn(COUNTRY_ISO_CODE);
        when(region.getIsocode()).thenReturn(REGION_ISO_CODE);

        when(address.getVisibleInAddressBook()).thenReturn(true);
        when(address.getCountry()).thenReturn(country);
        when(address.getRegion()).thenReturn(region);
        when(address.getFirstname()).thenReturn(FIRST_NAME);
        when(address.getLastname()).thenReturn(LAST_NAME);
        when(address.getTown()).thenReturn(TOWN);
        when(address.getPostalcode()).thenReturn(POSTAL_CODE);
        when(address.getLine1()).thenReturn(StringUtils.EMPTY);

        boolean result = unit.isDuplicateAddressForCurrentUser(addressData);

        assertTrue(result);
    }

    @Test
    public void shouldReturnTrueIfAddressesAreSame() {
        AddressData addressData = new AddressData();
        CountryData countryData = new CountryData();
        RegionData regionData = new RegionData();
        countryData.setIsocode(COUNTRY_ISO_CODE);
        regionData.setIsocode(REGION_ISO_CODE);
        addressData.setCountry(countryData);
        addressData.setRegion(regionData);
        addressData.setFirstName(FIRST_NAME);
        addressData.setLastName(LAST_NAME);
        addressData.setTown(TOWN);
        addressData.setPostalCode(POSTAL_CODE);
        addressData.setLine1(LINE_1);

        when(userService.getCurrentUser()).thenReturn(customer);
        when(customer.getAddresses()).thenReturn(List.of(address));

        when(country.getIsocode()).thenReturn(COUNTRY_ISO_CODE);
        when(region.getIsocode()).thenReturn(REGION_ISO_CODE);

        when(address.getVisibleInAddressBook()).thenReturn(true);
        when(address.getCountry()).thenReturn(country);
        when(address.getRegion()).thenReturn(region);
        when(address.getFirstname()).thenReturn(FIRST_NAME);
        when(address.getLastname()).thenReturn(LAST_NAME);
        when(address.getTown()).thenReturn(TOWN);
        when(address.getPostalcode()).thenReturn(POSTAL_CODE);
        when(address.getLine1()).thenReturn(LINE_1);

        boolean result = unit.isDuplicateAddressForCurrentUser(addressData);

        assertTrue(result);
    }

    @Test
    public void shouldSaveAddressForCurrentCustomer() {
        AddressData addressData = new AddressData();
        addressData.setId(PK_VALUE.toString());

        doNothing().when(unit).addAddress(addressData);

        when(address.getPk()).thenReturn(PK_VALUE);
        when(address.getBrainTreeAddressId()).thenReturn(BRAINTREE_ADDRESS_ID);
        when(address.getEmail()).thenReturn(EMAIL);
        when(checkoutCustomerStrategy.getCurrentUserForCheckout()).thenReturn(customer);
        when(customerEmailResolutionService.getEmailForCustomer(customer)).thenReturn(EMAIL);
        when(customer.getAddresses()).thenReturn(List.of(address));

        unit.saveAddressForCurrentCustomer(addressData);

        verify(modelService).save(address);
        assertEquals(BRAINTREE_ADDRESS_ID, addressData.getBrainTreeAddressId());
    }

    @Test
    public void shouldNotSaveAddressForCurrentCustomer() {
        AddressData addressData = new AddressData();

        doNothing().when(unit).addAddress(addressData);

        when(address.getPk()).thenReturn(PK_VALUE);
        when(checkoutCustomerStrategy.getCurrentUserForCheckout()).thenReturn(customer);
        when(customerEmailResolutionService.getEmailForCustomer(customer)).thenReturn(EMAIL);
        when(customer.getAddresses()).thenReturn(List.of(address));

        unit.saveAddressForCurrentCustomer(addressData);

        verify(modelService, never()).save(address);
    }

    @Test
    public void shouldUpdateAddressOnBraintreeSide() {
        AddressData addressData = new AddressData();
        CountryData countryData = new CountryData();
        RegionData regionData = new RegionData();
        countryData.setIsocode(COUNTRY_ISO_CODE);
        regionData.setIsocode(REGION_ISO_CODE);
        addressData.setCountry(countryData);
        addressData.setRegion(regionData);
        addressData.setId(ADDRESS_ID);

        when(checkoutCustomerStrategy.getCurrentUserForCheckout()).thenReturn(customer);
        when(customerAccountService.getAddressForCode(customer, ADDRESS_ID)).thenReturn(address);
        when(address.getBrainTreeAddressId()).thenReturn(BRAINTREE_ADDRESS_ID);
        when(customer.getBraintreeCustomerId()).thenReturn(BRAINTREE_CUSTOMER_ID);

        unit.editAddress(addressData);

        verify(brainTreePaymentService).updateAddress(addressRequestCaptor.capture());
        assertEquals(BRAINTREE_ADDRESS_ID, addressRequestCaptor.getValue().getAddressId());
    }

    @Test
    public void shouldNotUpdateAddressOnBraintreeSide() {
        AddressData addressData = new AddressData();
        addressData.setId(ADDRESS_ID);

        when(checkoutCustomerStrategy.getCurrentUserForCheckout()).thenReturn(customer);
        when(customerAccountService.getAddressForCode(customer, ADDRESS_ID)).thenReturn(address);

        unit.editAddress(addressData);

        verify(brainTreePaymentService, never()).updateAddress(any(BrainTreeAddressRequest.class));
    }

    @Test
    public void shouldRemoveAddressOnBraintreeSide() {
        AddressData addressData = new AddressData();
        CountryData countryData = new CountryData();
        RegionData regionData = new RegionData();
        countryData.setIsocode(COUNTRY_ISO_CODE);
        regionData.setIsocode(REGION_ISO_CODE);
        addressData.setCountry(countryData);
        addressData.setRegion(regionData);
        addressData.setId(ADDRESS_ID);

        when(checkoutCustomerStrategy.getCurrentUserForCheckout()).thenReturn(customer);
        when(customerAccountService.getAddressForCode(customer, ADDRESS_ID)).thenReturn(address);
        when(address.getBrainTreeAddressId()).thenReturn(BRAINTREE_ADDRESS_ID);
        when(customer.getBraintreeCustomerId()).thenReturn(BRAINTREE_CUSTOMER_ID);

        unit.removeAddress(addressData);

        verify(brainTreePaymentService).removeAddress(addressRequestCaptor.capture());
        assertEquals(BRAINTREE_ADDRESS_ID, addressRequestCaptor.getValue().getAddressId());
    }

    @Test
    public void shouldNotRemoveAddressOnBraintreeSide() {
        AddressData addressData = new AddressData();
        addressData.setId(ADDRESS_ID);

        when(checkoutCustomerStrategy.getCurrentUserForCheckout()).thenReturn(customer);
        when(customerAccountService.getAddressForCode(customer, ADDRESS_ID)).thenReturn(address);

        unit.removeAddress(addressData);

        verify(brainTreePaymentService, never()).removeAddress(any(BrainTreeAddressRequest.class));
    }

    @Test
    public void shouldSuccessfullyEditPaymentMethod() {
        AddressData addressData = new AddressData();
        CountryData countryData = new CountryData();
        RegionData regionData = new RegionData();
        UpdateCreditCardBillingAddressForm data = new UpdateCreditCardBillingAddressForm(addressData, PAYMENT_METHOD_ID);
        BrainTreeUpdateCreditCardBillingAddressResult updateResult = new BrainTreeUpdateCreditCardBillingAddressResult();
        BrainTreeBillingAddressResult billingAddressResult = new BrainTreeBillingAddressResult();
        countryData.setIsocode(COUNTRY_ISO_CODE);
        regionData.setIsocode(REGION_ISO_CODE);
        addressData.setCountry(countryData);
        addressData.setRegion(regionData);
        addressData.setFirstName(FIRST_NAME);
        addressData.setLastName(LAST_NAME);
        addressData.setLine1(LINE_1);
        addressData.setLine2(LINE_2);
        addressData.setTown(TOWN);
        addressData.setPostalCode(POSTAL_CODE);
        updateResult.setSuccess(true);
        updateResult.setBillingAddressResult(billingAddressResult);

        when(userService.getCurrentUser()).thenReturn(customer);
        when(brainTreeCustomerAccountService.getBrainTreePaymentInfoForCode(customer, PAYMENT_METHOD_ID)).thenReturn(paymentInfo);
        when(brainTreePaymentService.updatePaymentMethod(any(BrainTreeUpdateCreditCardRequest.class))).thenReturn(updateResult);

        when(paymentInfo.getPaymentMethodToken()).thenReturn(PAYMENT_METHOD_TOKEN);
        when(paymentInfo.getBillingAddress()).thenReturn(billingAddress);

        unit.editPaymentMethod(data);

        verify(modelService).save(paymentInfo);
        verify(brainTreePaymentService).updatePaymentMethod(updateCreditCardRequestCaptor.capture());
        BrainTreeUpdateCreditCardRequest updateCreditCardRequestCaptorValue = updateCreditCardRequestCaptor.getValue();
        BillingInfo billingInfo = updateCreditCardRequestCaptorValue.getBillingInfo();
        assertEquals(PAYMENT_METHOD_TOKEN, updateCreditCardRequestCaptorValue.getToken());
        assertEquals(LINE_1, billingInfo.getStreet1());
        assertEquals(LINE_2, billingInfo.getStreet2());
        assertEquals(FIRST_NAME, billingInfo.getFirstName());
        assertEquals(LAST_NAME, billingInfo.getLastName());
        assertEquals(TOWN, billingInfo.getCity());
        assertEquals(COUNTRY_ISO_CODE, billingInfo.getCountry());
        assertEquals(REGION_ISO_CODE, billingInfo.getState());
        assertEquals(POSTAL_CODE, billingInfo.getPostalCode());
    }

    @Test(expected = AdapterException.class)
    public void shouldThrowAdapterExceptionIfEditPaymentMethodResultUnsuccessful() {
        AddressData addressData = new AddressData();
        CountryData countryData = new CountryData();
        UpdateCreditCardBillingAddressForm data = new UpdateCreditCardBillingAddressForm(addressData, PAYMENT_METHOD_ID);
        BrainTreeUpdateCreditCardBillingAddressResult updateResult = new BrainTreeUpdateCreditCardBillingAddressResult();
        countryData.setIsocode(COUNTRY_ISO_CODE);
        addressData.setCountry(countryData);
        updateResult.setSuccess(false);

        when(userService.getCurrentUser()).thenReturn(customer);
        when(brainTreeCustomerAccountService.getBrainTreePaymentInfoForCode(customer, PAYMENT_METHOD_ID)).thenReturn(paymentInfo);
        when(brainTreePaymentService.updatePaymentMethod(any(BrainTreeUpdateCreditCardRequest.class))).thenReturn(updateResult);

        when(paymentInfo.getPaymentMethodToken()).thenReturn(PAYMENT_METHOD_TOKEN);

        unit.editPaymentMethod(data);
    }

    @Test
    public void shouldSuccessfullyEditPaymentMethodAndCreateNewBillingAddressModel() {
        AddressData addressData = new AddressData();
        CountryData countryData = new CountryData();
        UpdateCreditCardBillingAddressForm data = new UpdateCreditCardBillingAddressForm(addressData, PAYMENT_METHOD_ID);
        BrainTreeUpdateCreditCardBillingAddressResult updateResult = new BrainTreeUpdateCreditCardBillingAddressResult();
        BrainTreeBillingAddressResult billingAddressResult = new BrainTreeBillingAddressResult();
        countryData.setIsocode(COUNTRY_ISO_CODE);
        addressData.setCountry(countryData);
        updateResult.setSuccess(true);
        updateResult.setBillingAddressResult(billingAddressResult);

        when(userService.getCurrentUser()).thenReturn(customer);
        when(brainTreeCustomerAccountService.getBrainTreePaymentInfoForCode(customer, PAYMENT_METHOD_ID)).thenReturn(paymentInfo);
        when(brainTreePaymentService.updatePaymentMethod(any(BrainTreeUpdateCreditCardRequest.class))).thenReturn(updateResult);

        when(paymentInfo.getPaymentMethodToken()).thenReturn(PAYMENT_METHOD_TOKEN);
        when(modelService.create(AddressModel.class)).thenReturn(billingAddress);

        unit.editPaymentMethod(data);

        verify(modelService).save(paymentInfo);
        verify(billingAddress).setOwner(paymentInfo);
    }

    @Test
    public void shouldReturnTrueIfCurrentCustomerHasBraintreeCustomerId() {
        when(userService.getCurrentUser()).thenReturn(customer);
        when(customer.getBraintreeCustomerId()).thenReturn(BRAINTREE_CUSTOMER_ID);

        boolean result = unit.isCurrentUserHasBrainTreeCustomerId();

        assertTrue(result);
    }

    @Test
    public void shouldReturnFalseIfCurrentCustomerDoNotHasBraintreeCustomerId() {
        when(userService.getCurrentUser()).thenReturn(customer);

        boolean result = unit.isCurrentUserHasBrainTreeCustomerId();

        assertFalse(result);
    }

    @Test
    public void shouldGetCCPaymentInfos() {
        when(checkoutCustomerStrategy.getCurrentUserForCheckout()).thenReturn(customer);
        when(customerAccountService.getCreditCardPaymentInfos(customer, true)).thenReturn(Collections.emptyList());

        doReturn(Collections.emptyList()).when(unit).getBrainTreeCCPaymentInfos(true);

        List<CCPaymentInfoData> result = unit.getCCPaymentInfos(true);

        assertTrue(result.isEmpty());
    }

    @Test
    public void shouldGetBraintreePaymentInfos() {
        CCPaymentInfoData data = new CCPaymentInfoData();
        data.setPaymentMethodNonce(NONCE);
        when(userService.getCurrentUser()).thenReturn(customer);
        when(brainTreeCustomerAccountService.getBrainTreePaymentInfos(customer, true)).thenReturn(List.of(paymentInfo));
        when(brainTreePaymentInfoConverter.convert(paymentInfo)).thenReturn(data);

        when(brainTreeConfigService.isPayPalEnabled()).thenReturn(true);
        when(brainTreeConfigService.isHostedFieldEnabled()).thenReturn(true);
        when(brainTreeConfigService.isVenmoEnabled()).thenReturn(true);
        when(brainTreeConfigService.isGooglePayEnabled()).thenReturn(true);
        when(brainTreeConfigService.isSrcEnabled()).thenReturn(true);
        when(brainTreeConfigService.isUsBankAccountEnabled()).thenReturn(true);

        List<CCPaymentInfoData> result = unit.getBrainTreeCCPaymentInfos(true);

        assertFalse(result.isEmpty());
        assertEquals(data, result.get(0));
    }

    @Test
    public void shouldReturnEmptyListOfBraintreePaymentInfosIfPaymentMethodWasDeletedOnBraintreeSide() {
        CCPaymentInfoData data = new CCPaymentInfoData();
        when(userService.getCurrentUser()).thenReturn(customer);
        when(brainTreeCustomerAccountService.getBrainTreePaymentInfos(customer, true)).thenReturn(List.of(paymentInfo));
        when(brainTreePaymentInfoConverter.convert(paymentInfo)).thenReturn(data);

        when(brainTreeConfigService.isPayPalEnabled()).thenReturn(true);
        when(brainTreeConfigService.isHostedFieldEnabled()).thenReturn(true);
        when(brainTreeConfigService.isVenmoEnabled()).thenReturn(true);
        when(brainTreeConfigService.isGooglePayEnabled()).thenReturn(true);
        when(brainTreeConfigService.isSrcEnabled()).thenReturn(true);
        when(brainTreeConfigService.isUsBankAccountEnabled()).thenReturn(true);

        List<CCPaymentInfoData> result = unit.getBrainTreeCCPaymentInfos(true);

        assertTrue(result.isEmpty());
    }

    @Test
    public void shouldGetBraintreePaymentInfosAndUpdatePaymentInfoWithDefaultStatus() {
        CCPaymentInfoData data = new CCPaymentInfoData();
        data.setPaymentMethodNonce(NONCE);
        when(userService.getCurrentUser()).thenReturn(customer);
        when(brainTreeCustomerAccountService.getBrainTreePaymentInfos(customer, true)).thenReturn(List.of(paymentInfo));
        when(brainTreePaymentInfoConverter.convert(paymentInfo)).thenReturn(data);

        when(brainTreeConfigService.isPayPalEnabled()).thenReturn(true);
        when(brainTreeConfigService.isHostedFieldEnabled()).thenReturn(true);
        when(brainTreeConfigService.isVenmoEnabled()).thenReturn(true);
        when(brainTreeConfigService.isGooglePayEnabled()).thenReturn(true);
        when(brainTreeConfigService.isSrcEnabled()).thenReturn(true);
        when(brainTreeConfigService.isUsBankAccountEnabled()).thenReturn(true);

        when(customer.getDefaultPaymentInfo()).thenReturn(paymentInfo);

        List<CCPaymentInfoData> result = unit.getBrainTreeCCPaymentInfos(true);

        assertFalse(result.isEmpty());
        assertEquals(data, result.get(0));
        assertTrue(result.get(0).isDefaultPaymentInfo());
    }

    @Test
    public void shouldReturnEmptyListOfBraintreePaymentInfosIfAllPaymentMethodsDisabled() {
        when(userService.getCurrentUser()).thenReturn(customer);
        when(brainTreeCustomerAccountService.getBrainTreePaymentInfos(customer, true)).thenReturn(List.of(paymentInfo));

        when(brainTreeConfigService.isPayPalEnabled()).thenReturn(false);
        when(brainTreeConfigService.isHostedFieldEnabled()).thenReturn(false);
        when(brainTreeConfigService.isVenmoEnabled()).thenReturn(false);
        when(brainTreeConfigService.isGooglePayEnabled()).thenReturn(false);
        when(brainTreeConfigService.isSrcEnabled()).thenReturn(false);
        when(brainTreeConfigService.isUsBankAccountEnabled()).thenReturn(false);

        List<CCPaymentInfoData> result = unit.getBrainTreeCCPaymentInfos(true);

        assertTrue(result.isEmpty());
    }

    @Test
    public void shouldOnlyOneBraintreePaymentInfoIfOnlyPayPalPaymentMethodEnabled() {
        CCPaymentInfoData data = new CCPaymentInfoData();
        data.setPaymentMethodNonce(NONCE);
        when(userService.getCurrentUser()).thenReturn(customer);
        when(brainTreeCustomerAccountService.getBrainTreePaymentInfos(customer, true)).thenReturn(List.of(paymentInfo, secondPaymentInfo));
        when(brainTreePaymentInfoConverter.convert(paymentInfo)).thenReturn(data);

        when(brainTreeConfigService.isPayPalEnabled()).thenReturn(true);
        when(brainTreeConfigService.isHostedFieldEnabled()).thenReturn(false);
        when(brainTreeConfigService.isVenmoEnabled()).thenReturn(false);
        when(brainTreeConfigService.isGooglePayEnabled()).thenReturn(false);
        when(brainTreeConfigService.isSrcEnabled()).thenReturn(false);
        when(brainTreeConfigService.isUsBankAccountEnabled()).thenReturn(false);

        when(paymentInfo.getPaymentProvider()).thenReturn(PAY_PAL_PAYMENT_PROVIDER);

        List<CCPaymentInfoData> result = unit.getBrainTreeCCPaymentInfos(true);

        assertFalse(result.isEmpty());
        assertEquals(RESTRICTION_OF_PAYMENT_METHOD_REMOVAL, result.size());
    }

    @Test
    public void shouldOnlyOneBraintreePaymentInfoIfOnlyCreditCardPaymentMethodEnabled() {
        CCPaymentInfoData data = new CCPaymentInfoData();
        data.setPaymentMethodNonce(NONCE);
        when(userService.getCurrentUser()).thenReturn(customer);
        when(brainTreeCustomerAccountService.getBrainTreePaymentInfos(customer, true)).thenReturn(List.of(paymentInfo, secondPaymentInfo));
        when(brainTreePaymentInfoConverter.convert(paymentInfo)).thenReturn(data);

        when(brainTreeConfigService.isPayPalEnabled()).thenReturn(false);
        when(brainTreeConfigService.isHostedFieldEnabled()).thenReturn(true);
        when(brainTreeConfigService.isVenmoEnabled()).thenReturn(false);
        when(brainTreeConfigService.isGooglePayEnabled()).thenReturn(false);
        when(brainTreeConfigService.isSrcEnabled()).thenReturn(false);
        when(brainTreeConfigService.isUsBankAccountEnabled()).thenReturn(false);

        when(paymentInfo.getPaymentProvider()).thenReturn(CREDIT_CARD_PAYMENT_PROVIDER);

        List<CCPaymentInfoData> result = unit.getBrainTreeCCPaymentInfos(true);

        assertFalse(result.isEmpty());
        assertEquals(RESTRICTION_OF_PAYMENT_METHOD_REMOVAL, result.size());
    }

    @Test
    public void shouldOnlyOneBraintreePaymentInfoIfOnlyVenmoPaymentMethodEnabled() {
        CCPaymentInfoData data = new CCPaymentInfoData();
        data.setPaymentMethodNonce(NONCE);
        when(userService.getCurrentUser()).thenReturn(customer);
        when(brainTreeCustomerAccountService.getBrainTreePaymentInfos(customer, true)).thenReturn(List.of(paymentInfo, secondPaymentInfo));
        when(brainTreePaymentInfoConverter.convert(paymentInfo)).thenReturn(data);

        when(brainTreeConfigService.isPayPalEnabled()).thenReturn(false);
        when(brainTreeConfigService.isHostedFieldEnabled()).thenReturn(false);
        when(brainTreeConfigService.isVenmoEnabled()).thenReturn(true);
        when(brainTreeConfigService.isGooglePayEnabled()).thenReturn(false);
        when(brainTreeConfigService.isSrcEnabled()).thenReturn(false);
        when(brainTreeConfigService.isUsBankAccountEnabled()).thenReturn(false);

        when(paymentInfo.getPaymentProvider()).thenReturn(VENMO_ACCOUNT_PAYMENT_PROVIDER);

        List<CCPaymentInfoData> result = unit.getBrainTreeCCPaymentInfos(true);

        assertFalse(result.isEmpty());
        assertEquals(RESTRICTION_OF_PAYMENT_METHOD_REMOVAL, result.size());
    }

    @Test
    public void shouldOnlyOneBraintreePaymentInfoIfOnlyGooglePayPaymentMethodEnabled() {
        CCPaymentInfoData data = new CCPaymentInfoData();
        data.setPaymentMethodNonce(NONCE);
        when(userService.getCurrentUser()).thenReturn(customer);
        when(brainTreeCustomerAccountService.getBrainTreePaymentInfos(customer, true)).thenReturn(List.of(paymentInfo, secondPaymentInfo));
        when(brainTreePaymentInfoConverter.convert(paymentInfo)).thenReturn(data);

        when(brainTreeConfigService.isPayPalEnabled()).thenReturn(false);
        when(brainTreeConfigService.isHostedFieldEnabled()).thenReturn(false);
        when(brainTreeConfigService.isVenmoEnabled()).thenReturn(false);
        when(brainTreeConfigService.isGooglePayEnabled()).thenReturn(true);
        when(brainTreeConfigService.isSrcEnabled()).thenReturn(false);
        when(brainTreeConfigService.isUsBankAccountEnabled()).thenReturn(false);

        when(paymentInfo.getPaymentProvider()).thenReturn(ANDROID_PAY_CARD_PAYMENT_PROVIDER);

        List<CCPaymentInfoData> result = unit.getBrainTreeCCPaymentInfos(true);

        assertFalse(result.isEmpty());
        assertEquals(RESTRICTION_OF_PAYMENT_METHOD_REMOVAL, result.size());
    }

    @Test
    public void shouldOnlyOneBraintreePaymentInfoIfOnlySRCPaymentMethodEnabled() {
        CCPaymentInfoData data = new CCPaymentInfoData();
        data.setPaymentMethodNonce(NONCE);
        when(userService.getCurrentUser()).thenReturn(customer);
        when(brainTreeCustomerAccountService.getBrainTreePaymentInfos(customer, true)).thenReturn(List.of(paymentInfo, secondPaymentInfo));
        when(brainTreePaymentInfoConverter.convert(paymentInfo)).thenReturn(data);

        when(brainTreeConfigService.isPayPalEnabled()).thenReturn(false);
        when(brainTreeConfigService.isHostedFieldEnabled()).thenReturn(false);
        when(brainTreeConfigService.isVenmoEnabled()).thenReturn(false);
        when(brainTreeConfigService.isGooglePayEnabled()).thenReturn(false);
        when(brainTreeConfigService.isSrcEnabled()).thenReturn(true);
        when(brainTreeConfigService.isUsBankAccountEnabled()).thenReturn(false);

        when(paymentInfo.getPaymentProvider()).thenReturn(VISA_CHECKOUT_CARD_PAYMENT_PROVIDER);

        List<CCPaymentInfoData> result = unit.getBrainTreeCCPaymentInfos(true);

        assertFalse(result.isEmpty());
        assertEquals(RESTRICTION_OF_PAYMENT_METHOD_REMOVAL, result.size());
    }

    @Test
    public void shouldOnlyOneBraintreePaymentInfoIfOnlyACHPaymentMethodEnabled() {
        CCPaymentInfoData data = new CCPaymentInfoData();
        data.setPaymentMethodNonce(NONCE);
        when(userService.getCurrentUser()).thenReturn(customer);
        when(brainTreeCustomerAccountService.getBrainTreePaymentInfos(customer, true)).thenReturn(List.of(paymentInfo, secondPaymentInfo));
        when(brainTreePaymentInfoConverter.convert(paymentInfo)).thenReturn(data);

        when(brainTreeConfigService.isPayPalEnabled()).thenReturn(false);
        when(brainTreeConfigService.isHostedFieldEnabled()).thenReturn(false);
        when(brainTreeConfigService.isVenmoEnabled()).thenReturn(false);
        when(brainTreeConfigService.isGooglePayEnabled()).thenReturn(false);
        when(brainTreeConfigService.isSrcEnabled()).thenReturn(false);
        when(brainTreeConfigService.isUsBankAccountEnabled()).thenReturn(true);

        when(paymentInfo.getPaymentProvider()).thenReturn(US_BANK_ACCOUNT_PAYMENT_PROVIDER);

        List<CCPaymentInfoData> result = unit.getBrainTreeCCPaymentInfos(true);

        assertFalse(result.isEmpty());
        assertEquals(RESTRICTION_OF_PAYMENT_METHOD_REMOVAL, result.size());
    }

    @Test
    public void shouldReturnCustomerExistFalseWhenBraintreeCustomerIdEmpty() {
        BrainTreeFindCustomerResult result = unit.findBrainTreeCustomer(StringUtils.EMPTY);

        assertFalse(result.isCustomerExist());
    }

    @Test
    public void shouldFindBraintreeCustomer() {
        BrainTreeFindCustomerResult data = new BrainTreeFindCustomerResult();
        when(brainTreePaymentService.findCustomer(any(BrainTreeCustomerRequest.class))).thenReturn(data);

        BrainTreeFindCustomerResult result = unit.findBrainTreeCustomer(BRAINTREE_CUSTOMER_ID);

        verify(brainTreePaymentService).findCustomer(customerRequestCaptor.capture());
        assertEquals(data, result);
        assertEquals(BRAINTREE_CUSTOMER_ID, customerRequestCaptor.getValue().getCustomerId());
    }

    @Test
    public void shouldGetCustomerByPayerId() {
        CustomerData data = new CustomerData();
        when(brainTreeCustomerAccountService.getCustomerByPayerId(PAYER_ID)).thenReturn(Optional.of(customer));
        when(customerConverter.convert(customer)).thenReturn(data);

        CustomerData result = unit.getCustomerByPayerId(PAYER_ID);

        assertEquals(data, result);
    }

    @Test(expected = BrainTreeConnectWithPayPalException.class)
    public void shouldThrowExceptionIfCustomerWithPayerIdNotExist() {
        when(brainTreeCustomerAccountService.getCustomerByPayerId(PAYER_ID)).thenReturn(Optional.empty());

        unit.getCustomerByPayerId(PAYER_ID);
    }

    @Test
    public void shouldReturnTrueIfCustomerWithPayerIdExist() {
        when(brainTreeCustomerAccountService.getCustomerByPayerId(PAYER_ID)).thenReturn(Optional.of(customer));

        boolean result = unit.isCustomerWithPayerIdExist(PAYER_ID);

        assertTrue(result);
    }

    @Test
    public void shouldReturnFalseIfCustomerWithPayerIdNotExist() {
        when(brainTreeCustomerAccountService.getCustomerByPayerId(PAYER_ID)).thenReturn(Optional.empty());

        boolean result = unit.isCustomerWithPayerIdExist(PAYER_ID);

        assertFalse(result);
    }

    @Test
    public void shouldGetCustomerByUid() {
        CustomerData data = new CustomerData();

        when(userService.getUserForUID(UID, CustomerModel.class)).thenReturn(customer);
        when(customerConverter.convert(customer)).thenReturn(data);

        CustomerData result = unit.getCustomerDataByUid(UID);

        assertEquals(data, result);
    }

    @Test
    public void shouldReturnBillingAddressOfGivenPaymentMethod() {
        AddressData data = new AddressData();
        when(userService.getCurrentUser()).thenReturn(customer);
        when(brainTreeCustomerAccountService.getBrainTreePaymentInfoForCode(customer, PAYMENT_METHOD_ID)).thenReturn(paymentInfo);
        when(addressConverter.convert(billingAddress)).thenReturn(data);
        when(paymentInfo.getBillingAddress()).thenReturn(billingAddress);

        AddressData result = unit.getBillingAddressByPaymentMethodId(PAYMENT_METHOD_ID);

        assertEquals(data, result);
    }

    @Test
    public void shouldReturnNullIfPaymentInfoDoNotHaveBillingAddress() {
        when(userService.getCurrentUser()).thenReturn(customer);
        when(brainTreeCustomerAccountService.getBrainTreePaymentInfoForCode(customer, PAYMENT_METHOD_ID)).thenReturn(paymentInfo);

        AddressData result = unit.getBillingAddressByPaymentMethodId(PAYMENT_METHOD_ID);

        assertNull(result);
    }

    @Test
    public void shouldReturnTrueIfLoginForCurrentUserDisabled() {
        when(userService.getUserForUID(EMAIL)).thenReturn(customer);
        when(customer.isLoginDisabled()).thenReturn(true);

        boolean result = unit.isLoginDisabledForCurrentEmail(EMAIL);

        assertTrue(result);
    }

    @Test
    public void shouldCallPasswordEncoderServiceIsValid() {
        when(userService.getUserForUID(EMAIL)).thenReturn(customer);
        when(passwordEncoderService.isValid(customer, PASSWORD)).thenReturn(true);

        boolean result = unit.isPasswordValid(EMAIL, PASSWORD);

        assertTrue(result);
    }

    @Test
    public void shouldUnlinkPaymentMethod() {
        when(userService.getCurrentUser()).thenReturn(customer);
        when(brainTreeCustomerAccountService.getBrainTreePaymentInfoForCode(customer, PAYMENT_METHOD_ID)).thenReturn(paymentInfo);

        unit.unlinkCCPaymentInfo(PAYMENT_METHOD_ID);

        verify(brainTreeCustomerAccountService).unlinkCCPaymentInfo(customer, paymentInfo);
    }

    @Test
    public void shouldUpdateCCPaymentInfo() {
        CCPaymentInfoData data = new CCPaymentInfoData();
        data.setId(PAYMENT_METHOD_ID);

        when(userService.getCurrentUser()).thenReturn(customer);
        when(brainTreeCustomerAccountService.getBrainTreePaymentInfoForCode(customer, PAYMENT_METHOD_ID)).thenReturn(paymentInfo);
        when(paymentInfo.getBillingAddress()).thenReturn(billingAddress);

        unit.updateCCPaymentInfo(data);

        verify(braintreePaymentInfoReversePopulator).populate(data, paymentInfo);
        verify(modelService).save(paymentInfo);
        verify(modelService).save(billingAddress);
    }

    @Test
    public void shouldUseSuperUpdateCCPaymentInfo() {
        CCPaymentInfoData data = new CCPaymentInfoData();
        data.setId(PAYMENT_METHOD_ID);

        when(customerAccountService.getCreditCardPaymentInfoForCode(customer, PAYMENT_METHOD_ID)).thenReturn(creditCardPaymentInfo);
        when(userService.getCurrentUser()).thenReturn(customer);

        unit.updateCCPaymentInfo(data);

        verify(customerAccountService).getCreditCardPaymentInfoForCode(customer, PAYMENT_METHOD_ID);
    }

    @Test
    public void shouldUpdateDefaultPaymentMethod() {
        when(brainTreeCustomerAccountService.getBrainTreePaymentInfos(customer, true)).thenReturn(List.of(paymentInfo));

        unit.updateDefaultPaymentInfo(customer);

        verify(paymentInfo).setIsDefault(true);
        verify(modelService).save(paymentInfo);
        verify(customerAccountService).setDefaultPaymentInfo(customer, paymentInfo);
    }

    @Test
    public void shouldUseSuperUpdateDefaultPaymentMethod() {
        unit.updateDefaultPaymentInfo(customer);

        verify(customerAccountService).getCreditCardPaymentInfos(customer, true);
    }

    @Test
    public void shouldDeletePaymentMethodOnBraintreeSide() {
        when(userService.getCurrentUser()).thenReturn(customer);
        when(customer.getBraintreeCustomerId()).thenReturn(BRAINTREE_CUSTOMER_ID);

        unit.removeBTCCPaymentInfo(PAYMENT_METHOD_TOKEN);

        verify(brainTreePaymentService).deletePaymentMethod(deletePaymentMethodRequestCaptor.capture());
        assertEquals(PAYMENT_METHOD_TOKEN, deletePaymentMethodRequestCaptor.getValue().getToken());
        assertEquals(BRAINTREE_CUSTOMER_ID, deletePaymentMethodRequestCaptor.getValue().getMerchantTransactionCode());
    }

    @Test
    public void shouldReturnPaymentMethodByGivenCode() {
        CCPaymentInfoData data = new CCPaymentInfoData();
        when(userService.getCurrentUser()).thenReturn(customer);
        when(brainTreeCustomerAccountService.getBrainTreePaymentInfoForCode(customer, PAYMENT_METHOD_ID)).thenReturn(paymentInfo);
        when(brainTreePaymentInfoConverter.convert(paymentInfo)).thenReturn(data);

        when(customer.getDefaultPaymentInfo()).thenReturn(paymentInfo);

        CCPaymentInfoData result = unit.getCCPaymentInfoForCode(PAYMENT_METHOD_ID);

        assertEquals(data, result);
        assertTrue(result.isDefaultPaymentInfo());
    }

    @Test
    public void shouldReturnNullGivenCodeEmpty() {
        CCPaymentInfoData result = unit.getCCPaymentInfoForCode(StringUtils.EMPTY);

        assertNull(result);
    }

    @Test
    public void shouldUseSuperMethodIfNoBraintreePaymentInfoForCodeExist() {
        when(userService.getCurrentUser()).thenReturn(customer);

        unit.getCCPaymentInfoForCode(PAYMENT_METHOD_ID);

        verify(customerAccountService).getCreditCardPaymentInfoForCode(customer, PAYMENT_METHOD_ID);
    }

    @Test
    public void shouldSetDefaultPaymentInfo() {
        CCPaymentInfoData data = new CCPaymentInfoData();
        data.setId(PAYMENT_METHOD_ID);

        when(checkoutCustomerStrategy.getCurrentUserForCheckout()).thenReturn(customer);
        when(brainTreeCustomerAccountService.getBrainTreePaymentInfoForCode(customer, PAYMENT_METHOD_ID)).thenReturn(paymentInfo);

        when(customer.getBraintreeCustomerId()).thenReturn(BRAINTREE_CUSTOMER_ID);
        when(paymentInfo.getPaymentMethodToken()).thenReturn(PAYMENT_METHOD_TOKEN);

        unit.setDefaultPaymentInfo(data);

        verify(paymentInfo).setIsDefault(true);
        verify(customerAccountService).setDefaultPaymentInfo(customer, paymentInfo);
        verify(modelService).save(paymentInfo);
        verify(brainTreePaymentService).setBrainTreeDefaultPaymentInfo(updateCustomerRequestCaptor.capture());
        assertEquals(BRAINTREE_CUSTOMER_ID, updateCustomerRequestCaptor.getValue().getId());
        assertEquals(PAYMENT_METHOD_TOKEN, updateCustomerRequestCaptor.getValue().getDefaultPaymentMethodToken());
    }

    @Test
    public void shouldUseSuperSetDefaultPaymentInfo() {
        CCPaymentInfoData data = new CCPaymentInfoData();
        data.setId(PAYMENT_METHOD_ID);

        when(checkoutCustomerStrategy.getCurrentUserForCheckout()).thenReturn(customer);

        unit.setDefaultPaymentInfo(data);

        verify(customerAccountService).getCreditCardPaymentInfoForCode(customer, PAYMENT_METHOD_ID);
    }

    @Test
    public void shouldRemovePaymentInfoByGivenCode() {
        when(userService.getCurrentUser()).thenReturn(customer);
        when(brainTreeCustomerAccountService.getBrainTreePaymentInfoForCode(customer, PAYMENT_METHOD_ID)).thenReturn(paymentInfo);

        unit.removeCCPaymentInfo(PAYMENT_METHOD_ID);

        verify(brainTreeCustomerAccountService).deleteBrainTreePaymentInfo(customer, paymentInfo);
    }

    @Test
    public void shouldRemoveDefaultPaymentInfoByGivenCode() {
        when(userService.getCurrentUser()).thenReturn(customer);
        when(brainTreeCustomerAccountService.getBrainTreePaymentInfoForCode(customer, PAYMENT_METHOD_ID)).thenReturn(paymentInfo);

        when(paymentInfo.isIsDefault()).thenReturn(true);

        unit.removeCCPaymentInfo(PAYMENT_METHOD_ID);

        verify(brainTreeCustomerAccountService).deleteBrainTreePaymentInfo(customer, paymentInfo);
        verify(customer).setDefaultPaymentInfo(null);
        verify(paymentInfo).setIsDefault(false);
    }

    @Test
    public void shouldRemovePaymentInfoByGivenCodeOnBraintreeSide() {
        when(userService.getCurrentUser()).thenReturn(customer);
        when(brainTreeCustomerAccountService.getBrainTreePaymentInfoForCode(customer, PAYMENT_METHOD_ID)).thenReturn(paymentInfo);

        when(paymentInfo.getPaymentMethodToken()).thenReturn(PAYMENT_METHOD_TOKEN);

        unit.removeCCPaymentInfo(PAYMENT_METHOD_ID);

        verify(brainTreeCustomerAccountService).deleteBrainTreePaymentInfo(customer, paymentInfo);
        verify(paymentInfo).setIsDeletedOnBraintree(true);
    }

    @Test
    public void shouldUseSuperRemovePaymentInfoByGivenCode() {
        when(userService.getCurrentUser()).thenReturn(customer);

        unit.removeCCPaymentInfo(PAYMENT_METHOD_ID);

        verify(customerAccountService).getCreditCardPaymentInfos(customer, false);
    }

    @Test
    public void shouldAllowRemovePaymentInfoByGivenCodeForPayPalPaymentProvider() {
        when(userService.getCurrentUser()).thenReturn(customer);
        when(brainTreeCustomerAccountService.getBrainTreePaymentInfoForCode(customer, PAYMENT_METHOD_ID)).thenReturn(paymentInfo);
        when(brainTreeConfigService.isRestrictionPaymentMethodRemovalEnable()).thenReturn(true);

        when(paymentInfo.getPaymentProvider()).thenReturn(PAY_PAL_PAYMENT_PROVIDER);

        unit.removeCCPaymentInfo(PAYMENT_METHOD_ID);

        verify(brainTreeCustomerAccountService).deleteBrainTreePaymentInfo(customer, paymentInfo);
    }

    @Test
    public void shouldAllowRemovePaymentInfoByGivenCodeIfNoOrderWithExpiredTransaction() {
        when(userService.getCurrentUser()).thenReturn(customer);
        when(brainTreeCustomerAccountService.getBrainTreePaymentInfoForCode(customer, PAYMENT_METHOD_ID)).thenReturn(paymentInfo);
        when(brainTreeConfigService.isRestrictionPaymentMethodRemovalEnable()).thenReturn(true);
        when(brainTreeConfigService.getRestrictionPaymentMethodRemovalExpiration()).thenReturn(RESTRICTION_OF_PAYMENT_METHOD_REMOVAL);
        when(orderRetrievalService.countOrdersWithExpiredTransactions(paymentInfo, RESTRICTION_OF_PAYMENT_METHOD_REMOVAL)).thenReturn(0);

        unit.removeCCPaymentInfo(PAYMENT_METHOD_ID);

        verify(brainTreeCustomerAccountService).deleteBrainTreePaymentInfo(customer, paymentInfo);
    }

    @Test(expected = BraintreeDeletePaymentDetailsWithNotCapturedOrderException.class)
    public void shouldThrowExceptionOnRemovePaymentInfoByGivenCodeIfOrderWithNotCapturedTransactionPresent() {
        when(userService.getCurrentUser()).thenReturn(customer);
        when(brainTreeCustomerAccountService.getBrainTreePaymentInfoForCode(customer, PAYMENT_METHOD_ID)).thenReturn(paymentInfo);
        when(brainTreeConfigService.isRestrictionPaymentMethodRemovalEnable()).thenReturn(true);
        when(brainTreeConfigService.getRestrictionPaymentMethodRemovalExpiration()).thenReturn(RESTRICTION_OF_PAYMENT_METHOD_REMOVAL);
        when(orderRetrievalService.countOrdersWithExpiredTransactions(paymentInfo, RESTRICTION_OF_PAYMENT_METHOD_REMOVAL)).thenReturn(1);

        unit.removeCCPaymentInfo(PAYMENT_METHOD_ID);
    }

    @Test
    public void shouldReturnTrueIfPayerIdNotBlankInCustomerModel() {
        when(userService.getUserForUID(UID)).thenReturn(customer);
        when(customer.getPayPalPayerId()).thenReturn(PAYER_ID);

        boolean result = unit.isPayerIdInCustomer(UID);

        assertTrue(result);
    }

    @Test
    public void shouldReturnFalseIfPayerIdBlankInCustomerModel() {
        when(userService.getUserForUID(UID)).thenReturn(customer);
        when(customer.getPayPalPayerId()).thenReturn(StringUtils.EMPTY);

        boolean result = unit.isPayerIdInCustomer(UID);

        assertFalse(result);
    }

    @Test
    public void shouldReturnRegisteredUserData() {
        when(cartService.getSessionCart()).thenReturn(cart);
        when(cart.getUser()).thenReturn(customer);
        when(customer.getName()).thenReturn(String.format("%s %s", FIRST_NAME, LAST_NAME));
        when(customer.getUid()).thenReturn(EMAIL);

        BraintreeUserData result = unit.getUserData();

        assertEquals(FIRST_NAME, result.getFirstName());
        assertEquals(LAST_NAME, result.getLastName());
        assertEquals(EMAIL, result.getEmail());
    }

    @Test
    public void shouldReturnGuestUserData() {
        when(cartService.getSessionCart()).thenReturn(cart);
        when(cart.getUser()).thenReturn(customer);
        when(customer.getName()).thenReturn(GUEST);
        when(customer.getUid()).thenReturn(String.format("%s|%s", UID, EMAIL));

        BraintreeUserData result = unit.getUserData();

        assertEquals(GUEST, result.getLastName());
        assertEquals(EMAIL, result.getEmail());
    }

}
