package com.braintree.payment.info.service.impl;

import com.braintree.configuration.service.BrainTreeConfigService;
import com.braintree.customer.dao.BrainTreeCustomerAccountDao;
import com.braintree.customer.service.BrainTreeCustomerAccountService;
import com.braintree.enums.BrainTreeCardType;
import com.braintree.enums.BrainTreePaymentMethod;
import com.braintree.enums.ExpirationStatus;
import com.braintree.hybris.data.graphql.commands.response.BrainTreePaymentMethodDetails;
import com.braintree.hybris.data.graphql.commands.response.BrainTreePaymentMethodOrigin;
import com.braintree.hybris.data.graphql.commands.response.BrainTreePaymentMethodSnapshot;
import com.braintree.method.BrainTreePaymentService;
import com.braintree.model.BrainTreePaymentInfoModel;
import com.braintree.payment.info.dao.BrainTreePaymentInfoDao;
import com.braintreegateway.CreditCard.CardType;
import de.hybris.bootstrap.annotations.UnitTest;
import de.hybris.platform.commercefacades.user.data.AddressData;
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.user.AddressModel;
import de.hybris.platform.core.model.user.CustomerModel;
import de.hybris.platform.core.model.user.TitleModel;
import de.hybris.platform.core.servicelayer.data.SearchPageData;
import de.hybris.platform.servicelayer.model.ModelService;
import de.hybris.platform.servicelayer.search.FlexibleSearchService;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.runners.MockitoJUnitRunner;

import java.time.Month;
import java.time.YearMonth;
import java.util.Collections;
import java.util.List;
import java.util.Optional;

import static com.braintree.constants.BraintreeConstants.APPLE_PAY_ORIGIN_TYPE;
import static com.braintree.constants.BraintreeConstants.CREDIT_CARD_DETAILS;
import static com.braintree.constants.BraintreeConstants.GOOGLE_PAY_ORIGIN_TYPE;
import static com.braintree.constants.BraintreeConstants.InstrumentType.ANDROID_PAY_CARD_PAYMENT_INSTRUMENT_TYPE;
import static com.braintree.constants.BraintreeConstants.InstrumentType.APPLE_PAY_PAYMENT_INSTRUMENT_TYPE;
import static com.braintree.constants.BraintreeConstants.InstrumentType.CREDIT_CARD_PAYMENT_INSTRUMENT_TYPE;
import static com.braintree.constants.BraintreeConstants.InstrumentType.LOCAL_PAYMENT_INSTRUMENT_TYPE;
import static com.braintree.constants.BraintreeConstants.InstrumentType.PAYPAL_PAYMENT_INSTRUMENT_TYPE;
import static com.braintree.constants.BraintreeConstants.InstrumentType.US_BANK_ACCOUNT_PAYMENT_INSTRUMENT_TYPE;
import static com.braintree.constants.BraintreeConstants.InstrumentType.VENMO_PAYMENT_INSTRUMENT_TYPE;
import static com.braintree.constants.BraintreeConstants.InstrumentType.VISA_CHECKOUT_CARD_PAYMENT_INSTRUMENT_TYPE;
import static com.braintree.constants.BraintreeConstants.LOCAL_PAYMENT_DETAILS;
import static com.braintree.constants.BraintreeConstants.PAYPAL_ACCOUNT_DETAILS;
import static com.braintree.constants.BraintreeConstants.PAYPAL_TRANSACTION_DETAILS;
import static com.braintree.constants.BraintreeConstants.PropertyConfiguration.BRAINTREE_ACCEPTED_PAYMENT_METHODS;
import static com.braintree.constants.BraintreeConstants.US_BANK_ACCOUNT_DETAIL;
import static com.braintree.constants.BraintreeConstants.VENMO_ACCOUNT_DETAILS;
import static com.braintree.constants.BraintreeConstants.VISA_CHECKOUT_ORIGIN_TYPE;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import static org.mockito.Matchers.any;
import static org.mockito.Matchers.anyBoolean;
import static org.mockito.Matchers.anyString;
import static org.mockito.Matchers.eq;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;

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

    private static final String CUSTOMER_ID = "CUSTOMER_ID";
    private static final String PAYMENT_METHOD_TOKEN = "PAYMENT_METHOD_TOKEN";
    private static final String PAYMENT_METHOD_GRAPHQL_TOKEN = "PAYMENT_METHOD_GRAPHQL_TOKEN";
    private static final String CORRECT_SOURCE = "CORRECT_SOURCE";
    private static final String UID = "UID";
    private static final boolean SAVE_PAYMENT_INFO = true;
    private static final Boolean USE_PAYMENT_METHOD_TOKEN = Boolean.TRUE;
    private static final Boolean BRAINTREE_3D_SECURE = Boolean.TRUE;
    private static final Boolean BRAINTREE_ADVANCED_FRAUD_TOOLS_ENABLED = Boolean.TRUE;
    private static final String MERCHANT_ACCOUNT_ID_FOR_CURRENT_SITE_AND_CURRENCY = "MERCHANT_ACCOUNT_ID_FOR_CURRENT_SITE_AND_CURRENCY";
    private static final String BRAINTREE_CHANNEL = "Hybris_BT";
    private static final String CARDHOLDER_NAME = "CARDHOLDER_NAME";
    private static final String CARD_NUMBER = "CARD_NUMBER";
    private static final String EXPIRATION_MONTH = "EXPIRATION_MONTH";
    private static final String EXPIRATION_YEAR = "EXPIRATION_YEAR";
    private static final String FIRST_NAME = "John";
    private static final String LAST_NAME = "Cena";
    private static final String US_ISO_CODE = "US";
    private static final String POSTAL_CODE = "postalCode";
    private static final String LINE_1 = "Line1";
    private static final String TOWN = "town";
    private static final String CARD_HOLDER_NAME_2 = "cardHolderName2";
    private static final String CARD_NUMBER_2 = "CardNumber2";
    private static final String EXP_MONTH_2 = "expMonth2";
    private static final String EXPIRATION_YEAR_2 = "expirationYear2";
    private static final String CUSTOMER_ID_2 = "customerId2";
    private static final String FIRST_NAME_2 = "FIRST_NAME_2";
    private static final String LAST_NAME_2 = "LAST_NAME_2";
    private static final String UK_ISO_CODE = "UK";
    private static final String POSTAL_CODE_2 = "POSTAL_CODE_2";
    private static final String LINE_1_2 = "LINE_1_2";
    private static final String TOWN_2 = "TOWN_2";
    private static final String LAST_4 = "Last4";
    private static final String ROUTING_NUMBER = "routingNumber";
    private static final String LAST_4_2 = "LAST_4_2";
    private static final String ROUTING_NUMBER_2 = "ROUTING_NUMBER_2";
    private static final String TITLE_CODE = "titleCode";

    @Mock
    private BrainTreePaymentInfoDao brainTreePaymentInfoDao;

    @Mock
    private BrainTreeCustomerAccountDao brainTreeCustomerAccountDao;

    @Mock
    private ModelService modelService;

    @Mock
    private BrainTreeConfigService brainTreeConfigService;

    @Mock
    private BrainTreePaymentService brainTreePaymentService;

    @Mock
    private BrainTreeCustomerAccountService brainTreeCustomerAccountService;

    @Mock
    private FlexibleSearchService flexibleSearchService;

    @InjectMocks
    private DefaultBraintreePaymentInfoService unit;

    @Mock
    private BrainTreePaymentInfoModel paymentInfoModel;

    @Mock
    private BrainTreePaymentInfoModel paymentInfoDuplicate;

    @Mock
    private CustomerModel customerModel;

    @Mock
    private AddressModel addressModel;

    @Mock
    private AddressModel addressModel2;

    @Mock
    private RegionModel regionModel;

    @Mock
    private CountryModel countryModel;

    @Mock
    private CountryModel countryModel2;

    @Mock
    private TitleModel titleModel;

    @Mock
    private BrainTreePaymentMethodSnapshot paymentMethodSnapshot;

    @Mock
    private BrainTreePaymentMethodOrigin origin;

    @Mock
    private BrainTreePaymentInfoModel expiredCard;

    @Mock
    private BrainTreePaymentInfoModel notExpiredCard;

    @Mock
    private BrainTreePaymentInfoModel expiredSoonCard;

    @Mock
    private SearchPageData<BrainTreePaymentInfoModel> searchPageData;

    @Test
    public void shouldFindByCustomerIdAndCode() {
        // given
        when(brainTreePaymentInfoDao.findByCustomerIdAndCode(CUSTOMER_ID, PAYMENT_METHOD_TOKEN)).thenReturn(Optional.of(paymentInfoModel));

        // when
        Optional<BrainTreePaymentInfoModel> result = unit.findByCustomerIdAndCode(CUSTOMER_ID, PAYMENT_METHOD_TOKEN);

        // then
        assertTrue(result.isPresent());
        verify(brainTreePaymentInfoDao).findByCustomerIdAndCode(CUSTOMER_ID, PAYMENT_METHOD_TOKEN);
    }

    @Test
    public void shouldRemovePaymentInfo() {
        // given
        when(brainTreePaymentInfoDao.find(CUSTOMER_ID, PAYMENT_METHOD_TOKEN)).thenReturn(paymentInfoModel);

        // when
        unit.remove(CUSTOMER_ID, PAYMENT_METHOD_TOKEN);

        // then
        verify(modelService).remove(paymentInfoModel);
    }

    @Test
    public void shouldNotRemovePaymentInfoIfNotExist() {
        // given
        when(brainTreePaymentInfoDao.find(CUSTOMER_ID, PAYMENT_METHOD_TOKEN)).thenReturn(null);

        // when
        unit.remove(CUSTOMER_ID, PAYMENT_METHOD_TOKEN);

        // then
        verify(modelService, never()).remove(any(BrainTreePaymentInfoModel.class));
    }

    @Test
    public void shouldAddToCustomer() {
        // given
        when(brainTreeCustomerAccountDao.findCustomerByBrainTreeCustomerId(CUSTOMER_ID)).thenReturn(customerModel);
        when(customerModel.getUid()).thenReturn(UID);

        when(paymentInfoModel.getCustomerId()).thenReturn(CUSTOMER_ID);
        when(paymentInfoModel.getBillingAddress()).thenReturn(addressModel);

        when(brainTreeConfigService.is3dSecureConfiguration()).thenReturn(BRAINTREE_3D_SECURE);
        when(brainTreeConfigService.isAdvancedFraudTools()).thenReturn(BRAINTREE_ADVANCED_FRAUD_TOOLS_ENABLED);
        when(brainTreeConfigService.getMerchantAccountIdForCurrentSiteAndCurrency())
                .thenReturn(MERCHANT_ACCOUNT_ID_FOR_CURRENT_SITE_AND_CURRENCY);
        when(brainTreeConfigService.getBrainTreeChannel()).thenReturn(BRAINTREE_CHANNEL);

        // when
        unit.addToCustomer(paymentInfoModel);

        // then
        verify(paymentInfoModel).setUser(customerModel);
        verify(paymentInfoModel).setCode(anyString());
        verify(paymentInfoModel).setSaved(SAVE_PAYMENT_INFO);
        verify(paymentInfoModel).setUsePaymentMethodToken(USE_PAYMENT_METHOD_TOKEN);
        verify(paymentInfoModel).setThreeDSecureConfiguration(BRAINTREE_3D_SECURE);
        verify(paymentInfoModel).setAdvancedFraudTools(BRAINTREE_ADVANCED_FRAUD_TOOLS_ENABLED);
        verify(paymentInfoModel)
                .setMerchantAccountIdForCurrentSite(MERCHANT_ACCOUNT_ID_FOR_CURRENT_SITE_AND_CURRENCY);
        verify(paymentInfoModel).setBrainTreeChannel(BRAINTREE_CHANNEL);
        verify(addressModel).setOwner(paymentInfoModel);

        verify(modelService).save(addressModel);
        verify(modelService).save(paymentInfoModel);
        verify(modelService).refresh(customerModel);
    }

    @Test
    public void shouldNotAddToCustomerWhenCustomerIsNull() {
        // given
        when(paymentInfoModel.getCustomerId()).thenReturn(CUSTOMER_ID);
        when(brainTreeCustomerAccountDao.findCustomerByBrainTreeCustomerId(CUSTOMER_ID)).thenReturn(null);

        // when
        unit.addToCustomer(paymentInfoModel);

        // then
        verify(paymentInfoModel, never()).setUser(any(CustomerModel.class));
        verify(paymentInfoModel, never()).setCode(anyString());
        verify(paymentInfoModel, never()).setSaved(anyBoolean());
        verify(paymentInfoModel, never()).setUsePaymentMethodToken(anyBoolean());
        verify(paymentInfoModel, never()).setThreeDSecureConfiguration(anyBoolean());
        verify(paymentInfoModel, never()).setAdvancedFraudTools(anyBoolean());
        verify(paymentInfoModel, never())
                .setMerchantAccountIdForCurrentSite(anyString());
        verify(paymentInfoModel, never()).setBrainTreeChannel(anyString());

        verify(modelService, never()).save(any(AddressModel.class));
        verify(modelService, never()).save(any(BrainTreePaymentInfoModel.class));
        verify(modelService, never()).refresh(any(CustomerModel.class));
    }

    @Test
    public void shouldNotAddToCustomerBillingAddressIfPaymentBillingAddressIsNull() {
        // given
        when(brainTreeCustomerAccountDao.findCustomerByBrainTreeCustomerId(CUSTOMER_ID)).thenReturn(customerModel);
        when(customerModel.getUid()).thenReturn(UID);

        when(paymentInfoModel.getCustomerId()).thenReturn(CUSTOMER_ID);
        when(paymentInfoModel.getBillingAddress()).thenReturn(null);

        when(brainTreeConfigService.is3dSecureConfiguration()).thenReturn(BRAINTREE_3D_SECURE);
        when(brainTreeConfigService.isAdvancedFraudTools()).thenReturn(BRAINTREE_ADVANCED_FRAUD_TOOLS_ENABLED);
        when(brainTreeConfigService.getMerchantAccountIdForCurrentSiteAndCurrency())
                .thenReturn(MERCHANT_ACCOUNT_ID_FOR_CURRENT_SITE_AND_CURRENCY);
        when(brainTreeConfigService.getBrainTreeChannel()).thenReturn(BRAINTREE_CHANNEL);

        // when
        unit.addToCustomer(paymentInfoModel);

        // then
        verify(paymentInfoModel).setUser(customerModel);
        verify(paymentInfoModel).setCode(anyString());
        verify(paymentInfoModel).setSaved(SAVE_PAYMENT_INFO);
        verify(paymentInfoModel).setUsePaymentMethodToken(USE_PAYMENT_METHOD_TOKEN);
        verify(paymentInfoModel).setThreeDSecureConfiguration(BRAINTREE_3D_SECURE);
        verify(paymentInfoModel).setAdvancedFraudTools(BRAINTREE_ADVANCED_FRAUD_TOOLS_ENABLED);
        verify(paymentInfoModel)
                .setMerchantAccountIdForCurrentSite(MERCHANT_ACCOUNT_ID_FOR_CURRENT_SITE_AND_CURRENCY);
        verify(paymentInfoModel).setBrainTreeChannel(BRAINTREE_CHANNEL);

        verify(modelService).save(any(BrainTreePaymentInfoModel.class));
        verify(modelService, never()).save(null);
        verify(modelService).refresh(customerModel);
    }

    @Test
    public void shouldUpdatePaymentInfo() {
        // given
        when(paymentInfoModel.getCustomerId()).thenReturn(CUSTOMER_ID);
        when(paymentInfoModel.getPaymentMethodToken()).thenReturn(PAYMENT_METHOD_TOKEN);
        when(paymentInfoModel.getCardNumber()).thenReturn(CARD_NUMBER);
        when(paymentInfoModel.getCardholderName()).thenReturn(CARDHOLDER_NAME);
        when(paymentInfoModel.getExpirationMonth()).thenReturn(EXPIRATION_MONTH);
        when(paymentInfoModel.getExpirationYear()).thenReturn(EXPIRATION_YEAR);
        when(paymentInfoModel.getBillingAddress()).thenReturn(addressModel);
        when(brainTreePaymentInfoDao.find(CUSTOMER_ID, PAYMENT_METHOD_TOKEN)).thenReturn(paymentInfoModel);

        // when
        unit.update(PAYMENT_METHOD_TOKEN, paymentInfoModel);

        // then
        verify(paymentInfoModel).setPaymentMethodToken(PAYMENT_METHOD_TOKEN);
        verify(paymentInfoModel).setCardNumber(CARD_NUMBER);
        verify(paymentInfoModel).setCardholderName(CARDHOLDER_NAME);
        verify(paymentInfoModel).setExpirationMonth(EXPIRATION_MONTH);
        verify(paymentInfoModel).setExpirationYear(EXPIRATION_YEAR);
        verify(addressModel).setOwner(paymentInfoModel);
        verify(paymentInfoModel).setBillingAddress(addressModel);

        verify(modelService).save(addressModel);
        verify(modelService).save(paymentInfoModel);
        verify(modelService).refresh(paymentInfoModel);
    }

    @Test
    public void shouldUpdatePaymentInfoWithAddressData() {
        // given
        ArgumentCaptor<TitleModel> argument = ArgumentCaptor.forClass(TitleModel.class);

        AddressData addressData = new AddressData();
        addressData.setTitleCode(TITLE_CODE);

        when(paymentInfoModel.getCustomerId()).thenReturn(CUSTOMER_ID);
        when(brainTreePaymentInfoDao.find(CUSTOMER_ID, PAYMENT_METHOD_TOKEN)).thenReturn(paymentInfoModel);
        when(paymentInfoModel.getPaymentMethodToken()).thenReturn(PAYMENT_METHOD_TOKEN);
        when(paymentInfoModel.getCardNumber()).thenReturn(CARD_NUMBER);
        when(paymentInfoModel.getCardholderName()).thenReturn(CARDHOLDER_NAME);
        when(paymentInfoModel.getExpirationMonth()).thenReturn(EXPIRATION_MONTH);
        when(paymentInfoModel.getExpirationYear()).thenReturn(EXPIRATION_YEAR);
        when(paymentInfoModel.getBillingAddress()).thenReturn(addressModel);
        when(flexibleSearchService.getModelByExample(any(TitleModel.class))).thenReturn(titleModel);

        // when
        unit.update(paymentInfoModel, addressData);

        // then
        verify(paymentInfoModel).setPaymentMethodToken(PAYMENT_METHOD_TOKEN);
        verify(paymentInfoModel).setCardNumber(CARD_NUMBER);
        verify(paymentInfoModel).setCardholderName(CARDHOLDER_NAME);
        verify(paymentInfoModel).setExpirationMonth(EXPIRATION_MONTH);
        verify(paymentInfoModel).setExpirationYear(EXPIRATION_YEAR);
        verify(addressModel).setOwner(paymentInfoModel);
        verify(addressModel).setTitle(titleModel);
        verify(paymentInfoModel).setBillingAddress(addressModel);

        verify(flexibleSearchService).getModelByExample(argument.capture());
        assertEquals(TITLE_CODE, argument.getValue().getCode());

        verify(modelService).save(addressModel);
        verify(modelService).save(paymentInfoModel);
        verify(modelService).refresh(paymentInfoModel);
    }

    @Test
    public void shouldNotUpdateWhenPaymentInfoNotExistByGivenToken() {
        // given
        AddressData addressData = new AddressData();
        addressData.setTitleCode(TITLE_CODE);

        // when
        unit.update(paymentInfoModel, addressData);

        // then
        verify(modelService, never()).save(any(AddressModel.class));
        verify(modelService, never()).save(any(BrainTreePaymentInfoModel.class));
        verify(modelService, never()).refresh(BrainTreePaymentInfoModel.class);
    }

    @Test
    public void disableShouldNotDisableMethodIfItIsNotBelongForCustomer() {
        when(customerModel.getPaymentInfos()).thenReturn(Collections.emptyList());
        when(brainTreePaymentInfoDao.find(CUSTOMER_ID, PAYMENT_METHOD_TOKEN)).thenReturn(paymentInfoModel);
        when(brainTreeCustomerAccountDao.findCustomerByBrainTreeCustomerId(CUSTOMER_ID)).thenReturn(customerModel);

        unit.disable(CUSTOMER_ID, PAYMENT_METHOD_TOKEN);
        verify(brainTreePaymentInfoDao).find(CUSTOMER_ID, PAYMENT_METHOD_TOKEN);
        verify(brainTreeCustomerAccountDao).findCustomerByBrainTreeCustomerId(CUSTOMER_ID);
        verify(paymentInfoModel, never()).setDuplicate(Boolean.TRUE);
    }

    @Test
    public void disableShouldNotDisableMethodIfCustomerIsNull() {
        when(brainTreePaymentInfoDao.find(CUSTOMER_ID, PAYMENT_METHOD_TOKEN)).thenReturn(paymentInfoModel);
        when(brainTreeCustomerAccountDao.findCustomerByBrainTreeCustomerId(CUSTOMER_ID)).thenReturn(null);

        unit.disable(CUSTOMER_ID, PAYMENT_METHOD_TOKEN);
        verify(brainTreePaymentInfoDao).find(CUSTOMER_ID, PAYMENT_METHOD_TOKEN);
        verify(brainTreeCustomerAccountDao).findCustomerByBrainTreeCustomerId(CUSTOMER_ID);
        verify(paymentInfoModel, never()).setDuplicate(Boolean.TRUE);
    }

    @Test
    public void disableShouldNotDisableMethodIfPaymentInfoIsNull() {
        when(brainTreePaymentInfoDao.find(CUSTOMER_ID, PAYMENT_METHOD_TOKEN)).thenReturn(null);
        when(brainTreeCustomerAccountDao.findCustomerByBrainTreeCustomerId(CUSTOMER_ID)).thenReturn(customerModel);

        unit.disable(CUSTOMER_ID, PAYMENT_METHOD_TOKEN);
        verify(brainTreePaymentInfoDao).find(CUSTOMER_ID, PAYMENT_METHOD_TOKEN);
        verify(brainTreeCustomerAccountDao).findCustomerByBrainTreeCustomerId(CUSTOMER_ID);
        verify(paymentInfoModel, never()).setDuplicate(Boolean.TRUE);
    }

    @Test
    public void disableShouldSetDuplicateToTrue() {
        when(customerModel.getPaymentInfos()).thenReturn(Collections.singletonList(paymentInfoModel));
        when(brainTreePaymentInfoDao.find(CUSTOMER_ID, PAYMENT_METHOD_TOKEN)).thenReturn(paymentInfoModel);
        when(brainTreeCustomerAccountDao.findCustomerByBrainTreeCustomerId(CUSTOMER_ID)).thenReturn(customerModel);

        unit.disable(CUSTOMER_ID, PAYMENT_METHOD_TOKEN);
        verify(brainTreePaymentInfoDao).find(CUSTOMER_ID, PAYMENT_METHOD_TOKEN);
        verify(brainTreeCustomerAccountDao).findCustomerByBrainTreeCustomerId(CUSTOMER_ID);
        verify(paymentInfoModel).setDuplicate(Boolean.TRUE);
        verify(modelService).save(paymentInfoModel);
        verify(modelService).refresh(customerModel);
    }

    @Test
    public void getGraphQLTokenForPaymentMethodShouldExchangeTokenAndSaveForPaymentInfo() {
        when(brainTreePaymentInfoDao.find(PAYMENT_METHOD_TOKEN)).thenReturn(paymentInfoModel);
        when(brainTreePaymentService.exchangeLegacyIdToGraphQLId(any())).thenReturn(PAYMENT_METHOD_GRAPHQL_TOKEN);

        assertEquals(PAYMENT_METHOD_GRAPHQL_TOKEN, unit.getGraphQLTokenForPaymentMethod(PAYMENT_METHOD_TOKEN));
        verify(paymentInfoModel).setPaymentMethodGraphQLToken(PAYMENT_METHOD_GRAPHQL_TOKEN);
        verify(modelService).save(paymentInfoModel);
    }

    @Test
    public void getGraphQLTokenForPaymentMethodShouldExchangeTokenEvenPaymentInfoNotExist() {
        when(brainTreePaymentService.exchangeLegacyIdToGraphQLId(any())).thenReturn(PAYMENT_METHOD_GRAPHQL_TOKEN);

        assertEquals(PAYMENT_METHOD_GRAPHQL_TOKEN, unit.getGraphQLTokenForPaymentMethod(PAYMENT_METHOD_TOKEN));
    }

    @Test
    public void getPaymentInstrumentTypeBySnapshotAndOriginShouldReturnPayPal() {
        when(paymentMethodSnapshot.getOrigin()).thenReturn(origin);
        when(paymentMethodSnapshot.get__typename()).thenReturn(PAYPAL_TRANSACTION_DETAILS);

        assertEquals(PAYPAL_PAYMENT_INSTRUMENT_TYPE,
                unit.getPaymentInstrumentTypeBySnapshotAndOrigin(paymentMethodSnapshot));
    }

    @Test
    public void getPaymentInstrumentTypeBySnapshotAndOriginShouldReturnCreditCard() {
        when(paymentMethodSnapshot.getOrigin()).thenReturn(origin);
        when(paymentMethodSnapshot.get__typename()).thenReturn(CREDIT_CARD_DETAILS);

        assertEquals(CREDIT_CARD_PAYMENT_INSTRUMENT_TYPE,
                unit.getPaymentInstrumentTypeBySnapshotAndOrigin(paymentMethodSnapshot));
    }

    @Test
    public void shouldGetPaymentInstrumentTypeWhenPaymentSnapshotCreditCardPresent() {
        BrainTreePaymentMethodOrigin origin = new BrainTreePaymentMethodOrigin();
        origin.setType(GOOGLE_PAY_ORIGIN_TYPE);

        BrainTreePaymentMethodDetails creditCard = new BrainTreePaymentMethodDetails();
        creditCard.setOrigin(origin);

        BrainTreePaymentMethodSnapshot paymentMethodSnapshot = new BrainTreePaymentMethodSnapshot();
        paymentMethodSnapshot.setCreditCard(creditCard);

        String result = unit.getPaymentInstrumentTypeBySnapshotAndOrigin(paymentMethodSnapshot);

        assertEquals(ANDROID_PAY_CARD_PAYMENT_INSTRUMENT_TYPE, result);
    }

    @Test
    public void shouldGetUsBankAccountPaymentInstrumentType() {
        BrainTreePaymentMethodSnapshot paymentMethodSnapshot = new BrainTreePaymentMethodSnapshot();
        paymentMethodSnapshot.set__typename(US_BANK_ACCOUNT_DETAIL);

        String result = unit.getPaymentInstrumentTypeBySnapshotAndOrigin(paymentMethodSnapshot);

        assertEquals(US_BANK_ACCOUNT_PAYMENT_INSTRUMENT_TYPE, result);
    }

    @Test
    public void getPaymentInstrumentTypeBySnapshotAndOriginShouldReturnVenmo() {
        when(paymentMethodSnapshot.getOrigin()).thenReturn(origin);
        when(paymentMethodSnapshot.get__typename()).thenReturn(VENMO_ACCOUNT_DETAILS);

        assertEquals(VENMO_PAYMENT_INSTRUMENT_TYPE,
                unit.getPaymentInstrumentTypeBySnapshotAndOrigin(paymentMethodSnapshot));
    }

    @Test
    public void getPaymentInstrumentTypeBySnapshotAndOriginShouldReturnAndroid() {
        when(paymentMethodSnapshot.getOrigin()).thenReturn(origin);
        when(origin.getType()).thenReturn(GOOGLE_PAY_ORIGIN_TYPE);
        when(paymentMethodSnapshot.get__typename()).thenReturn(CREDIT_CARD_DETAILS);

        assertEquals(ANDROID_PAY_CARD_PAYMENT_INSTRUMENT_TYPE,
                unit.getPaymentInstrumentTypeBySnapshotAndOrigin(paymentMethodSnapshot));
    }

    @Test
    public void getPaymentInstrumentTypeBySnapshotAndOriginShouldReturnSRC() {
        when(paymentMethodSnapshot.getOrigin()).thenReturn(origin);
        when(origin.getType()).thenReturn(VISA_CHECKOUT_ORIGIN_TYPE);
        when(paymentMethodSnapshot.get__typename()).thenReturn(CREDIT_CARD_DETAILS);

        assertEquals(VISA_CHECKOUT_CARD_PAYMENT_INSTRUMENT_TYPE,
                unit.getPaymentInstrumentTypeBySnapshotAndOrigin(paymentMethodSnapshot));
    }

    @Test
    public void getPaymentInstrumentTypeBySnapshotAndOriginShouldReturnApplePay() {
        when(paymentMethodSnapshot.getOrigin()).thenReturn(origin);
        when(origin.getType()).thenReturn(APPLE_PAY_ORIGIN_TYPE);
        when(paymentMethodSnapshot.get__typename()).thenReturn(CREDIT_CARD_DETAILS);

        assertEquals(APPLE_PAY_PAYMENT_INSTRUMENT_TYPE,
                unit.getPaymentInstrumentTypeBySnapshotAndOrigin(paymentMethodSnapshot));
    }

    @Test
    public void getPaymentInstrumentTypeBySnapshotAndOriginShouldReturnLPM() {
        when(paymentMethodSnapshot.getOrigin()).thenReturn(origin);
        when(paymentMethodSnapshot.get__typename()).thenReturn(LOCAL_PAYMENT_DETAILS);

        assertEquals(LOCAL_PAYMENT_INSTRUMENT_TYPE,
                unit.getPaymentInstrumentTypeBySnapshotAndOrigin(paymentMethodSnapshot));
    }

    @Test(expected = IllegalArgumentException.class)
    public void getPaymentInstrumentTypeBySnapshotAndOriginShouldThrowException() {
        when(paymentMethodSnapshot.getOrigin()).thenReturn(origin);
        when(paymentMethodSnapshot.get__typename()).thenReturn(LOCAL_PAYMENT_INSTRUMENT_TYPE);

        assertEquals(LOCAL_PAYMENT_INSTRUMENT_TYPE,
                unit.getPaymentInstrumentTypeBySnapshotAndOrigin(paymentMethodSnapshot));
    }

    @Test
    public void shouldReturnCorrectProviderForPayPalWhenGetPaymentProviderByDetailsAndOrigin() {
        assertEquals(BrainTreePaymentMethod.PAYPALACCOUNT.getCode(),
                unit.getPaymentProviderByDetailsAndOrigin(PAYPAL_ACCOUNT_DETAILS, null));
    }

    @Test
    public void shouldReturnCorrectProviderForVenmoGetPaymentProviderByDetailsAndOrigin() {
        assertEquals(BrainTreePaymentMethod.VENMOACCOUNT.getCode(),
                unit.getPaymentProviderByDetailsAndOrigin(VENMO_ACCOUNT_DETAILS, null));
    }

    @Test
    public void shouldReturnCorrectProviderForGooglePayGetPaymentProviderByDetailsAndOrigin() {
        assertEquals(BrainTreePaymentMethod.ANDROIDPAYCARD.getCode(),
                unit.getPaymentProviderByDetailsAndOrigin(CREDIT_CARD_DETAILS, GOOGLE_PAY_ORIGIN_TYPE));
    }

    @Test
    public void shouldReturnCorrectProviderForSrcGetPaymentProviderByDetailsAndOrigin() {
        assertEquals(BrainTreePaymentMethod.VISACHECKOUTCARD.getCode(),
                unit.getPaymentProviderByDetailsAndOrigin(CREDIT_CARD_DETAILS, VISA_CHECKOUT_ORIGIN_TYPE));
    }

    @Test
    public void shouldReturnCorrectProviderForApplePayGetPaymentProviderByDetailsAndOrigin() {
        assertEquals(BrainTreePaymentMethod.APPLEPAYCARD.getCode(),
                unit.getPaymentProviderByDetailsAndOrigin(CREDIT_CARD_DETAILS, APPLE_PAY_ORIGIN_TYPE));
    }

    @Test
    public void shouldReturnCorrectProviderForCreditCardGetPaymentProviderByDetailsAndOrigin() {
        assertEquals(BrainTreePaymentMethod.CREDITCARD.getCode(),
                unit.getPaymentProviderByDetailsAndOrigin(CREDIT_CARD_DETAILS, null));
    }

    @Test
    public void getImageSourceByPaymentProviderAndCardTypeShouldReturnCorrectSourceForPayPal() {
        when(brainTreePaymentService
                .getImageSourceForPaymentMethod(BrainTreePaymentMethod.PAYPAL.getCode(), BRAINTREE_ACCEPTED_PAYMENT_METHODS))
                .thenReturn(CORRECT_SOURCE);

        assertEquals(CORRECT_SOURCE,
                unit.getImageSourceByPaymentProviderAndCardType(BrainTreePaymentMethod.PAYPALACCOUNT.toString(), null));
    }


    @Test
    public void getImageSourceByPaymentProviderAndCardTypeShouldReturnCorrectSourceForVenmo() {
        when(brainTreePaymentService
                .getImageSourceForPaymentMethod(BrainTreePaymentMethod.VENMOACCOUNT.toString(),
                        BRAINTREE_ACCEPTED_PAYMENT_METHODS))
                .thenReturn(CORRECT_SOURCE);

        assertEquals(CORRECT_SOURCE,
                unit.getImageSourceByPaymentProviderAndCardType(BrainTreePaymentMethod.VENMOACCOUNT.getCode(), null));
    }


    @Test
    public void getImageSourceByPaymentProviderAndCardTypeShouldReturnCorrectSourceForGooglePay() {
        when(brainTreePaymentService
                .getImageSourceForPaymentMethod(BrainTreePaymentMethod.ANDROIDPAYCARD.getCode(),
                        BRAINTREE_ACCEPTED_PAYMENT_METHODS))
                .thenReturn(CORRECT_SOURCE);

        assertEquals(CORRECT_SOURCE,
                unit.getImageSourceByPaymentProviderAndCardType(BrainTreePaymentMethod.ANDROIDPAYCARD.getCode(), null));
    }


    @Test
    public void getImageSourceByPaymentProviderAndCardTypeShouldReturnCorrectSourceForApplePay() {
        when(brainTreePaymentService
                .getImageSourceForPaymentMethod(BrainTreePaymentMethod.APPLEPAYCARD.getCode(),
                        BRAINTREE_ACCEPTED_PAYMENT_METHODS))
                .thenReturn(CORRECT_SOURCE);

        assertEquals(CORRECT_SOURCE,
                unit.getImageSourceByPaymentProviderAndCardType(BrainTreePaymentMethod.APPLEPAYCARD.getCode(), null));
    }


    @Test
    public void getImageSourceByPaymentProviderAndCardTypeShouldReturnCorrectSourceForCreditCard() {
        when(brainTreePaymentService
                .getImageSourceForPaymentMethod(CardType.VISA.toString(), BRAINTREE_ACCEPTED_PAYMENT_METHODS))
                .thenReturn(CORRECT_SOURCE);

        assertEquals(CORRECT_SOURCE,
                unit.getImageSourceByPaymentProviderAndCardType(BrainTreePaymentMethod.CREDITCARD.getCode(),
                        CardType.VISA.toString()));
    }

    @Test
    public void updateStatusToExpire() {
        when(brainTreeConfigService.getBrainTreeCreditCartExpireBetween()).thenReturn(1);
        when(expiredCard.getExpirationMonth()).thenReturn(coverMonth(YearMonth.now().getMonth()));
        when(expiredCard.getExpirationYear()).thenReturn(String.valueOf(YearMonth.now().getYear() - 1));

        unit.updateExpiredStatus(expiredCard);

        verify(expiredCard, never()).setExpirationStatus(ExpirationStatus.NOT_EXPIRED);
        verify(expiredCard, never()).setExpirationStatus(ExpirationStatus.EXPIRE_SOON);
        verify(expiredCard).setExpirationStatus(ExpirationStatus.EXPIRED);
    }

    @Test
    public void updateStatusToExpireWhenExpireBetweenIsZero() {
        when(brainTreeConfigService.getBrainTreeCreditCartExpireBetween()).thenReturn(0);
        when(expiredCard.getExpirationMonth()).thenReturn(coverMonth(YearMonth.now().getMonth()));
        when(expiredCard.getExpirationYear()).thenReturn(String.valueOf(YearMonth.now().getYear() - 1));

        unit.updateExpiredStatus(expiredCard);

        verify(expiredCard, never()).setExpirationStatus(ExpirationStatus.NOT_EXPIRED);
        verify(expiredCard, never()).setExpirationStatus(ExpirationStatus.EXPIRE_SOON);
        verify(expiredCard).setExpirationStatus(ExpirationStatus.EXPIRED);
    }

    @Test
    public void updateStatusToNotExpire() {
        when(brainTreeConfigService.getBrainTreeCreditCartExpireBetween()).thenReturn(1);
        when(notExpiredCard.getExpirationMonth()).thenReturn(coverMonth(YearMonth.now().getMonth()));
        when(notExpiredCard.getExpirationYear()).thenReturn(String.valueOf(YearMonth.now().getYear() + 1));

        unit.updateExpiredStatus(notExpiredCard);

        verify(notExpiredCard).setExpirationStatus(ExpirationStatus.NOT_EXPIRED);
        verify(notExpiredCard, never()).setExpirationStatus(ExpirationStatus.EXPIRE_SOON);
        verify(notExpiredCard, never()).setExpirationStatus(ExpirationStatus.EXPIRED);
    }

    @Test
    public void updateStatusToNotExpireWhenExpireBetweenIsZero() {
        when(brainTreeConfigService.getBrainTreeCreditCartExpireBetween()).thenReturn(0);
        when(notExpiredCard.getExpirationMonth()).thenReturn(coverMonth(YearMonth.now().getMonth()));
        when(notExpiredCard.getExpirationYear()).thenReturn(String.valueOf(YearMonth.now().getYear() + 1));

        unit.updateExpiredStatus(notExpiredCard);

        verify(notExpiredCard).setExpirationStatus(ExpirationStatus.NOT_EXPIRED);
        verify(notExpiredCard, never()).setExpirationStatus(ExpirationStatus.EXPIRE_SOON);
        verify(notExpiredCard, never()).setExpirationStatus(ExpirationStatus.EXPIRED);
    }

    @Test
    public void updateStatusToExpireSoon() {
        when(brainTreeConfigService.getBrainTreeCreditCartExpireBetween()).thenReturn(1);
        when(expiredSoonCard.getExpirationMonth()).thenReturn(coverMonth(YearMonth.now().getMonth()));
        when(expiredSoonCard.getExpirationYear()).thenReturn(String.valueOf(YearMonth.now().getYear()));
        when(expiredSoonCard.getCustomerId()).thenReturn(CUSTOMER_ID);
        when(brainTreeCustomerAccountDao.findCustomerByBrainTreeCustomerId(CUSTOMER_ID)).thenReturn(customerModel);

        unit.updateExpiredStatus(expiredSoonCard);

        verify(expiredSoonCard).setExpirationStatus(ExpirationStatus.EXPIRE_SOON);
        verify(expiredSoonCard, never()).setExpirationStatus(ExpirationStatus.NOT_EXPIRED);
        verify(expiredSoonCard, never()).setExpirationStatus(ExpirationStatus.EXPIRED);
        verify(customerModel).setCustomerHasNewExpireSoonCard(true);
        verify(modelService).save(customerModel);
    }

    @Test
    public void updateStatusToExpireSoonWhenExpireBetweenIsZero() {
        when(brainTreeConfigService.getBrainTreeCreditCartExpireBetween()).thenReturn(0);
        when(expiredSoonCard.getExpirationMonth()).thenReturn(coverMonth(YearMonth.now().getMonth()));
        when(expiredSoonCard.getExpirationYear()).thenReturn(String.valueOf(YearMonth.now().getYear()));

        unit.updateExpiredStatus(expiredSoonCard);

        verify(expiredSoonCard).setExpirationStatus(ExpirationStatus.NOT_EXPIRED);
        verify(expiredSoonCard, never()).setExpirationStatus(ExpirationStatus.EXPIRE_SOON);
        verify(expiredSoonCard, never()).setExpirationStatus(ExpirationStatus.EXPIRED);
    }

    @Test
    public void getAllSavedCreditCard() {
        when(brainTreePaymentInfoDao.findAllSavedCreditCard(searchPageData)).thenReturn(searchPageData);

        SearchPageData<BrainTreePaymentInfoModel> result = unit.getAllSavedCreditCard(searchPageData);

        assertEquals(searchPageData, result);
    }

    @Test
    public void getAllSavedCreditCardByCustomer() {
        final List<BrainTreePaymentInfoModel> brainTreePaymentInfoModels = List.of(paymentInfoModel, notExpiredCard);

        when(brainTreePaymentInfoDao.findAllSavedCreditCardByCustomer(CUSTOMER_ID)).thenReturn(brainTreePaymentInfoModels);

        assertEquals(2, unit.getAllSavedCreditCardByCustomer(CUSTOMER_ID).size());
    }

    @Test
    public void shouldMarkAsDuplicatePaymentMethodWhenGivenPaymentInfoIsSaved() {
        when(paymentInfoModel.getPaymentProvider()).thenReturn(BrainTreePaymentMethod.PAYPALACCOUNT.getCode());
        when(paymentInfoDuplicate.getPaymentProvider()).thenReturn(BrainTreePaymentMethod.PAYPALACCOUNT.getCode());
        when(paymentInfoModel.getPk()).thenReturn(PK.fromLong(1));
        when(paymentInfoDuplicate.getPk()).thenReturn(PK.fromLong(2));
        when(paymentInfoModel.getPayer()).thenReturn(CUSTOMER_ID);
        when(paymentInfoDuplicate.getPayer()).thenReturn(CUSTOMER_ID);
        when(paymentInfoModel.getBillingAddress()).thenReturn(addressModel);
        when(paymentInfoModel.isSaved()).thenReturn(true);
        when(brainTreeCustomerAccountService.getBrainTreePaymentInfos(customerModel, true)).thenReturn(List.of(paymentInfoModel, paymentInfoDuplicate));

        unit.checkOnDuplicatePaymentMethod(paymentInfoModel, customerModel, null);

        verify(paymentInfoDuplicate).setDuplicate(true);
        verify(paymentInfoModel, never()).setDuplicate(anyBoolean());
        verify(modelService).save(paymentInfoDuplicate);
        verify(modelService, never()).save(paymentInfoModel);
    }

    @Test
    public void shouldMarkGivenPaymentInfoAsDuplicateWhenIsNotSaved() {
        when(paymentInfoModel.getPaymentProvider()).thenReturn(BrainTreePaymentMethod.PAYPALACCOUNT.getCode());
        when(paymentInfoDuplicate.getPaymentProvider()).thenReturn(BrainTreePaymentMethod.PAYPALACCOUNT.getCode());
        when(paymentInfoModel.getPk()).thenReturn(PK.fromLong(1));
        when(paymentInfoDuplicate.getPk()).thenReturn(PK.fromLong(2));
        when(paymentInfoModel.getPayer()).thenReturn(CUSTOMER_ID);
        when(paymentInfoDuplicate.getPayer()).thenReturn(CUSTOMER_ID);
        when(paymentInfoModel.getBillingAddress()).thenReturn(addressModel);
        when(paymentInfoModel.isSaved()).thenReturn(false);
        when(brainTreeCustomerAccountService.getBrainTreePaymentInfos(customerModel, true)).thenReturn(List.of(paymentInfoModel, paymentInfoDuplicate));

        unit.checkOnDuplicatePaymentMethod(paymentInfoModel, customerModel, null);

        verify(paymentInfoModel).setDuplicate(true);
        verify(paymentInfoDuplicate, never()).setDuplicate(anyBoolean());
        verify(modelService, never()).save(paymentInfoDuplicate);
        verify(modelService).save(paymentInfoModel);
    }

    @Test
    public void shouldCheckOnDuplicatePaymentMethodWhenPayPalPaymentProvider() {
        when(paymentInfoModel.getPaymentProvider()).thenReturn(BrainTreePaymentMethod.PAYPALACCOUNT.getCode());
        when(paymentInfoDuplicate.getPaymentProvider()).thenReturn(BrainTreePaymentMethod.PAYPALACCOUNT.getCode());
        when(paymentInfoModel.getPk()).thenReturn(PK.fromLong(1));
        when(paymentInfoDuplicate.getPk()).thenReturn(PK.fromLong(2));
        when(paymentInfoModel.getPayer()).thenReturn(CUSTOMER_ID);
        when(paymentInfoDuplicate.getPayer()).thenReturn(CUSTOMER_ID);
        when(paymentInfoModel.getBillingAddress()).thenReturn(addressModel);
        when(paymentInfoModel.isSaved()).thenReturn(true);
        when(brainTreeCustomerAccountService.getBrainTreePaymentInfos(customerModel, true)).thenReturn(List.of(paymentInfoModel, paymentInfoDuplicate));

        unit.checkOnDuplicatePaymentMethod(paymentInfoModel, customerModel, null);

        verify(paymentInfoDuplicate).setDuplicate(true);
        verify(paymentInfoModel, never()).setDuplicate(anyBoolean());
        verify(modelService).save(paymentInfoDuplicate);
        verify(modelService, never()).save(paymentInfoModel);
    }

    @Test
    public void shouldCheckOnDuplicatePaymentMethodWhenVenmoPaymentProvider() {
        when(paymentInfoModel.getPaymentProvider()).thenReturn(BrainTreePaymentMethod.VENMOACCOUNT.getCode());
        when(paymentInfoDuplicate.getPaymentProvider()).thenReturn(BrainTreePaymentMethod.VENMOACCOUNT.getCode());
        when(paymentInfoModel.getPk()).thenReturn(PK.fromLong(1));
        when(paymentInfoDuplicate.getPk()).thenReturn(PK.fromLong(2));
        when(paymentInfoModel.getPayer()).thenReturn(CUSTOMER_ID);
        when(paymentInfoDuplicate.getPayer()).thenReturn(CUSTOMER_ID);
        when(paymentInfoModel.getBillingAddress()).thenReturn(addressModel);
        when(brainTreeCustomerAccountService.getBrainTreePaymentInfos(customerModel, true)).thenReturn(List.of(paymentInfoModel, paymentInfoDuplicate));

        unit.checkOnDuplicatePaymentMethod(paymentInfoModel, customerModel, null);

        verify(paymentInfoModel).setDuplicate(true);
        verify(paymentInfoDuplicate, never()).setDuplicate(anyBoolean());
        verify(modelService, never()).save(paymentInfoDuplicate);
        verify(modelService).save(paymentInfoModel);
    }

    @Test
    public void shouldCheckOnDuplicatePaymentMethodWhenSrcPaymentProvider() {
        when(paymentInfoModel.getPaymentProvider()).thenReturn(BrainTreePaymentMethod.VISACHECKOUTCARD.getCode());
        when(paymentInfoModel.getPk()).thenReturn(PK.fromLong(1));
        when(paymentInfoModel.getPayer()).thenReturn(CUSTOMER_ID);
        when(paymentInfoModel.getBillingAddress()).thenReturn(addressModel);
        when(paymentInfoModel.getCardType()).thenReturn(BrainTreeCardType.AMERICAN_EXPRESS);
        when(paymentInfoModel.getCardNumber()).thenReturn(CARD_NUMBER);
        when(paymentInfoModel.getExpirationMonth()).thenReturn(EXPIRATION_MONTH);
        when(paymentInfoModel.getExpirationYear()).thenReturn(EXPIRATION_YEAR);

        when(paymentInfoDuplicate.getPaymentProvider()).thenReturn(BrainTreePaymentMethod.VISACHECKOUTCARD.getCode());
        when(paymentInfoDuplicate.getPk()).thenReturn(PK.fromLong(2));
        when(paymentInfoDuplicate.getPayer()).thenReturn(CUSTOMER_ID);
        when(paymentInfoDuplicate.getCardType()).thenReturn(BrainTreeCardType.AMERICAN_EXPRESS);
        when(paymentInfoDuplicate.getCardNumber()).thenReturn(CARD_NUMBER);
        when(paymentInfoDuplicate.getExpirationMonth()).thenReturn(EXPIRATION_MONTH);
        when(paymentInfoDuplicate.getExpirationYear()).thenReturn(EXPIRATION_YEAR);
        when(brainTreeCustomerAccountService.getBrainTreePaymentInfos(customerModel, true)).thenReturn(List.of(paymentInfoModel, paymentInfoDuplicate));

        unit.checkOnDuplicatePaymentMethod(paymentInfoModel, customerModel, null);

        verify(paymentInfoModel).setDuplicate(true);
        verify(paymentInfoDuplicate, never()).setDuplicate(anyBoolean());
        verify(modelService, never()).save(paymentInfoDuplicate);
        verify(modelService).save(paymentInfoModel);
    }

    @Test
    public void shouldCheckOnDuplicatePaymentMethodWhenGooglePayPaymentProvider() {
        when(paymentInfoModel.getPaymentProvider()).thenReturn(BrainTreePaymentMethod.ANDROIDPAYCARD.getCode());
        when(paymentInfoModel.getPk()).thenReturn(PK.fromLong(1));
        when(paymentInfoModel.getPayer()).thenReturn(CUSTOMER_ID);
        when(paymentInfoModel.getBillingAddress()).thenReturn(addressModel);
        when(paymentInfoModel.getCardType()).thenReturn(BrainTreeCardType.DISCOVER);
        when(paymentInfoModel.getCardNumber()).thenReturn(CARD_NUMBER);
        when(paymentInfoModel.getExpirationMonth()).thenReturn(EXPIRATION_MONTH);
        when(paymentInfoModel.getExpirationYear()).thenReturn(EXPIRATION_YEAR);

        when(paymentInfoDuplicate.getPaymentProvider()).thenReturn(BrainTreePaymentMethod.ANDROIDPAYCARD.getCode());
        when(paymentInfoDuplicate.getPk()).thenReturn(PK.fromLong(2));
        when(paymentInfoDuplicate.getPayer()).thenReturn(CUSTOMER_ID);
        when(paymentInfoDuplicate.getCardType()).thenReturn(BrainTreeCardType.DISCOVER);
        when(paymentInfoDuplicate.getCardNumber()).thenReturn(CARD_NUMBER);
        when(paymentInfoDuplicate.getExpirationMonth()).thenReturn(EXPIRATION_MONTH);
        when(paymentInfoDuplicate.getExpirationYear()).thenReturn(EXPIRATION_YEAR);
        when(brainTreeCustomerAccountService.getBrainTreePaymentInfos(customerModel, true)).thenReturn(List.of(paymentInfoModel, paymentInfoDuplicate));

        unit.checkOnDuplicatePaymentMethod(paymentInfoModel, customerModel, null);

        verify(paymentInfoModel).setDuplicate(true);
        verify(paymentInfoDuplicate, never()).setDuplicate(anyBoolean());
        verify(modelService, never()).save(paymentInfoDuplicate);
        verify(modelService).save(paymentInfoModel);
    }

    @Test
    public void shouldNotMarkAsDuplicatePaymentMethodWhenGooglePayPaymentProviderAndDifferentCardNumber() {
        when(paymentInfoModel.getPaymentProvider()).thenReturn(BrainTreePaymentMethod.ANDROIDPAYCARD.getCode());
        when(paymentInfoModel.getPk()).thenReturn(PK.fromLong(1));
        when(paymentInfoModel.getBillingAddress()).thenReturn(addressModel);
        when(paymentInfoModel.getCardNumber()).thenReturn(CARD_NUMBER);

        when(paymentInfoDuplicate.getPaymentProvider()).thenReturn(BrainTreePaymentMethod.ANDROIDPAYCARD.getCode());
        when(paymentInfoDuplicate.getPk()).thenReturn(PK.fromLong(2));
        when(paymentInfoDuplicate.getCardNumber()).thenReturn(CARD_NUMBER_2);
        when(brainTreeCustomerAccountService.getBrainTreePaymentInfos(customerModel, true)).thenReturn(List.of(paymentInfoModel, paymentInfoDuplicate));

        unit.checkOnDuplicatePaymentMethod(paymentInfoModel, customerModel, null);

        verify(paymentInfoModel, never()).setDuplicate(true);
        verify(paymentInfoDuplicate, never()).setDuplicate(anyBoolean());
        verify(modelService, never()).save(paymentInfoDuplicate);
        verify(modelService, never()).save(paymentInfoModel);
    }

    @Test
    public void shouldNotMarkAsDuplicatePaymentMethodWhenGooglePayPaymentProviderAndDifferentExpirationYear() {
        when(paymentInfoModel.getPaymentProvider()).thenReturn(BrainTreePaymentMethod.ANDROIDPAYCARD.getCode());
        when(paymentInfoModel.getPk()).thenReturn(PK.fromLong(1));
        when(paymentInfoModel.getBillingAddress()).thenReturn(addressModel);
        when(paymentInfoModel.getCardNumber()).thenReturn(CARD_NUMBER);
        when(paymentInfoModel.getExpirationMonth()).thenReturn(EXPIRATION_MONTH);
        when(paymentInfoModel.getExpirationYear()).thenReturn(EXPIRATION_YEAR);

        when(paymentInfoDuplicate.getPaymentProvider()).thenReturn(BrainTreePaymentMethod.ANDROIDPAYCARD.getCode());
        when(paymentInfoDuplicate.getPk()).thenReturn(PK.fromLong(2));
        when(paymentInfoDuplicate.getCardNumber()).thenReturn(CARD_NUMBER);
        when(paymentInfoDuplicate.getExpirationMonth()).thenReturn(EXPIRATION_MONTH);
        when(paymentInfoDuplicate.getExpirationYear()).thenReturn(EXPIRATION_YEAR_2);
        when(brainTreeCustomerAccountService.getBrainTreePaymentInfos(customerModel, true)).thenReturn(List.of(paymentInfoModel, paymentInfoDuplicate));

        unit.checkOnDuplicatePaymentMethod(paymentInfoModel, customerModel, null);

        verify(paymentInfoModel, never()).setDuplicate(true);
        verify(paymentInfoDuplicate, never()).setDuplicate(anyBoolean());
        verify(modelService, never()).save(paymentInfoDuplicate);
        verify(modelService, never()).save(paymentInfoModel);
    }

    @Test
    public void shouldNotMarkAsDuplicatePaymentMethodWhenGooglePayPaymentProviderAndDifferentExpirationMonth() {
        when(paymentInfoModel.getPaymentProvider()).thenReturn(BrainTreePaymentMethod.ANDROIDPAYCARD.getCode());
        when(paymentInfoModel.getPk()).thenReturn(PK.fromLong(1));
        when(paymentInfoModel.getBillingAddress()).thenReturn(addressModel);
        when(paymentInfoModel.getCardNumber()).thenReturn(CARD_NUMBER);
        when(paymentInfoModel.getExpirationMonth()).thenReturn(EXPIRATION_MONTH);

        when(paymentInfoDuplicate.getPaymentProvider()).thenReturn(BrainTreePaymentMethod.ANDROIDPAYCARD.getCode());
        when(paymentInfoDuplicate.getPk()).thenReturn(PK.fromLong(2));
        when(paymentInfoDuplicate.getCardNumber()).thenReturn(CARD_NUMBER);
        when(paymentInfoDuplicate.getExpirationMonth()).thenReturn(EXP_MONTH_2);
        when(brainTreeCustomerAccountService.getBrainTreePaymentInfos(customerModel, true)).thenReturn(List.of(paymentInfoModel, paymentInfoDuplicate));

        unit.checkOnDuplicatePaymentMethod(paymentInfoModel, customerModel, null);

        verify(paymentInfoModel, never()).setDuplicate(true);
        verify(paymentInfoDuplicate, never()).setDuplicate(anyBoolean());
        verify(modelService, never()).save(paymentInfoDuplicate);
        verify(modelService, never()).save(paymentInfoModel);
    }

    @Test
    public void shouldNotMarkAsDuplicatePaymentMethodWhenGooglePayPaymentProviderAndDifferentPayer() {
        when(paymentInfoModel.getPaymentProvider()).thenReturn(BrainTreePaymentMethod.ANDROIDPAYCARD.getCode());
        when(paymentInfoModel.getPk()).thenReturn(PK.fromLong(1));
        when(paymentInfoModel.getPayer()).thenReturn(CUSTOMER_ID);
        when(paymentInfoModel.getBillingAddress()).thenReturn(addressModel);
        when(paymentInfoModel.getCardType()).thenReturn(BrainTreeCardType.DISCOVER);
        when(paymentInfoModel.getCardNumber()).thenReturn(CARD_NUMBER);
        when(paymentInfoModel.getExpirationMonth()).thenReturn(EXPIRATION_MONTH);
        when(paymentInfoModel.getExpirationYear()).thenReturn(EXPIRATION_YEAR);

        when(paymentInfoDuplicate.getPaymentProvider()).thenReturn(BrainTreePaymentMethod.ANDROIDPAYCARD.getCode());
        when(paymentInfoDuplicate.getPk()).thenReturn(PK.fromLong(2));
        when(paymentInfoDuplicate.getPayer()).thenReturn(CUSTOMER_ID_2);
        when(paymentInfoDuplicate.getCardType()).thenReturn(BrainTreeCardType.DISCOVER);
        when(paymentInfoDuplicate.getCardNumber()).thenReturn(CARD_NUMBER);
        when(paymentInfoDuplicate.getExpirationMonth()).thenReturn(EXPIRATION_MONTH);
        when(paymentInfoDuplicate.getExpirationYear()).thenReturn(EXPIRATION_YEAR);
        when(brainTreeCustomerAccountService.getBrainTreePaymentInfos(customerModel, true)).thenReturn(List.of(paymentInfoModel, paymentInfoDuplicate));

        unit.checkOnDuplicatePaymentMethod(paymentInfoModel, customerModel, null);

        verify(paymentInfoModel, never()).setDuplicate(true);
        verify(paymentInfoDuplicate, never()).setDuplicate(anyBoolean());
        verify(modelService, never()).save(paymentInfoDuplicate);
        verify(modelService, never()).save(paymentInfoModel);
    }

    @Test
    public void shouldNotMarkAsDuplicatePaymentMethodWhenGooglePayPaymentProviderAndDifferentCardType() {
        when(paymentInfoModel.getPaymentProvider()).thenReturn(BrainTreePaymentMethod.ANDROIDPAYCARD.getCode());
        when(paymentInfoModel.getPk()).thenReturn(PK.fromLong(1));
        when(paymentInfoModel.getBillingAddress()).thenReturn(addressModel);
        when(paymentInfoModel.getCardType()).thenReturn(BrainTreeCardType.DISCOVER);
        when(paymentInfoModel.getCardNumber()).thenReturn(CARD_NUMBER);
        when(paymentInfoModel.getExpirationMonth()).thenReturn(EXPIRATION_MONTH);
        when(paymentInfoModel.getExpirationYear()).thenReturn(EXPIRATION_YEAR);

        when(paymentInfoDuplicate.getPaymentProvider()).thenReturn(BrainTreePaymentMethod.ANDROIDPAYCARD.getCode());
        when(paymentInfoDuplicate.getPk()).thenReturn(PK.fromLong(2));
        when(paymentInfoDuplicate.getCardType()).thenReturn(BrainTreeCardType.CHINA_UNIONPAY);
        when(paymentInfoDuplicate.getCardNumber()).thenReturn(CARD_NUMBER);
        when(paymentInfoDuplicate.getExpirationMonth()).thenReturn(EXPIRATION_MONTH);
        when(paymentInfoDuplicate.getExpirationYear()).thenReturn(EXPIRATION_YEAR);
        when(brainTreeCustomerAccountService.getBrainTreePaymentInfos(customerModel, true)).thenReturn(List.of(paymentInfoModel, paymentInfoDuplicate));

        unit.checkOnDuplicatePaymentMethod(paymentInfoModel, customerModel, null);

        verify(paymentInfoModel, never()).setDuplicate(true);
        verify(paymentInfoDuplicate, never()).setDuplicate(anyBoolean());
        verify(modelService, never()).save(paymentInfoDuplicate);
        verify(modelService, never()).save(paymentInfoModel);
    }

    @Test
    public void shouldCheckOnDuplicatePaymentMethodWhenUsBankAccountPaymentProvider() {
        when(paymentInfoModel.getPaymentProvider()).thenReturn(BrainTreePaymentMethod.USBANKACCOUNT.getCode());
        when(paymentInfoModel.getPk()).thenReturn(PK.fromLong(1));
        when(paymentInfoModel.getBillingAddress()).thenReturn(addressModel);
        when(paymentInfoModel.getLast4()).thenReturn(LAST_4);
        when(paymentInfoModel.getRoutingNumber()).thenReturn(ROUTING_NUMBER);

        when(paymentInfoDuplicate.getPaymentProvider()).thenReturn(BrainTreePaymentMethod.USBANKACCOUNT.getCode());
        when(paymentInfoDuplicate.getPk()).thenReturn(PK.fromLong(2));
        when(paymentInfoDuplicate.getLast4()).thenReturn(LAST_4);
        when(paymentInfoDuplicate.getRoutingNumber()).thenReturn(ROUTING_NUMBER);
        when(brainTreeCustomerAccountService.getBrainTreePaymentInfos(customerModel, true)).thenReturn(List.of(paymentInfoModel, paymentInfoDuplicate));

        unit.checkOnDuplicatePaymentMethod(paymentInfoModel, customerModel, null);

        verify(paymentInfoModel).setDuplicate(true);
        verify(paymentInfoDuplicate, never()).setDuplicate(anyBoolean());
        verify(modelService, never()).save(paymentInfoDuplicate);
        verify(modelService).save(paymentInfoModel);
    }

    @Test
    public void shouldNotMarkAsDuplicatePaymentMethodWhenUsBankAccountPaymentProviderAndDifferentLast4() {
        when(paymentInfoModel.getPaymentProvider()).thenReturn(BrainTreePaymentMethod.USBANKACCOUNT.getCode());
        when(paymentInfoModel.getPk()).thenReturn(PK.fromLong(1));
        when(paymentInfoModel.getBillingAddress()).thenReturn(addressModel);
        when(paymentInfoModel.getLast4()).thenReturn(LAST_4);

        when(paymentInfoDuplicate.getPaymentProvider()).thenReturn(BrainTreePaymentMethod.USBANKACCOUNT.getCode());
        when(paymentInfoDuplicate.getPk()).thenReturn(PK.fromLong(2));
        when(paymentInfoDuplicate.getLast4()).thenReturn(LAST_4_2);
        when(brainTreeCustomerAccountService.getBrainTreePaymentInfos(customerModel, true)).thenReturn(List.of(paymentInfoModel, paymentInfoDuplicate));

        unit.checkOnDuplicatePaymentMethod(paymentInfoModel, customerModel, null);

        verify(paymentInfoModel, never()).setDuplicate(true);
        verify(paymentInfoDuplicate, never()).setDuplicate(anyBoolean());
        verify(modelService, never()).save(paymentInfoDuplicate);
        verify(modelService, never()).save(paymentInfoModel);
    }

    @Test
    public void shouldNotMarkAsDuplicatePaymentMethodWhenUsBankAccountPaymentProviderAndDifferentRoutingNumber() {
        when(paymentInfoModel.getPaymentProvider()).thenReturn(BrainTreePaymentMethod.USBANKACCOUNT.getCode());
        when(paymentInfoModel.getPk()).thenReturn(PK.fromLong(1));
        when(paymentInfoModel.getBillingAddress()).thenReturn(addressModel);
        when(paymentInfoModel.getLast4()).thenReturn(LAST_4);
        when(paymentInfoModel.getRoutingNumber()).thenReturn(ROUTING_NUMBER);

        when(paymentInfoDuplicate.getPaymentProvider()).thenReturn(BrainTreePaymentMethod.USBANKACCOUNT.getCode());
        when(paymentInfoDuplicate.getPk()).thenReturn(PK.fromLong(2));
        when(paymentInfoDuplicate.getLast4()).thenReturn(LAST_4);
        when(paymentInfoDuplicate.getRoutingNumber()).thenReturn(ROUTING_NUMBER_2);
        when(brainTreeCustomerAccountService.getBrainTreePaymentInfos(customerModel, true)).thenReturn(List.of(paymentInfoModel, paymentInfoDuplicate));

        unit.checkOnDuplicatePaymentMethod(paymentInfoModel, customerModel, null);

        verify(paymentInfoModel, never()).setDuplicate(true);
        verify(paymentInfoDuplicate, never()).setDuplicate(anyBoolean());
        verify(modelService, never()).save(paymentInfoDuplicate);
        verify(modelService, never()).save(paymentInfoModel);
    }

    @Test
    public void shouldCheckOnDuplicatePaymentMethodWhenCCPaymentProvider() {
        when(countryModel.getIsocode()).thenReturn(US_ISO_CODE);

        when(addressModel.getFirstname()).thenReturn(FIRST_NAME);
        when(addressModel.getLastname()).thenReturn(LAST_NAME);
        when(addressModel.getCountry()).thenReturn(countryModel);
        when(addressModel.getPostalcode()).thenReturn(POSTAL_CODE);
        when(addressModel.getRegion()).thenReturn(regionModel);
        when(addressModel.getLine1()).thenReturn(LINE_1);
        when(addressModel.getTown()).thenReturn(TOWN);

        when(paymentInfoModel.getPaymentProvider()).thenReturn(BrainTreePaymentMethod.CREDITCARD.getCode());
        when(paymentInfoModel.getPk()).thenReturn(PK.fromLong(1));
        when(paymentInfoModel.getBillingAddress()).thenReturn(addressModel);
        when(paymentInfoModel.getCardNumber()).thenReturn(CARD_NUMBER);
        when(paymentInfoModel.getCardholderName()).thenReturn(CARDHOLDER_NAME);
        when(paymentInfoModel.getExpirationMonth()).thenReturn(EXPIRATION_MONTH);
        when(paymentInfoModel.getExpirationYear()).thenReturn(EXPIRATION_YEAR);
        when(paymentInfoModel.getCardType()).thenReturn(BrainTreeCardType.DISCOVER);

        when(paymentInfoDuplicate.getPaymentProvider()).thenReturn(BrainTreePaymentMethod.CREDITCARD.getCode());
        when(paymentInfoDuplicate.getPk()).thenReturn(PK.fromLong(2));
        when(paymentInfoDuplicate.getCardholderName()).thenReturn(CARDHOLDER_NAME);
        when(paymentInfoDuplicate.getCardNumber()).thenReturn(CARD_NUMBER);
        when(paymentInfoDuplicate.getExpirationMonth()).thenReturn(EXPIRATION_MONTH);
        when(paymentInfoDuplicate.getExpirationYear()).thenReturn(EXPIRATION_YEAR);
        when(paymentInfoDuplicate.getCardType()).thenReturn(BrainTreeCardType.DISCOVER);
        when(paymentInfoDuplicate.getBillingAddress()).thenReturn(addressModel);

        when(brainTreeCustomerAccountService.getBrainTreePaymentInfos(customerModel, true)).thenReturn(List.of(paymentInfoModel, paymentInfoDuplicate));

        unit.checkOnDuplicatePaymentMethod(paymentInfoModel, customerModel, null);

        verify(paymentInfoModel).setDuplicate(true);
        verify(paymentInfoDuplicate, never()).setDuplicate(anyBoolean());
        verify(modelService, never()).save(paymentInfoDuplicate);
        verify(modelService).save(paymentInfoModel);
    }

    @Test
    public void shouldNotMarkAsDuplicatePaymentMethodWhenCCPaymentProviderAndDifferentCardHolderName() {
        when(paymentInfoModel.getPaymentProvider()).thenReturn(BrainTreePaymentMethod.CREDITCARD.getCode());
        when(paymentInfoModel.getPk()).thenReturn(PK.fromLong(1));
        when(paymentInfoModel.getBillingAddress()).thenReturn(addressModel);
        when(paymentInfoModel.getCardNumber()).thenReturn(CARD_NUMBER);
        when(paymentInfoModel.getCardholderName()).thenReturn(CARDHOLDER_NAME);
        when(paymentInfoModel.getExpirationMonth()).thenReturn(EXPIRATION_MONTH);
        when(paymentInfoModel.getExpirationYear()).thenReturn(EXPIRATION_YEAR);
        when(paymentInfoModel.getCardType()).thenReturn(BrainTreeCardType.DISCOVER);

        when(paymentInfoDuplicate.getPaymentProvider()).thenReturn(BrainTreePaymentMethod.CREDITCARD.getCode());
        when(paymentInfoDuplicate.getPk()).thenReturn(PK.fromLong(2));
        when(paymentInfoDuplicate.getCardholderName()).thenReturn(CARD_HOLDER_NAME_2);
        when(paymentInfoDuplicate.getCardNumber()).thenReturn(CARD_NUMBER);
        when(paymentInfoDuplicate.getExpirationMonth()).thenReturn(EXPIRATION_MONTH);
        when(paymentInfoDuplicate.getExpirationYear()).thenReturn(EXPIRATION_YEAR);
        when(paymentInfoDuplicate.getCardType()).thenReturn(BrainTreeCardType.DISCOVER);

        when(brainTreeCustomerAccountService.getBrainTreePaymentInfos(customerModel, true)).thenReturn(List.of(paymentInfoModel, paymentInfoDuplicate));

        unit.checkOnDuplicatePaymentMethod(paymentInfoModel, customerModel, null);

        verify(paymentInfoModel, never()).setDuplicate(anyBoolean());
        verify(paymentInfoDuplicate, never()).setDuplicate(anyBoolean());
        verify(modelService, never()).save(paymentInfoDuplicate);
        verify(modelService, never()).save(paymentInfoModel);
    }

    @Test
    public void shouldNotMarkAsDuplicatePaymentMethodWhenCCPaymentProviderAndDifferentCardNumber() {
        when(paymentInfoModel.getPaymentProvider()).thenReturn(BrainTreePaymentMethod.CREDITCARD.getCode());
        when(paymentInfoModel.getPk()).thenReturn(PK.fromLong(1));
        when(paymentInfoModel.getBillingAddress()).thenReturn(addressModel);
        when(paymentInfoModel.getCardNumber()).thenReturn(CARD_NUMBER);

        when(paymentInfoDuplicate.getPaymentProvider()).thenReturn(BrainTreePaymentMethod.CREDITCARD.getCode());
        when(paymentInfoDuplicate.getPk()).thenReturn(PK.fromLong(2));
        when(paymentInfoDuplicate.getCardNumber()).thenReturn(CARD_NUMBER_2);

        when(brainTreeCustomerAccountService.getBrainTreePaymentInfos(customerModel, true)).thenReturn(List.of(paymentInfoModel, paymentInfoDuplicate));

        unit.checkOnDuplicatePaymentMethod(paymentInfoModel, customerModel, null);

        verify(paymentInfoModel, never()).setDuplicate(anyBoolean());
        verify(paymentInfoDuplicate, never()).setDuplicate(anyBoolean());
        verify(modelService, never()).save(paymentInfoDuplicate);
        verify(modelService, never()).save(paymentInfoModel);
    }

    @Test
    public void shouldNotMarkAsDuplicatePaymentMethodWhenCCPaymentProviderAndDifferentExpirationMonth() {
        when(paymentInfoModel.getPaymentProvider()).thenReturn(BrainTreePaymentMethod.CREDITCARD.getCode());
        when(paymentInfoModel.getPk()).thenReturn(PK.fromLong(1));
        when(paymentInfoModel.getBillingAddress()).thenReturn(addressModel);
        when(paymentInfoModel.getCardNumber()).thenReturn(CARD_NUMBER);
        when(paymentInfoModel.getExpirationMonth()).thenReturn(EXPIRATION_MONTH);

        when(paymentInfoDuplicate.getPaymentProvider()).thenReturn(BrainTreePaymentMethod.CREDITCARD.getCode());
        when(paymentInfoDuplicate.getPk()).thenReturn(PK.fromLong(2));
        when(paymentInfoDuplicate.getCardNumber()).thenReturn(CARD_NUMBER);
        when(paymentInfoDuplicate.getExpirationMonth()).thenReturn(EXP_MONTH_2);

        when(brainTreeCustomerAccountService.getBrainTreePaymentInfos(customerModel, true)).thenReturn(List.of(paymentInfoModel, paymentInfoDuplicate));

        unit.checkOnDuplicatePaymentMethod(paymentInfoModel, customerModel, null);

        verify(paymentInfoModel, never()).setDuplicate(anyBoolean());
        verify(paymentInfoDuplicate, never()).setDuplicate(anyBoolean());
        verify(modelService, never()).save(paymentInfoDuplicate);
        verify(modelService, never()).save(paymentInfoModel);
    }

    @Test
    public void shouldNotMarkAsDuplicatePaymentMethodWhenCCPaymentProviderAndDifferentExpirationYear() {
        when(paymentInfoModel.getPaymentProvider()).thenReturn(BrainTreePaymentMethod.CREDITCARD.getCode());
        when(paymentInfoModel.getPk()).thenReturn(PK.fromLong(1));
        when(paymentInfoModel.getBillingAddress()).thenReturn(addressModel);
        when(paymentInfoModel.getCardNumber()).thenReturn(CARD_NUMBER);
        when(paymentInfoModel.getExpirationMonth()).thenReturn(EXPIRATION_MONTH);
        when(paymentInfoModel.getExpirationYear()).thenReturn(EXPIRATION_YEAR);

        when(paymentInfoDuplicate.getPaymentProvider()).thenReturn(BrainTreePaymentMethod.CREDITCARD.getCode());
        when(paymentInfoDuplicate.getPk()).thenReturn(PK.fromLong(2));
        when(paymentInfoDuplicate.getCardNumber()).thenReturn(CARD_NUMBER);
        when(paymentInfoDuplicate.getExpirationMonth()).thenReturn(EXPIRATION_MONTH);
        when(paymentInfoDuplicate.getExpirationYear()).thenReturn(EXPIRATION_YEAR_2);

        when(brainTreeCustomerAccountService.getBrainTreePaymentInfos(customerModel, true)).thenReturn(List.of(paymentInfoModel, paymentInfoDuplicate));

        unit.checkOnDuplicatePaymentMethod(paymentInfoModel, customerModel, null);

        verify(paymentInfoModel, never()).setDuplicate(anyBoolean());
        verify(paymentInfoDuplicate, never()).setDuplicate(anyBoolean());
        verify(modelService, never()).save(paymentInfoDuplicate);
        verify(modelService, never()).save(paymentInfoModel);
    }

    @Test
    public void shouldNotMarkAsDuplicatePaymentMethodWhenCCPaymentProviderWhenDifferentBillingAddressFirstName() {
        when(countryModel.getIsocode()).thenReturn(US_ISO_CODE);

        when(addressModel.getFirstname()).thenReturn(FIRST_NAME);
        when(addressModel.getCountry()).thenReturn(countryModel);
        when(addressModel.getRegion()).thenReturn(regionModel);

        when(addressModel2.getFirstname()).thenReturn(FIRST_NAME_2);
        when(addressModel2.getCountry()).thenReturn(countryModel);
        when(addressModel2.getRegion()).thenReturn(regionModel);

        when(paymentInfoModel.getPaymentProvider()).thenReturn(BrainTreePaymentMethod.CREDITCARD.getCode());
        when(paymentInfoModel.getPk()).thenReturn(PK.fromLong(1));
        when(paymentInfoModel.getBillingAddress()).thenReturn(addressModel);
        when(paymentInfoModel.getCardNumber()).thenReturn(CARD_NUMBER);
        when(paymentInfoModel.getCardholderName()).thenReturn(CARDHOLDER_NAME);
        when(paymentInfoModel.getExpirationMonth()).thenReturn(EXPIRATION_MONTH);
        when(paymentInfoModel.getExpirationYear()).thenReturn(EXPIRATION_YEAR);
        when(paymentInfoModel.getCardType()).thenReturn(BrainTreeCardType.DISCOVER);

        when(paymentInfoDuplicate.getPaymentProvider()).thenReturn(BrainTreePaymentMethod.CREDITCARD.getCode());
        when(paymentInfoDuplicate.getPk()).thenReturn(PK.fromLong(2));
        when(paymentInfoDuplicate.getCardholderName()).thenReturn(CARDHOLDER_NAME);
        when(paymentInfoDuplicate.getCardNumber()).thenReturn(CARD_NUMBER);
        when(paymentInfoDuplicate.getExpirationMonth()).thenReturn(EXPIRATION_MONTH);
        when(paymentInfoDuplicate.getExpirationYear()).thenReturn(EXPIRATION_YEAR);
        when(paymentInfoDuplicate.getCardType()).thenReturn(BrainTreeCardType.DISCOVER);
        when(paymentInfoDuplicate.getBillingAddress()).thenReturn(addressModel2);

        when(brainTreeCustomerAccountService.getBrainTreePaymentInfos(customerModel, true)).thenReturn(List.of(paymentInfoModel, paymentInfoDuplicate));

        unit.checkOnDuplicatePaymentMethod(paymentInfoModel, customerModel, null);

        verify(paymentInfoModel, never()).setDuplicate(anyBoolean());
        verify(paymentInfoDuplicate, never()).setDuplicate(anyBoolean());
        verify(modelService, never()).save(paymentInfoDuplicate);
        verify(modelService, never()).save(paymentInfoModel);
    }

    @Test
    public void shouldNotMarkAsDuplicatePaymentMethodWhenCCPaymentProviderAndDifferentBillingAddressLastName() {
        when(countryModel.getIsocode()).thenReturn(US_ISO_CODE);

        when(addressModel.getFirstname()).thenReturn(FIRST_NAME);
        when(addressModel.getLastname()).thenReturn(LAST_NAME);
        when(addressModel.getCountry()).thenReturn(countryModel);
        when(addressModel.getRegion()).thenReturn(regionModel);

        when(addressModel2.getFirstname()).thenReturn(FIRST_NAME);
        when(addressModel2.getLastname()).thenReturn(LAST_NAME_2);
        when(addressModel2.getCountry()).thenReturn(countryModel);
        when(addressModel2.getRegion()).thenReturn(regionModel);

        when(paymentInfoModel.getPaymentProvider()).thenReturn(BrainTreePaymentMethod.CREDITCARD.getCode());
        when(paymentInfoModel.getPk()).thenReturn(PK.fromLong(1));
        when(paymentInfoModel.getBillingAddress()).thenReturn(addressModel);
        when(paymentInfoModel.getCardNumber()).thenReturn(CARD_NUMBER);
        when(paymentInfoModel.getCardholderName()).thenReturn(CARDHOLDER_NAME);
        when(paymentInfoModel.getExpirationMonth()).thenReturn(EXPIRATION_MONTH);
        when(paymentInfoModel.getExpirationYear()).thenReturn(EXPIRATION_YEAR);
        when(paymentInfoModel.getCardType()).thenReturn(BrainTreeCardType.DISCOVER);

        when(paymentInfoDuplicate.getPaymentProvider()).thenReturn(BrainTreePaymentMethod.CREDITCARD.getCode());
        when(paymentInfoDuplicate.getPk()).thenReturn(PK.fromLong(2));
        when(paymentInfoDuplicate.getCardholderName()).thenReturn(CARDHOLDER_NAME);
        when(paymentInfoDuplicate.getCardNumber()).thenReturn(CARD_NUMBER);
        when(paymentInfoDuplicate.getExpirationMonth()).thenReturn(EXPIRATION_MONTH);
        when(paymentInfoDuplicate.getExpirationYear()).thenReturn(EXPIRATION_YEAR);
        when(paymentInfoDuplicate.getCardType()).thenReturn(BrainTreeCardType.DISCOVER);
        when(paymentInfoDuplicate.getBillingAddress()).thenReturn(addressModel2);

        when(brainTreeCustomerAccountService.getBrainTreePaymentInfos(customerModel, true)).thenReturn(List.of(paymentInfoModel, paymentInfoDuplicate));

        unit.checkOnDuplicatePaymentMethod(paymentInfoModel, customerModel, null);

        verify(paymentInfoModel, never()).setDuplicate(anyBoolean());
        verify(paymentInfoDuplicate, never()).setDuplicate(anyBoolean());
        verify(modelService, never()).save(paymentInfoDuplicate);
        verify(modelService, never()).save(paymentInfoModel);
    }

    @Test
    public void shouldNotMarkAsDuplicatePaymentMethodWhenCCPaymentProviderAndDifferentBillingAddressCountryIsoCode() {
        when(countryModel.getIsocode()).thenReturn(US_ISO_CODE);
        when(countryModel2.getIsocode()).thenReturn(UK_ISO_CODE);

        when(addressModel.getCountry()).thenReturn(countryModel);

        when(addressModel2.getCountry()).thenReturn(countryModel2);

        when(paymentInfoModel.getPaymentProvider()).thenReturn(BrainTreePaymentMethod.CREDITCARD.getCode());
        when(paymentInfoModel.getPk()).thenReturn(PK.fromLong(1));
        when(paymentInfoModel.getBillingAddress()).thenReturn(addressModel);
        when(paymentInfoModel.getCardNumber()).thenReturn(CARD_NUMBER);
        when(paymentInfoModel.getCardholderName()).thenReturn(CARDHOLDER_NAME);
        when(paymentInfoModel.getExpirationMonth()).thenReturn(EXPIRATION_MONTH);
        when(paymentInfoModel.getExpirationYear()).thenReturn(EXPIRATION_YEAR);
        when(paymentInfoModel.getCardType()).thenReturn(BrainTreeCardType.DISCOVER);

        when(paymentInfoDuplicate.getPaymentProvider()).thenReturn(BrainTreePaymentMethod.CREDITCARD.getCode());
        when(paymentInfoDuplicate.getPk()).thenReturn(PK.fromLong(2));
        when(paymentInfoDuplicate.getCardholderName()).thenReturn(CARDHOLDER_NAME);
        when(paymentInfoDuplicate.getCardNumber()).thenReturn(CARD_NUMBER);
        when(paymentInfoDuplicate.getExpirationMonth()).thenReturn(EXPIRATION_MONTH);
        when(paymentInfoDuplicate.getExpirationYear()).thenReturn(EXPIRATION_YEAR);
        when(paymentInfoDuplicate.getCardType()).thenReturn(BrainTreeCardType.DISCOVER);
        when(paymentInfoDuplicate.getBillingAddress()).thenReturn(addressModel2);

        when(brainTreeCustomerAccountService.getBrainTreePaymentInfos(customerModel, true)).thenReturn(List.of(paymentInfoModel, paymentInfoDuplicate));

        unit.checkOnDuplicatePaymentMethod(paymentInfoModel, customerModel, null);

        verify(paymentInfoModel, never()).setDuplicate(anyBoolean());
        verify(paymentInfoDuplicate, never()).setDuplicate(anyBoolean());
        verify(modelService, never()).save(paymentInfoDuplicate);
        verify(modelService, never()).save(paymentInfoModel);
    }

    @Test
    public void shouldNotMarkAsDuplicatePaymentMethodWhenCCPaymentProviderAndDifferentBillingAddressPostalCode() {
        when(countryModel.getIsocode()).thenReturn(US_ISO_CODE);

        when(addressModel.getFirstname()).thenReturn(FIRST_NAME);
        when(addressModel.getLastname()).thenReturn(LAST_NAME);
        when(addressModel.getCountry()).thenReturn(countryModel);
        when(addressModel.getPostalcode()).thenReturn(POSTAL_CODE);
        when(addressModel.getRegion()).thenReturn(regionModel);

        when(addressModel2.getFirstname()).thenReturn(FIRST_NAME);
        when(addressModel2.getLastname()).thenReturn(LAST_NAME);
        when(addressModel2.getCountry()).thenReturn(countryModel);
        when(addressModel2.getPostalcode()).thenReturn(POSTAL_CODE_2);
        when(addressModel2.getRegion()).thenReturn(regionModel);

        when(paymentInfoModel.getPaymentProvider()).thenReturn(BrainTreePaymentMethod.CREDITCARD.getCode());
        when(paymentInfoModel.getPk()).thenReturn(PK.fromLong(1));
        when(paymentInfoModel.getBillingAddress()).thenReturn(addressModel);
        when(paymentInfoModel.getCardNumber()).thenReturn(CARD_NUMBER);
        when(paymentInfoModel.getCardholderName()).thenReturn(CARDHOLDER_NAME);
        when(paymentInfoModel.getExpirationMonth()).thenReturn(EXPIRATION_MONTH);
        when(paymentInfoModel.getExpirationYear()).thenReturn(EXPIRATION_YEAR);
        when(paymentInfoModel.getCardType()).thenReturn(BrainTreeCardType.DISCOVER);

        when(paymentInfoDuplicate.getPaymentProvider()).thenReturn(BrainTreePaymentMethod.CREDITCARD.getCode());
        when(paymentInfoDuplicate.getPk()).thenReturn(PK.fromLong(2));
        when(paymentInfoDuplicate.getCardholderName()).thenReturn(CARDHOLDER_NAME);
        when(paymentInfoDuplicate.getCardNumber()).thenReturn(CARD_NUMBER);
        when(paymentInfoDuplicate.getExpirationMonth()).thenReturn(EXPIRATION_MONTH);
        when(paymentInfoDuplicate.getExpirationYear()).thenReturn(EXPIRATION_YEAR);
        when(paymentInfoDuplicate.getCardType()).thenReturn(BrainTreeCardType.DISCOVER);
        when(paymentInfoDuplicate.getBillingAddress()).thenReturn(addressModel2);

        when(brainTreeCustomerAccountService.getBrainTreePaymentInfos(customerModel, true)).thenReturn(List.of(paymentInfoModel, paymentInfoDuplicate));

        unit.checkOnDuplicatePaymentMethod(paymentInfoModel, customerModel, null);

        verify(paymentInfoModel, never()).setDuplicate(anyBoolean());
        verify(paymentInfoDuplicate, never()).setDuplicate(anyBoolean());
        verify(modelService, never()).save(paymentInfoDuplicate);
        verify(modelService, never()).save(paymentInfoModel);
    }

    @Test
    public void shouldNotMarkAsDuplicatePaymentMethodWhenCCPaymentProviderAndDifferentBillingAddressLine1() {
        when(countryModel.getIsocode()).thenReturn(US_ISO_CODE);

        when(addressModel.getFirstname()).thenReturn(FIRST_NAME);
        when(addressModel.getLastname()).thenReturn(LAST_NAME);
        when(addressModel.getCountry()).thenReturn(countryModel);
        when(addressModel.getPostalcode()).thenReturn(POSTAL_CODE);
        when(addressModel.getRegion()).thenReturn(regionModel);
        when(addressModel.getLine1()).thenReturn(LINE_1);

        when(addressModel2.getFirstname()).thenReturn(FIRST_NAME);
        when(addressModel2.getLastname()).thenReturn(LAST_NAME);
        when(addressModel2.getCountry()).thenReturn(countryModel);
        when(addressModel2.getPostalcode()).thenReturn(POSTAL_CODE);
        when(addressModel2.getRegion()).thenReturn(regionModel);
        when(addressModel2.getLine1()).thenReturn(LINE_1_2);

        when(paymentInfoModel.getPaymentProvider()).thenReturn(BrainTreePaymentMethod.CREDITCARD.getCode());
        when(paymentInfoModel.getPk()).thenReturn(PK.fromLong(1));
        when(paymentInfoModel.getBillingAddress()).thenReturn(addressModel);
        when(paymentInfoModel.getCardNumber()).thenReturn(CARD_NUMBER);
        when(paymentInfoModel.getCardholderName()).thenReturn(CARDHOLDER_NAME);
        when(paymentInfoModel.getExpirationMonth()).thenReturn(EXPIRATION_MONTH);
        when(paymentInfoModel.getExpirationYear()).thenReturn(EXPIRATION_YEAR);
        when(paymentInfoModel.getCardType()).thenReturn(BrainTreeCardType.DISCOVER);

        when(paymentInfoDuplicate.getPaymentProvider()).thenReturn(BrainTreePaymentMethod.CREDITCARD.getCode());
        when(paymentInfoDuplicate.getPk()).thenReturn(PK.fromLong(2));
        when(paymentInfoDuplicate.getCardholderName()).thenReturn(CARDHOLDER_NAME);
        when(paymentInfoDuplicate.getCardNumber()).thenReturn(CARD_NUMBER);
        when(paymentInfoDuplicate.getExpirationMonth()).thenReturn(EXPIRATION_MONTH);
        when(paymentInfoDuplicate.getExpirationYear()).thenReturn(EXPIRATION_YEAR);
        when(paymentInfoDuplicate.getCardType()).thenReturn(BrainTreeCardType.DISCOVER);
        when(paymentInfoDuplicate.getBillingAddress()).thenReturn(addressModel2);

        when(brainTreeCustomerAccountService.getBrainTreePaymentInfos(customerModel, true)).thenReturn(List.of(paymentInfoModel, paymentInfoDuplicate));

        unit.checkOnDuplicatePaymentMethod(paymentInfoModel, customerModel, null);

        verify(paymentInfoModel, never()).setDuplicate(anyBoolean());
        verify(paymentInfoDuplicate, never()).setDuplicate(anyBoolean());
        verify(modelService, never()).save(paymentInfoDuplicate);
        verify(modelService, never()).save(paymentInfoModel);
    }

    @Test
    public void shouldNotMarkAsDuplicatePaymentMethodWhenCCPaymentProviderAndDifferentBillingAddressTown() {
        when(countryModel.getIsocode()).thenReturn(US_ISO_CODE);

        when(addressModel.getFirstname()).thenReturn(FIRST_NAME);
        when(addressModel.getLastname()).thenReturn(LAST_NAME);
        when(addressModel.getCountry()).thenReturn(countryModel);
        when(addressModel.getPostalcode()).thenReturn(POSTAL_CODE);
        when(addressModel.getRegion()).thenReturn(regionModel);
        when(addressModel.getLine1()).thenReturn(LINE_1);
        when(addressModel.getTown()).thenReturn(TOWN);

        when(addressModel2.getFirstname()).thenReturn(FIRST_NAME);
        when(addressModel2.getLastname()).thenReturn(LAST_NAME);
        when(addressModel2.getCountry()).thenReturn(countryModel);
        when(addressModel2.getPostalcode()).thenReturn(POSTAL_CODE);
        when(addressModel2.getRegion()).thenReturn(regionModel);
        when(addressModel2.getLine1()).thenReturn(LINE_1);
        when(addressModel2.getTown()).thenReturn(TOWN_2);

        when(paymentInfoModel.getPaymentProvider()).thenReturn(BrainTreePaymentMethod.CREDITCARD.getCode());
        when(paymentInfoModel.getPk()).thenReturn(PK.fromLong(1));
        when(paymentInfoModel.getBillingAddress()).thenReturn(addressModel);
        when(paymentInfoModel.getCardNumber()).thenReturn(CARD_NUMBER);
        when(paymentInfoModel.getCardholderName()).thenReturn(CARDHOLDER_NAME);
        when(paymentInfoModel.getExpirationMonth()).thenReturn(EXPIRATION_MONTH);
        when(paymentInfoModel.getExpirationYear()).thenReturn(EXPIRATION_YEAR);
        when(paymentInfoModel.getCardType()).thenReturn(BrainTreeCardType.DISCOVER);

        when(paymentInfoDuplicate.getPaymentProvider()).thenReturn(BrainTreePaymentMethod.CREDITCARD.getCode());
        when(paymentInfoDuplicate.getPk()).thenReturn(PK.fromLong(2));
        when(paymentInfoDuplicate.getCardholderName()).thenReturn(CARDHOLDER_NAME);
        when(paymentInfoDuplicate.getCardNumber()).thenReturn(CARD_NUMBER);
        when(paymentInfoDuplicate.getExpirationMonth()).thenReturn(EXPIRATION_MONTH);
        when(paymentInfoDuplicate.getExpirationYear()).thenReturn(EXPIRATION_YEAR);
        when(paymentInfoDuplicate.getCardType()).thenReturn(BrainTreeCardType.DISCOVER);
        when(paymentInfoDuplicate.getBillingAddress()).thenReturn(addressModel2);

        when(brainTreeCustomerAccountService.getBrainTreePaymentInfos(customerModel, true)).thenReturn(List.of(paymentInfoModel, paymentInfoDuplicate));

        unit.checkOnDuplicatePaymentMethod(paymentInfoModel, customerModel, null);

        verify(paymentInfoModel, never()).setDuplicate(anyBoolean());
        verify(paymentInfoDuplicate, never()).setDuplicate(anyBoolean());
        verify(modelService, never()).save(paymentInfoDuplicate);
        verify(modelService, never()).save(paymentInfoModel);
    }

    @Test
    public void shouldNotMarkAsDuplicatePaymentMethodWhenCCPaymentProviderAndDifferentBillingAddressRegion() {
        when(countryModel.getIsocode()).thenReturn(US_ISO_CODE);

        when(addressModel.getCountry()).thenReturn(countryModel);
        when(addressModel.getRegion()).thenReturn(regionModel);

        when(addressModel2.getCountry()).thenReturn(countryModel);

        when(paymentInfoModel.getPaymentProvider()).thenReturn(BrainTreePaymentMethod.CREDITCARD.getCode());
        when(paymentInfoModel.getPk()).thenReturn(PK.fromLong(1));
        when(paymentInfoModel.getBillingAddress()).thenReturn(addressModel);
        when(paymentInfoModel.getCardNumber()).thenReturn(CARD_NUMBER);
        when(paymentInfoModel.getCardholderName()).thenReturn(CARDHOLDER_NAME);
        when(paymentInfoModel.getExpirationMonth()).thenReturn(EXPIRATION_MONTH);
        when(paymentInfoModel.getExpirationYear()).thenReturn(EXPIRATION_YEAR);
        when(paymentInfoModel.getCardType()).thenReturn(BrainTreeCardType.DISCOVER);

        when(paymentInfoDuplicate.getPaymentProvider()).thenReturn(BrainTreePaymentMethod.CREDITCARD.getCode());
        when(paymentInfoDuplicate.getPk()).thenReturn(PK.fromLong(2));
        when(paymentInfoDuplicate.getCardholderName()).thenReturn(CARDHOLDER_NAME);
        when(paymentInfoDuplicate.getCardNumber()).thenReturn(CARD_NUMBER);
        when(paymentInfoDuplicate.getExpirationMonth()).thenReturn(EXPIRATION_MONTH);
        when(paymentInfoDuplicate.getExpirationYear()).thenReturn(EXPIRATION_YEAR);
        when(paymentInfoDuplicate.getCardType()).thenReturn(BrainTreeCardType.DISCOVER);
        when(paymentInfoDuplicate.getBillingAddress()).thenReturn(addressModel2);

        when(brainTreeCustomerAccountService.getBrainTreePaymentInfos(customerModel, true)).thenReturn(List.of(paymentInfoModel, paymentInfoDuplicate));

        unit.checkOnDuplicatePaymentMethod(paymentInfoModel, customerModel, null);

        verify(paymentInfoModel, never()).setDuplicate(anyBoolean());
        verify(paymentInfoDuplicate, never()).setDuplicate(anyBoolean());
        verify(modelService, never()).save(paymentInfoDuplicate);
        verify(modelService, never()).save(paymentInfoModel);
    }

    private String coverMonth(Month month) {
        if (month.getValue() < 10) {
            return "0" + month.getValue();
        } else {
            return String.valueOf(month.getValue());
        }
    }

}
